// Copyright 2013 The Flutter Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#include "export.h"
#include "helpers.h"
#include "wrappers.h"

#include "third_party/skia/include/core/SkPoint3.h"
#include "third_party/skia/include/core/SkVertices.h"
#include "third_party/skia/include/utils/SkShadowUtils.h"
#include "third_party/skia/modules/skparagraph/include/Paragraph.h"

using namespace skia::textlayout;

using namespace Skwasm;

namespace {
// These numbers have been chosen empirically to give a result closest to the
// material spec.
// These values are also used by the CanvasKit renderer and the native engine.
// See:
//   flutter/display_list/skia/dl_sk_dispatcher.cc
//   flutter/lib/web_ui/lib/src/engine/canvaskit/util.dart
constexpr SkScalar kShadowAmbientAlpha = 0.039;
constexpr SkScalar kShadowSpotAlpha = 0.25;
constexpr SkScalar kShadowLightRadius = 1.1;
constexpr SkScalar kShadowLightHeight = 600.0;
constexpr SkScalar kShadowLightXOffset = 0;
constexpr SkScalar kShadowLightYOffset = -450;
}  // namespace

SKWASM_EXPORT void canvas_saveLayer(SkCanvas* canvas,
                                    SkRect* rect,
                                    SkPaint* paint,
                                    SkImageFilter* backdrop) {
  canvas->saveLayer(SkCanvas::SaveLayerRec(rect, paint, backdrop, 0));
}

SKWASM_EXPORT void canvas_save(SkCanvas* canvas) {
  canvas->save();
}

SKWASM_EXPORT void canvas_restore(SkCanvas* canvas) {
  canvas->restore();
}

SKWASM_EXPORT void canvas_restoreToCount(SkCanvas* canvas, int count) {
  canvas->restoreToCount(count);
}

SKWASM_EXPORT int canvas_getSaveCount(SkCanvas* canvas) {
  return canvas->getSaveCount();
}

SKWASM_EXPORT void canvas_translate(SkCanvas* canvas,
                                    SkScalar dx,
                                    SkScalar dy) {
  canvas->translate(dx, dy);
}

SKWASM_EXPORT void canvas_scale(SkCanvas* canvas, SkScalar sx, SkScalar sy) {
  canvas->scale(sx, sy);
}

SKWASM_EXPORT void canvas_rotate(SkCanvas* canvas, SkScalar degrees) {
  canvas->rotate(degrees);
}

SKWASM_EXPORT void canvas_skew(SkCanvas* canvas, SkScalar sx, SkScalar sy) {
  canvas->skew(sx, sy);
}

SKWASM_EXPORT void canvas_transform(SkCanvas* canvas, const SkM44* matrix44) {
  canvas->concat(*matrix44);
}

SKWASM_EXPORT void canvas_clipRect(SkCanvas* canvas,
                                   const SkRect* rect,
                                   SkClipOp op,
                                   bool antialias) {
  canvas->clipRect(*rect, op, antialias);
}

SKWASM_EXPORT void canvas_clipRRect(SkCanvas* canvas,
                                    const SkScalar* rrectValues,
                                    bool antialias) {
  canvas->clipRRect(createRRect(rrectValues), antialias);
}

SKWASM_EXPORT void canvas_clipPath(SkCanvas* canvas,
                                   SkPath* path,
                                   bool antialias) {
  canvas->clipPath(*path, antialias);
}

SKWASM_EXPORT void canvas_drawColor(SkCanvas* canvas,
                                    SkColor color,
                                    SkBlendMode blendMode) {
  canvas->drawColor(color, blendMode);
}

SKWASM_EXPORT void canvas_drawLine(SkCanvas* canvas,
                                   SkScalar x1,
                                   SkScalar y1,
                                   SkScalar x2,
                                   SkScalar y2,
                                   SkPaint* paint) {
  canvas->drawLine(x1, y1, x2, y2, *paint);
}

SKWASM_EXPORT void canvas_drawPaint(SkCanvas* canvas, SkPaint* paint) {
  canvas->drawPaint(*paint);
}

SKWASM_EXPORT void canvas_drawRect(SkCanvas* canvas,
                                   SkRect* rect,
                                   SkPaint* paint) {
  canvas->drawRect(*rect, *paint);
}

SKWASM_EXPORT void canvas_drawRRect(SkCanvas* canvas,
                                    const SkScalar* rrectValues,
                                    SkPaint* paint) {
  canvas->drawRRect(createRRect(rrectValues), *paint);
}

SKWASM_EXPORT void canvas_drawDRRect(SkCanvas* canvas,
                                     const SkScalar* outerRrectValues,
                                     const SkScalar* innerRrectValues,
                                     SkPaint* paint) {
  canvas->drawDRRect(createRRect(outerRrectValues),
                     createRRect(innerRrectValues), *paint);
}

SKWASM_EXPORT void canvas_drawOval(SkCanvas* canvas,
                                   const SkRect* rect,
                                   SkPaint* paint) {
  canvas->drawOval(*rect, *paint);
}

SKWASM_EXPORT void canvas_drawCircle(SkCanvas* canvas,
                                     SkScalar x,
                                     SkScalar y,
                                     SkScalar radius,
                                     SkPaint* paint) {
  canvas->drawCircle(x, y, radius, *paint);
}

SKWASM_EXPORT void canvas_drawArc(SkCanvas* canvas,
                                  const SkRect* rect,
                                  SkScalar startAngleDegrees,
                                  SkScalar sweepAngleDegrees,
                                  bool useCenter,
                                  SkPaint* paint) {
  canvas->drawArc(*rect, startAngleDegrees, sweepAngleDegrees, useCenter,
                  *paint);
}

SKWASM_EXPORT void canvas_drawPath(SkCanvas* canvas,
                                   SkPath* path,
                                   SkPaint* paint) {
  canvas->drawPath(*path, *paint);
}

SKWASM_EXPORT void canvas_drawShadow(SkCanvas* canvas,
                                     SkPath* path,
                                     SkScalar elevation,
                                     SkScalar devicePixelRatio,
                                     SkColor color,
                                     bool transparentOccluder) {
  SkColor inAmbient =
      SkColorSetA(color, kShadowAmbientAlpha * SkColorGetA(color));
  SkColor inSpot = SkColorSetA(color, kShadowSpotAlpha * SkColorGetA(color));
  SkColor outAmbient;
  SkColor outSpot;
  SkShadowUtils::ComputeTonalColors(inAmbient, inSpot, &outAmbient, &outSpot);
  uint32_t flags = transparentOccluder
                       ? SkShadowFlags::kTransparentOccluder_ShadowFlag
                       : SkShadowFlags::kNone_ShadowFlag;
  flags |= SkShadowFlags::kDirectionalLight_ShadowFlag;
  SkShadowUtils::DrawShadow(
      canvas, *path, SkPoint3::Make(0.0f, 0.0f, elevation * devicePixelRatio),
      SkPoint3::Make(kShadowLightXOffset, kShadowLightYOffset,
                     kShadowLightHeight * devicePixelRatio),
      devicePixelRatio * kShadowLightRadius, outAmbient, outSpot, flags);
}

SKWASM_EXPORT void canvas_drawParagraph(SkCanvas* canvas,
                                        Paragraph* paragraph,
                                        SkScalar x,
                                        SkScalar y) {
  paragraph->paint(canvas, x, y);
}

SKWASM_EXPORT void canvas_drawPicture(SkCanvas* canvas, SkPicture* picture) {
  canvas->drawPicture(picture);
}

SKWASM_EXPORT void canvas_drawImage(SkCanvas* canvas,
                                    SkImage* image,
                                    SkScalar offsetX,
                                    SkScalar offsetY,
                                    SkPaint* paint,
                                    FilterQuality quality) {
  canvas->drawImage(image, offsetX, offsetY, samplingOptionsForQuality(quality),
                    paint);
}

SKWASM_EXPORT void canvas_drawImageRect(SkCanvas* canvas,
                                        SkImage* image,
                                        SkRect* sourceRect,
                                        SkRect* destRect,
                                        SkPaint* paint,
                                        FilterQuality quality) {
  canvas->drawImageRect(image, *sourceRect, *destRect,
                        samplingOptionsForQuality(quality), paint,
                        SkCanvas::kStrict_SrcRectConstraint);
}

SKWASM_EXPORT void canvas_drawImageNine(SkCanvas* canvas,
                                        SkImage* image,
                                        SkIRect* centerRect,
                                        SkRect* destinationRect,
                                        SkPaint* paint,
                                        FilterQuality quality) {
  canvas->drawImageNine(image, *centerRect, *destinationRect,
                        filterModeForQuality(quality), paint);
}

SKWASM_EXPORT void canvas_drawVertices(SkCanvas* canvas,
                                       SkVertices* vertices,
                                       SkBlendMode mode,
                                       SkPaint* paint) {
  canvas->drawVertices(sk_ref_sp<SkVertices>(vertices), mode, *paint);
}

SKWASM_EXPORT void canvas_drawPoints(SkCanvas* canvas,
                                     SkCanvas::PointMode mode,
                                     SkPoint* points,
                                     int pointCount,
                                     SkPaint* paint) {
  canvas->drawPoints(mode, pointCount, points, *paint);
}

SKWASM_EXPORT void canvas_drawAtlas(SkCanvas* canvas,
                                    SkImage* atlas,
                                    SkRSXform* transforms,
                                    SkRect* rects,
                                    SkColor* colors,
                                    int spriteCount,
                                    SkBlendMode mode,
                                    SkRect* cullRect,
                                    SkPaint* paint) {
  canvas->drawAtlas(
      atlas, transforms, rects, colors, spriteCount, mode,
      SkSamplingOptions{SkFilterMode::kLinear, SkMipmapMode::kNone}, cullRect,
      paint);
}

SKWASM_EXPORT void canvas_getTransform(SkCanvas* canvas, SkM44* outTransform) {
  *outTransform = canvas->getLocalToDevice();
}

SKWASM_EXPORT void canvas_getLocalClipBounds(SkCanvas* canvas,
                                             SkRect* outRect) {
  *outRect = canvas->getLocalClipBounds();
}

SKWASM_EXPORT void canvas_getDeviceClipBounds(SkCanvas* canvas,
                                              SkIRect* outRect) {
  *outRect = canvas->getDeviceClipBounds();
}
