#pragma once

#include "JsiDomDeclarationNode.h"
#include "PaintProps.h"

#include <memory>

namespace RNSkia {

class JsiPaintNode : public JsiDomDeclarationNode,
                     public JsiDomNodeCtor<JsiPaintNode> {
public:
  explicit JsiPaintNode(std::shared_ptr<RNSkPlatformContext> context)
      : JsiDomDeclarationNode(context, "skPaint", DeclarationType::Paint) {}

  void decorate(DeclarationContext *context) override {
    auto paint = std::make_shared<SkPaint>();
    paint->setAntiAlias(true);

    if (_paintProps->getOpacity()->isSet()) {
      paint->setAlphaf(paint->getAlphaf() *
                       _paintProps->getOpacity()->value().getAsNumber());
    }

    if (_paintProps->getColor()->isSet()) {
      auto currentOpacity = paint->getAlphaf();
      paint->setShader(nullptr);
      paint->setColor(*_paintProps->getColor()->getDerivedValue());
      paint->setAlphaf(paint->getAlphaf() * currentOpacity);
    }

    if (_paintProps->getStrokeWidth()->isSet()) {
      paint->setStrokeWidth(
          _paintProps->getStrokeWidth()->value().getAsNumber());
    }

    if (_paintProps->getBlendMode()->isSet()) {
      paint->setBlendMode(*_paintProps->getBlendMode()->getDerivedValue());
    }

    if (_paintProps->getStyle()->isSet()) {
      auto styleValue = _paintProps->getStyle()->value().getAsString();
      if (styleValue == "stroke") {
        paint->setStyle(SkPaint::Style::kStroke_Style);
      } else if (styleValue == "fill") {
        paint->setStyle(SkPaint::Style::kFill_Style);
      } else {
        throw std::runtime_error(
            styleValue + " is not a valud value for the style property.");
      }
    }

    if (_paintProps->getStrokeJoin()->isSet()) {
      paint->setStrokeJoin(*_paintProps->getStrokeJoin()->getDerivedValue());
    }

    if (_paintProps->getStrokeCap()->isSet()) {
      paint->setStrokeCap(*_paintProps->getStrokeCap()->getDerivedValue());
    }

    if (_paintProps->getStrokeMiter()->isSet()) {
      paint->setStrokeMiter(
          _paintProps->getStrokeMiter()->value().getAsNumber());
    }

    if (_paintProps->getAntiAlias()->isSet()) {
      paint->setAntiAlias(_paintProps->getAntiAlias()->value().getAsBool());
    }

    if (_paintProps->getDither()->isSet()) {
      paint->setDither(_paintProps->getDither()->value().getAsBool());
    }

    context->save();
    decorateChildren(context);

    auto imageFilter = context->getImageFilters()->popAsOne();
    auto colorFilter = context->getColorFilters()->popAsOne();
    auto shader = context->getShaders()->pop();
    auto maskFilter = context->getMaskFilters()->pop();
    auto pathEffect = context->getPathEffects()->popAsOne();

    context->restore();

    if (imageFilter) {
      paint->setImageFilter(imageFilter);
    }

    if (colorFilter) {
      paint->setColorFilter(colorFilter);
    }

    if (shader) {
      paint->setShader(shader);
    }

    if (maskFilter) {
      paint->setMaskFilter(maskFilter);
    }

    if (pathEffect) {
      paint->setPathEffect(pathEffect);
    }

    context->getPaints()->push(paint);
  }

protected:
  void defineProperties(NodePropsContainer *container) override {
    JsiDomDeclarationNode::defineProperties(container);

    _paintProps = container->defineProperty<PaintProps>();
  }

private:
  PaintProps *_paintProps;
};

} // namespace RNSkia
