/*
 * Copyright 2020 Google Inc.
 *
 * Use of this source code is governed by a BSD-style license that can be
 * found in the LICENSE file.
 */

#ifndef SkRive_DEFINED
#define SkRive_DEFINED

#include "include/core/SkBlendMode.h"
#include "include/core/SkColor.h"
#include "include/core/SkM44.h"
#include "include/core/SkPaint.h"
#include "include/core/SkPathTypes.h"
#include "include/core/SkRefCnt.h"
#include "include/core/SkString.h"

#include <memory>
#include <type_traits>
#include <vector>

class SkCanvas;
class SkPaint;
class SkStreamAsset;

namespace skrive {

#define ACTOR_ATTR(attr_name, attr_type, attr_default)               \
private:                                                             \
    attr_type f##attr_name = attr_default;                           \
public:                                                              \
    const attr_type& get##attr_name() const { return f##attr_name; } \
    void set##attr_name(const attr_type& v) {                        \
        if (f##attr_name == v) return;                               \
        f##attr_name = v;                                            \
        this->invalidate();                                          \
    }                                                                \
    void set##attr_name(attr_type&& v) {                             \
        if (f##attr_name == v) return;                               \
        f##attr_name = std::move(v);                                 \
        this->invalidate();                                          \
    }

class Node;

class Component : public SkRefCnt {
public:
    ACTOR_ATTR(Name, SkString, SkString())

    template <typename T>
    std::enable_if_t<std::is_base_of<Component, T>::value, bool>
    is() const {
        if constexpr(std::is_same<Component, T>::value) {
            return true;
        } else {
            return is_base_of<T>(fType);
        }
    }

    template <typename T>
    operator const T*() const {
        return this->is<T>() ? reinterpret_cast<const T*>(this) : nullptr;
    }

    template <typename T>
    operator T*() {
        return this->is<T>() ? reinterpret_cast<T*>(this) : nullptr;
    }

    void revalidate();

    // probably not the right place
    void render(SkCanvas* canvas) const {
        this->onRender(canvas);
    }

protected:
    enum class Type : uint32_t {
        kNode,
        kShape,
        kColorPaint,
        kEllipse,
        kRectangle,
    };

    explicit Component(Type t) : fType(t) {}

    void invalidate();

    bool hasInval() const { return fDirty; }

    virtual void onRevalidate() = 0;
    virtual void onRender(SkCanvas*) const;

private:
    friend class Node; // parent access

    template <typename T>
    static constexpr bool is_base_of(Type t);

    const Type  fType;

    Node* fParent = nullptr;
    bool  fDirty  = true;
};

class TransformableComponent : public Component {
public:
    ACTOR_ATTR(Translation        , SkV2 , SkV2({0, 0}))
    ACTOR_ATTR(Scale              , SkV2 , SkV2({1, 1}))
    ACTOR_ATTR(Rotation           , float, 0           )
    ACTOR_ATTR(Opacity            , float, 1           )

protected:
    explicit TransformableComponent(Type t) : INHERITED(t) {}

    class ScopedTransformContext final {
    public:
        ScopedTransformContext(const TransformableComponent*, SkCanvas*);
        ~ScopedTransformContext();

    private:
        SkCanvas* fCanvas;
        const int fRestoreCount;
    };

private:
    using INHERITED = Component;
};

class Node : public TransformableComponent {
public:
    Node() : INHERITED(Type::kNode) {}

    ACTOR_ATTR(CollapsedVisibility, bool , false       )

    void addChild(sk_sp<Component>);

    const std::vector<sk_sp<Component>>& children() const { return fChildren; }

protected:
    explicit Node(Type t) : INHERITED(t) {}

    void onRevalidate() override;

    void onRender(SkCanvas*) const override;

private:
    std::vector<sk_sp<Component>> fChildren;

    using INHERITED = TransformableComponent;
};

class Paint : public Component {
public:
    ACTOR_ATTR(Opacity    , float         , 1                         )
    ACTOR_ATTR(FillRule   , SkPathFillType, SkPathFillType::kWinding  )
    ACTOR_ATTR(StrokeWidth, float         , 1                         )
    ACTOR_ATTR(StrokeCap  , SkPaint::Cap  , SkPaint::Cap::kButt_Cap   )
    ACTOR_ATTR(StrokeJoin , SkPaint::Join , SkPaint::Join::kMiter_Join)

    enum class StrokeTrim : uint8_t { kOff, kSequential, kSynced };
    ACTOR_ATTR(StrokeTrim      , StrokeTrim, StrokeTrim::kOff)
    ACTOR_ATTR(StrokeTrimStart , float     , 0)
    ACTOR_ATTR(StrokeTrimEnd   , float     , 0)
    ACTOR_ATTR(StrokeTrimOffset, float     , 0)

    void apply(SkPaint* paint) const {
        this->onApply(paint);
    }

    SkPaint::Style style() const { return fStyle; }

protected:
    Paint(Type t, SkPaint::Style style) : INHERITED(t), fStyle(style) {}

    virtual void onApply(SkPaint*) const;

private:
    const SkPaint::Style fStyle;

    using INHERITED = Component;
};

class ColorPaint final : public Paint {
public:
    explicit ColorPaint(SkPaint::Style style) : INHERITED(Type::kColorPaint, style) {}

    ACTOR_ATTR(Color, SkColor4f, SkColors::kBlack)

private:
    void onRevalidate() override;

    void onApply(SkPaint*) const override;

    using INHERITED = Paint;
};

class Geometry : public Node {
public:
    void draw(SkCanvas* canvas, const SkPaint& paint, SkPathFillType ftype) const {
        return this->onDraw(canvas, paint, ftype);
    }

protected:
    explicit Geometry(Type t) : INHERITED(t) {}

    virtual void onDraw(SkCanvas*, const SkPaint&, SkPathFillType) const = 0;

private:
    using INHERITED = Node;
};

class Ellipse final : public Geometry {
public:
    Ellipse() : INHERITED(Type::kEllipse) {}

    ACTOR_ATTR(Width , float, 0)
    ACTOR_ATTR(Height, float, 0)

private:
    void onRevalidate() override;
    void onDraw(SkCanvas*, const SkPaint&, SkPathFillType) const override;

    using INHERITED = Geometry;
};

class Rectangle final : public Geometry {
public:
    Rectangle() : INHERITED(Type::kRectangle) {}

    ACTOR_ATTR(Width , float, 0)
    ACTOR_ATTR(Height, float, 0)
    ACTOR_ATTR(Radius, float, 0)

private:
    void onRevalidate() override;
    void onDraw(SkCanvas*, const SkPaint&, SkPathFillType) const override;

    using INHERITED = Geometry;
};

class Drawable : public Node {
public:
    ACTOR_ATTR(DrawOrder, size_t     , 0                    )
    ACTOR_ATTR(BlendMode, SkBlendMode, SkBlendMode::kSrcOver)
    ACTOR_ATTR(IsHidden , bool       , false                )

protected:
    explicit Drawable(Type t) : INHERITED(t) {}

private:
    using INHERITED = Node;
};

class Shape final : public Drawable {
public:
    Shape() : INHERITED(Type::kShape) {}

    ACTOR_ATTR(TransformAffectsStroke, bool, true)

private:
    void onRevalidate() override;
    void onRender(SkCanvas*) const override;

    // cached on revalidation
    // tracked separately due to paint order (all fills before strokes)
    std::vector<const Paint*>    fFills,
                                 fStrokes;

    std::vector<const Geometry*> fGeometries;

    using INHERITED = Drawable;
};

template <typename T>
constexpr bool Component::is_base_of(Type t) {
    if (t == Type::kNode      ) return std::is_base_of<T, Node      >::value;
    if (t == Type::kShape     ) return std::is_base_of<T, Shape     >::value;
    if (t == Type::kColorPaint) return std::is_base_of<T, ColorPaint>::value;
    if (t == Type::kEllipse   ) return std::is_base_of<T, Ellipse   >::value;
    if (t == Type::kRectangle ) return std::is_base_of<T, Rectangle >::value;

    return false;
}

class Artboard final : public SkRefCnt {
public:
    ACTOR_ATTR(Root        , sk_sp<Node>, nullptr         )
    ACTOR_ATTR(Name        , SkString   , SkString()      )
    ACTOR_ATTR(Color       , SkColor4f  , SkColors::kBlack)
    ACTOR_ATTR(Size        , SkV2       , SkV2({0,0})     )
    ACTOR_ATTR(Origin      , SkV2       , SkV2({0,0})     )
    ACTOR_ATTR(Translation , SkV2       , SkV2({0,0})     )
    ACTOR_ATTR(ClipContents, bool       , false           )

    void render(SkCanvas*) const;

private:
    void invalidate() {}
};

class SK_API SkRive final : public SkNVRefCnt<SkRive> {
public:
    class Builder final {
    public:
        sk_sp<SkRive> make(std::unique_ptr<SkStreamAsset>);
    };

    const std::vector<sk_sp<Artboard>>& artboards() const { return fArtboards; }
          std::vector<sk_sp<Artboard>>& artboards()       { return fArtboards; }

private:
    std::vector<sk_sp<Artboard>> fArtboards;
};

}  // namespace skrive

#endif // SkRive_DEFINED