// 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.

#ifndef FLUTTER_IMPELLER_TOOLKIT_INTEROP_FORMATS_H_
#define FLUTTER_IMPELLER_TOOLKIT_INTEROP_FORMATS_H_

#include <vector>

#include "flutter/display_list/dl_builder.h"
#include "flutter/display_list/dl_color.h"
#include "flutter/display_list/geometry/dl_path_builder.h"
#include "flutter/txt/src/txt/font_style.h"
#include "flutter/txt/src/txt/font_weight.h"
#include "flutter/txt/src/txt/paragraph_style.h"
#include "impeller/entity/entity.h"
#include "impeller/geometry/color.h"
#include "impeller/geometry/matrix.h"
#include "impeller/geometry/point.h"
#include "impeller/geometry/rect.h"
#include "impeller/geometry/size.h"
#include "impeller/toolkit/interop/impeller.h"

#include "flutter/third_party/skia/include/core/SkM44.h"
#include "flutter/third_party/skia/include/core/SkPath.h"
#include "flutter/third_party/skia/include/core/SkRRect.h"

namespace impeller::interop {

constexpr std::optional<SkRect> ToSkiaType(const ImpellerRect* rect) {
  if (!rect) {
    return std::nullopt;
  }
  return SkRect::MakeXYWH(rect->x, rect->y, rect->width, rect->height);
}

constexpr SkPoint ToSkiaType(const Point& point) {
  return SkPoint::Make(point.x, point.y);
}

constexpr SkColor ToSkiaType(const ImpellerColor& color) {
  return SkColorSetARGB(color.alpha * 255,  //
                        color.red * 255,    //
                        color.green * 255,  //
                        color.blue * 255    //
  );
}

constexpr SkVector ToSkiaVector(const Size& point) {
  return SkVector::Make(point.width, point.height);
}

constexpr SkRect ToSkiaType(const Rect& rect) {
  return SkRect::MakeXYWH(rect.GetX(),      //
                          rect.GetY(),      //
                          rect.GetWidth(),  //
                          rect.GetHeight()  //
  );
}

constexpr SkPathFillType ToSkiaType(FillType type) {
  switch (type) {
    case FillType::kNonZero:
      return SkPathFillType::kWinding;
    case FillType::kOdd:
      return SkPathFillType::kEvenOdd;
  }
  return SkPathFillType::kWinding;
}

constexpr SkIRect ToSkiaType(IRect rect) {
  return SkIRect::MakeXYWH(rect.GetX(),      //
                           rect.GetY(),      //
                           rect.GetWidth(),  //
                           rect.GetHeight()  //
  );
}

template <class SkiaType, class OtherType>
std::vector<SkiaType> ToSkiaType(const std::vector<OtherType>& other_vec) {
  std::vector<SkiaType> skia_vec;
  skia_vec.reserve(other_vec.size());
  for (const auto& other : other_vec) {
    skia_vec.emplace_back(ToSkiaType(other));
  }
  return skia_vec;
}

constexpr flutter::DlColor ToDisplayListType(Color color) {
  return flutter::DlColor::RGBA(color.red,    //
                                color.green,  //
                                color.blue,   //
                                color.alpha   //
  );
}

inline SkMatrix ToSkMatrix(const Matrix& matrix) {
  return SkM44::ColMajor(matrix.m).asM33();
}

template <class DlType, class OtherType>
std::vector<DlType> ToDisplayListType(const std::vector<OtherType>& other_vec) {
  std::vector<DlType> dl_vec;
  dl_vec.reserve(other_vec.size());
  for (const auto& other : other_vec) {
    dl_vec.emplace_back(ToDisplayListType(other));
  }
  return dl_vec;
}

constexpr flutter::DlImageSampling ToDisplayListType(
    ImpellerTextureSampling sampling) {
  switch (sampling) {
    case kImpellerTextureSamplingNearestNeighbor:
      return flutter::DlImageSampling::kNearestNeighbor;
    case kImpellerTextureSamplingLinear:
      return flutter::DlImageSampling::kLinear;
  }
  return flutter::DlImageSampling::kLinear;
}

constexpr flutter::DlBlurStyle ToDisplayListType(ImpellerBlurStyle style) {
  switch (style) {
    case kImpellerBlurStyleNormal:
      return flutter::DlBlurStyle::kNormal;
    case kImpellerBlurStyleSolid:
      return flutter::DlBlurStyle::kSolid;
    case kImpellerBlurStyleOuter:
      return flutter::DlBlurStyle::kOuter;
    case kImpellerBlurStyleInner:
      return flutter::DlBlurStyle::kInner;
  }
  return flutter::DlBlurStyle::kNormal;
}

constexpr flutter::DlBlendMode ToDisplayListType(BlendMode mode) {
  using Mode = flutter::DlBlendMode;
  switch (mode) {
    case BlendMode::kClear:
      return Mode::kClear;
    case BlendMode::kSrc:
      return Mode::kSrc;
    case BlendMode::kDst:
      return Mode::kDst;
    case BlendMode::kSrcOver:
      return Mode::kSrcOver;
    case BlendMode::kDstOver:
      return Mode::kDstOver;
    case BlendMode::kSrcIn:
      return Mode::kSrcIn;
    case BlendMode::kDstIn:
      return Mode::kDstIn;
    case BlendMode::kSrcOut:
      return Mode::kSrcOut;
    case BlendMode::kDstOut:
      return Mode::kDstOut;
    case BlendMode::kSrcATop:
      return Mode::kSrcATop;
    case BlendMode::kDstATop:
      return Mode::kDstATop;
    case BlendMode::kXor:
      return Mode::kXor;
    case BlendMode::kPlus:
      return Mode::kPlus;
    case BlendMode::kModulate:
      return Mode::kModulate;
    case BlendMode::kScreen:
      return Mode::kScreen;
    case BlendMode::kOverlay:
      return Mode::kOverlay;
    case BlendMode::kDarken:
      return Mode::kDarken;
    case BlendMode::kLighten:
      return Mode::kLighten;
    case BlendMode::kColorDodge:
      return Mode::kColorDodge;
    case BlendMode::kColorBurn:
      return Mode::kColorBurn;
    case BlendMode::kHardLight:
      return Mode::kHardLight;
    case BlendMode::kSoftLight:
      return Mode::kSoftLight;
    case BlendMode::kDifference:
      return Mode::kDifference;
    case BlendMode::kExclusion:
      return Mode::kExclusion;
    case BlendMode::kMultiply:
      return Mode::kMultiply;
    case BlendMode::kHue:
      return Mode::kHue;
    case BlendMode::kSaturation:
      return Mode::kSaturation;
    case BlendMode::kColor:
      return Mode::kColor;
    case BlendMode::kLuminosity:
      return Mode::kLuminosity;
  }
  return Mode::kSrcOver;
}

inline SkRRect ToSkiaType(const Rect& rect, const RoundingRadii& radii) {
  using Corner = SkRRect::Corner;
  SkVector sk_radii[4];
  sk_radii[Corner::kUpperLeft_Corner] = ToSkiaVector(radii.top_left);
  sk_radii[Corner::kUpperRight_Corner] = ToSkiaVector(radii.top_right);
  sk_radii[Corner::kLowerRight_Corner] = ToSkiaVector(radii.bottom_right);
  sk_radii[Corner::kLowerLeft_Corner] = ToSkiaVector(radii.bottom_left);
  SkRRect result;
  result.setRectRadii(ToSkiaType(rect), sk_radii);
  return result;
}

constexpr Matrix ToImpellerType(const ImpellerMatrix& m) {
  return Matrix(m.m[0], m.m[1], m.m[2], m.m[3],     //
                m.m[4], m.m[5], m.m[6], m.m[7],     //
                m.m[8], m.m[9], m.m[10], m.m[11],   //
                m.m[12], m.m[13], m.m[14], m.m[15]  //
  );
}

constexpr void FromImpellerType(const Matrix& from, ImpellerMatrix& to) {
  to.m[0] = from.m[0];
  to.m[1] = from.m[1];
  to.m[2] = from.m[2];
  to.m[3] = from.m[3];
  to.m[4] = from.m[4];
  to.m[5] = from.m[5];
  to.m[6] = from.m[6];
  to.m[7] = from.m[7];
  to.m[8] = from.m[8];
  to.m[9] = from.m[9];
  to.m[10] = from.m[10];
  to.m[11] = from.m[11];
  to.m[12] = from.m[12];
  to.m[13] = from.m[13];
  to.m[14] = from.m[14];
  to.m[15] = from.m[15];
}

constexpr Size ToImpellerType(const ImpellerSize& size) {
  return Size{size.width, size.height};
}

constexpr Point ToImpellerType(const ImpellerPoint& point) {
  return Point{point.x, point.y};
}

constexpr Size ToImpellerSize(const ImpellerPoint& point) {
  return Size{point.x, point.y};
}

constexpr Rect ToImpellerType(const ImpellerRect& rect) {
  return Rect::MakeXYWH(rect.x, rect.y, rect.width, rect.height);
}

constexpr flutter::DlTileMode ToDisplayListType(ImpellerTileMode mode) {
  switch (mode) {
    case kImpellerTileModeClamp:
      return flutter::DlTileMode::kClamp;
    case kImpellerTileModeRepeat:
      return flutter::DlTileMode::kRepeat;
    case kImpellerTileModeMirror:
      return flutter::DlTileMode::kMirror;
    case kImpellerTileModeDecal:
      return flutter::DlTileMode::kDecal;
  }
  return flutter::DlTileMode::kClamp;
}

constexpr RoundingRadii ToImpellerType(const ImpellerRoundingRadii& radii) {
  auto result = RoundingRadii{};
  result.top_left = ToImpellerSize(radii.top_left);
  result.bottom_left = ToImpellerSize(radii.bottom_left);
  result.top_right = ToImpellerSize(radii.top_right);
  result.bottom_right = ToImpellerSize(radii.bottom_right);
  return result;
}

constexpr FillType ToImpellerType(ImpellerFillType type) {
  switch (type) {
    case kImpellerFillTypeNonZero:
      return FillType::kNonZero;
    case kImpellerFillTypeOdd:
      return FillType::kOdd;
  }
  return FillType::kNonZero;
}

constexpr flutter::DlClipOp ToImpellerType(ImpellerClipOperation op) {
  switch (op) {
    case kImpellerClipOperationDifference:
      return flutter::DlClipOp::kDifference;
    case kImpellerClipOperationIntersect:
      return flutter::DlClipOp::kIntersect;
  }
  return flutter::DlClipOp::kDifference;
}

constexpr Color ToImpellerType(const ImpellerColor& color) {
  Color result;
  result.red = color.red;
  result.green = color.green;
  result.blue = color.blue;
  result.alpha = color.alpha;
  return result;
}

constexpr BlendMode ToImpellerType(ImpellerBlendMode mode) {
  switch (mode) {
    case kImpellerBlendModeClear:
      return BlendMode::kClear;
    case kImpellerBlendModeSource:
      return BlendMode::kSrc;
    case kImpellerBlendModeDestination:
      return BlendMode::kDst;
    case kImpellerBlendModeSourceOver:
      return BlendMode::kSrcOver;
    case kImpellerBlendModeDestinationOver:
      return BlendMode::kDstOver;
    case kImpellerBlendModeSourceIn:
      return BlendMode::kSrcIn;
    case kImpellerBlendModeDestinationIn:
      return BlendMode::kDstIn;
    case kImpellerBlendModeSourceOut:
      return BlendMode::kSrcOut;
    case kImpellerBlendModeDestinationOut:
      return BlendMode::kDstOut;
    case kImpellerBlendModeSourceATop:
      return BlendMode::kSrcATop;
    case kImpellerBlendModeDestinationATop:
      return BlendMode::kDstATop;
    case kImpellerBlendModeXor:
      return BlendMode::kXor;
    case kImpellerBlendModePlus:
      return BlendMode::kPlus;
    case kImpellerBlendModeModulate:
      return BlendMode::kModulate;
    case kImpellerBlendModeScreen:
      return BlendMode::kScreen;
    case kImpellerBlendModeOverlay:
      return BlendMode::kOverlay;
    case kImpellerBlendModeDarken:
      return BlendMode::kDarken;
    case kImpellerBlendModeLighten:
      return BlendMode::kLighten;
    case kImpellerBlendModeColorDodge:
      return BlendMode::kColorDodge;
    case kImpellerBlendModeColorBurn:
      return BlendMode::kColorBurn;
    case kImpellerBlendModeHardLight:
      return BlendMode::kHardLight;
    case kImpellerBlendModeSoftLight:
      return BlendMode::kSoftLight;
    case kImpellerBlendModeDifference:
      return BlendMode::kDifference;
    case kImpellerBlendModeExclusion:
      return BlendMode::kExclusion;
    case kImpellerBlendModeMultiply:
      return BlendMode::kMultiply;
    case kImpellerBlendModeHue:
      return BlendMode::kHue;
    case kImpellerBlendModeSaturation:
      return BlendMode::kSaturation;
    case kImpellerBlendModeColor:
      return BlendMode::kColor;
    case kImpellerBlendModeLuminosity:
      return BlendMode::kLuminosity;
  }
  return BlendMode::kSrcOver;
}

constexpr flutter::DlDrawStyle ToDisplayListType(ImpellerDrawStyle style) {
  switch (style) {
    case kImpellerDrawStyleFill:
      return flutter::DlDrawStyle::kFill;
    case kImpellerDrawStyleStroke:
      return flutter::DlDrawStyle::kStroke;
    case kImpellerDrawStyleStrokeAndFill:
      return flutter::DlDrawStyle::kStrokeAndFill;
  }
  return flutter::DlDrawStyle::kFill;
}

constexpr flutter::DlStrokeCap ToDisplayListType(ImpellerStrokeCap cap) {
  switch (cap) {
    case kImpellerStrokeCapButt:
      return flutter::DlStrokeCap::kButt;
    case kImpellerStrokeCapRound:
      return flutter::DlStrokeCap::kRound;
    case kImpellerStrokeCapSquare:
      return flutter::DlStrokeCap::kSquare;
  }
  return flutter::DlStrokeCap::kButt;
}

constexpr flutter::DlStrokeJoin ToDisplayListType(ImpellerStrokeJoin join) {
  switch (join) {
    case kImpellerStrokeJoinMiter:
      return flutter::DlStrokeJoin::kMiter;
    case kImpellerStrokeJoinRound:
      return flutter::DlStrokeJoin::kRound;
    case kImpellerStrokeJoinBevel:
      return flutter::DlStrokeJoin::kBevel;
  }
  return flutter::DlStrokeJoin::kMiter;
}

constexpr PixelFormat ToImpellerType(ImpellerPixelFormat format) {
  switch (format) {
    case kImpellerPixelFormatRGBA8888:
      return PixelFormat::kR8G8B8A8UNormInt;
  }
  return PixelFormat::kR8G8B8A8UNormInt;
}

constexpr ISize ToImpellerType(const ImpellerISize& size) {
  return ISize::MakeWH(size.width, size.height);
}

constexpr flutter::DlColorSpace ToDisplayListType(
    ImpellerColorSpace color_space) {
  switch (color_space) {
    case kImpellerColorSpaceSRGB:
      return flutter::DlColorSpace::kSRGB;
    case kImpellerColorSpaceExtendedSRGB:
      return flutter::DlColorSpace::kExtendedSRGB;
    case kImpellerColorSpaceDisplayP3:
      return flutter::DlColorSpace::kDisplayP3;
  }
  return flutter::DlColorSpace::kSRGB;
}

constexpr flutter::DlColor ToDisplayListType(ImpellerColor color) {
  return flutter::DlColor(color.alpha,                          //
                          color.red,                            //
                          color.green,                          //
                          color.blue,                           //
                          ToDisplayListType(color.color_space)  //
  );
}

constexpr txt::TextDecorationStyle ToTxtType(
    ImpellerTextDecorationStyle style) {
  switch (style) {
    case kImpellerTextDecorationStyleSolid:
      return txt::TextDecorationStyle::kSolid;
    case kImpellerTextDecorationStyleDouble:
      return txt::TextDecorationStyle::kDouble;
    case kImpellerTextDecorationStyleDotted:
      return txt::TextDecorationStyle::kDotted;
    case kImpellerTextDecorationStyleDashed:
      return txt::TextDecorationStyle::kDashed;
    case kImpellerTextDecorationStyleWavy:
      return txt::TextDecorationStyle::kWavy;
  }
  return txt::TextDecorationStyle::kSolid;
}

constexpr txt::FontWeight ToTxtType(ImpellerFontWeight weight) {
  switch (weight) {
    case kImpellerFontWeight100:
      return txt::FontWeight::w100;
    case kImpellerFontWeight200:
      return txt::FontWeight::w200;
    case kImpellerFontWeight300:
      return txt::FontWeight::w300;
    case kImpellerFontWeight400:
      return txt::FontWeight::w400;
    case kImpellerFontWeight500:
      return txt::FontWeight::w500;
    case kImpellerFontWeight600:
      return txt::FontWeight::w600;
    case kImpellerFontWeight700:
      return txt::FontWeight::w700;
    case kImpellerFontWeight800:
      return txt::FontWeight::w800;
    case kImpellerFontWeight900:
      return txt::FontWeight::w900;
  }
  return txt::FontWeight::w400;
}

constexpr txt::FontStyle ToTxtType(ImpellerFontStyle style) {
  switch (style) {
    case kImpellerFontStyleNormal:
      return txt::FontStyle::normal;
    case kImpellerFontStyleItalic:
      return txt::FontStyle::italic;
  }
  return txt::FontStyle::normal;
}

constexpr txt::TextAlign ToTxtType(ImpellerTextAlignment align) {
  switch (align) {
    case kImpellerTextAlignmentLeft:
      return txt::TextAlign::left;
    case kImpellerTextAlignmentRight:
      return txt::TextAlign::right;
    case kImpellerTextAlignmentCenter:
      return txt::TextAlign::center;
    case kImpellerTextAlignmentJustify:
      return txt::TextAlign::justify;
    case kImpellerTextAlignmentStart:
      return txt::TextAlign::start;
    case kImpellerTextAlignmentEnd:
      return txt::TextAlign::end;
  }
  return txt::TextAlign::left;
}

constexpr txt::TextDirection ToTxtType(ImpellerTextDirection direction) {
  switch (direction) {
    case kImpellerTextDirectionRTL:
      return txt::TextDirection::rtl;
    case kImpellerTextDirectionLTR:
      return txt::TextDirection::ltr;
  }
  return txt::TextDirection::ltr;
}

}  // namespace impeller::interop

#endif  // FLUTTER_IMPELLER_TOOLKIT_INTEROP_FORMATS_H_
