#pragma once

#include "BlendModeProp.h"
#include "ColorProp.h"
#include "NodeProp.h"
#include "StrokeProps.h"

#include "JsiSkPaint.h"
#include "third_party/CSSColorParser.h"

#include <memory>

#pragma clang diagnostic push
#pragma clang diagnostic ignored "-Wdocumentation"

#include "include/core/SkPaint.h"

#pragma clang diagnostic pop

namespace RNSkia {

class PaintProp : public DerivedProp<SkPaint> {
public:
  explicit PaintProp(PropId name,
                     const std::function<void(BaseNodeProp *)> &onChange)
      : DerivedProp<SkPaint>(onChange) {
    _paintProp = defineProperty<NodeProp>(name);
  }

  explicit PaintProp(const std::function<void(BaseNodeProp *)> &onChange)
      : PaintProp(JsiPropId::get("paint"), onChange) {}

  void updateDerivedValue() override {
    if (_paintProp->isSet()) {
      if (_paintProp->value().getType() == PropType::HostObject) {
        // Read paint property as Host Object - JsiSkPaint
        auto ptr = _paintProp->value().getAs<JsiSkPaint>();
        if (ptr != nullptr) {
          setDerivedValue(ptr->getObject());
        } else {
          throw std::runtime_error("Expected SkPaint object, got unknown "
                                   "object when reading paint property.");
        }
      } else {
        setDerivedValue(nullptr);
      }
    } else {
      setDerivedValue(nullptr);
    }
  }

private:
  NodeProp *_paintProp;
};

class PaintDrawingContextProp : public DerivedProp<DrawingContext> {
public:
  explicit PaintDrawingContextProp(
      PropId name, const std::function<void(BaseNodeProp *)> &onChange)
      : DerivedProp<DrawingContext>(onChange) {
    _paintProp = defineProperty<NodeProp>(name);
  }

  explicit PaintDrawingContextProp(
      const std::function<void(BaseNodeProp *)> &onChange)
      : PaintDrawingContextProp(JsiPropId::get("paint"), onChange) {}

  void updateDerivedValue() override {
    if (_paintProp->isSet()) {
      if (_paintProp->value().getType() == PropType::HostObject) {
        // Read paint property as Host Object - JsiSkPaint
        auto ptr = _paintProp->value().getAs<JsiSkPaint>();
        if (ptr != nullptr) {
          setDerivedValue(std::make_shared<DrawingContext>(ptr->getObject()));
        } else {
          throw std::runtime_error("Expected SkPaint object, got unknown "
                                   "object when reading paint property.");
        }
      } else {
        setDerivedValue(nullptr);
      }
    } else {
      setDerivedValue(nullptr);
    }
  }

private:
  NodeProp *_paintProp;
};

class PaintProps : public BaseDerivedProp {
public:
  explicit PaintProps(const std::function<void(BaseNodeProp *)> &onChange)
      : BaseDerivedProp(onChange) {
    _color = defineProperty<ColorProp>("color");
    _style = defineProperty<NodeProp>("style");
    _strokeWidth = defineProperty<NodeProp>("strokeWidth");
    _blendMode = defineProperty<BlendModeProp>("blendMode");
    _strokeJoin = defineProperty<StrokeJoinProp>("strokeJoin");
    _strokeCap = defineProperty<StrokeCapProp>("strokeCap");
    _strokeMiter = defineProperty<NodeProp>("strokeMiter");
    _antiAlias = defineProperty<NodeProp>("antiAlias");
    _dither = defineProperty<NodeProp>("dither");
    _opacity = defineProperty<NodeProp>("opacity");
  }

  void updateDerivedValue() override {}

  ColorProp *getColor() { return _color; }
  NodeProp *getStyle() { return _style; }
  NodeProp *getStrokeWidth() { return _strokeWidth; }
  BlendModeProp *getBlendMode() { return _blendMode; }
  StrokeJoinProp *getStrokeJoin() { return _strokeJoin; }
  StrokeCapProp *getStrokeCap() { return _strokeCap; }
  NodeProp *getStrokeMiter() { return _strokeMiter; }
  NodeProp *getAntiAlias() { return _antiAlias; }
  NodeProp *getDither() { return _dither; }
  NodeProp *getOpacity() { return _opacity; }

private:
  ColorProp *_color;
  NodeProp *_style;
  NodeProp *_strokeWidth;
  BlendModeProp *_blendMode;
  StrokeJoinProp *_strokeJoin;
  StrokeCapProp *_strokeCap;
  NodeProp *_strokeMiter;
  NodeProp *_antiAlias;
  NodeProp *_dither;
  NodeProp *_opacity;
};

} // namespace RNSkia
