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

#ifndef SkottieEffects_DEFINED
#define SkottieEffects_DEFINED

#include "include/core/SkRefCnt.h"
#include "include/core/SkShader.h"
#include "include/core/SkSize.h"
#include "include/private/base/SkNoncopyable.h"
#include "modules/skottie/src/Composition.h"
#include "modules/skottie/src/animator/Animator.h"
#include "modules/sksg/include/SkSGRenderEffect.h"
#include "src/utils/SkJSON.h"  // IWYU pragma: keep

#include <cstddef>

namespace sksg {
class RenderNode;
} // namespace sksg

namespace skottie {
namespace internal {
class AnimationBuilder;
class LayerBuilder;

class EffectBuilder final : public SkNoncopyable {
public:
    EffectBuilder(const AnimationBuilder*, const SkSize&, CompositionBuilder*);

    sk_sp<sksg::RenderNode> attachEffects(const skjson::ArrayValue&,
                                          sk_sp<sksg::RenderNode>) const;

    sk_sp<sksg::RenderNode> attachStyles(const skjson::ArrayValue&,
                                         sk_sp<sksg::RenderNode>) const;

    static const skjson::Value& GetPropValue(const skjson::ArrayValue& jprops, size_t prop_index);

    LayerBuilder* getLayerBuilder(int layer_index) const {
        return fCompBuilder->layerBuilder(layer_index);
    }

private:
    using EffectBuilderT = sk_sp<sksg::RenderNode>(EffectBuilder::*)(const skjson::ArrayValue&,
                                                                     sk_sp<sksg::RenderNode>) const;

    sk_sp<sksg::RenderNode> attachBlackAndWhiteEffect     (const skjson::ArrayValue&,
                                                           sk_sp<sksg::RenderNode>) const;
    sk_sp<sksg::RenderNode> attachBrightnessContrastEffect(const skjson::ArrayValue&,
                                                           sk_sp<sksg::RenderNode>) const;
    sk_sp<sksg::RenderNode> attachBulgeEffect            (const skjson::ArrayValue&,
                                                           sk_sp<sksg::RenderNode>) const;
    sk_sp<sksg::RenderNode> attachCornerPinEffect         (const skjson::ArrayValue&,
                                                            sk_sp<sksg::RenderNode>) const;
    sk_sp<sksg::RenderNode> attachCCTonerEffect           (const skjson::ArrayValue&,
                                                            sk_sp<sksg::RenderNode>) const;
    sk_sp<sksg::RenderNode> attachDirectionalBlurEffect   (const skjson::ArrayValue&,
                                                            sk_sp<sksg::RenderNode>) const;
    sk_sp<sksg::RenderNode> attachDisplacementMapEffect   (const skjson::ArrayValue&,
                                                           sk_sp<sksg::RenderNode>) const;
    sk_sp<sksg::RenderNode> attachDropShadowEffect        (const skjson::ArrayValue&,
                                                           sk_sp<sksg::RenderNode>) const;
    sk_sp<sksg::RenderNode> attachFillEffect              (const skjson::ArrayValue&,
                                                           sk_sp<sksg::RenderNode>) const;
    sk_sp<sksg::RenderNode> attachFractalNoiseEffect      (const skjson::ArrayValue&,
                                                           sk_sp<sksg::RenderNode>) const;
    sk_sp<sksg::RenderNode> attachGaussianBlurEffect      (const skjson::ArrayValue&,
                                                           sk_sp<sksg::RenderNode>) const;
    sk_sp<sksg::RenderNode> attachGradientEffect          (const skjson::ArrayValue&,
                                                           sk_sp<sksg::RenderNode>) const;
    sk_sp<sksg::RenderNode> attachHueSaturationEffect     (const skjson::ArrayValue&,
                                                           sk_sp<sksg::RenderNode>) const;
    sk_sp<sksg::RenderNode> attachInvertEffect            (const skjson::ArrayValue&,
                                                           sk_sp<sksg::RenderNode>) const;
    sk_sp<sksg::RenderNode> attachEasyLevelsEffect        (const skjson::ArrayValue&,
                                                           sk_sp<sksg::RenderNode>) const;
    sk_sp<sksg::RenderNode> attachLinearWipeEffect        (const skjson::ArrayValue&,
                                                           sk_sp<sksg::RenderNode>) const;
    sk_sp<sksg::RenderNode> attachMotionTileEffect        (const skjson::ArrayValue&,
                                                           sk_sp<sksg::RenderNode>) const;
    sk_sp<sksg::RenderNode> attachProLevelsEffect         (const skjson::ArrayValue&,
                                                           sk_sp<sksg::RenderNode>) const;
    sk_sp<sksg::RenderNode> attachRadialWipeEffect        (const skjson::ArrayValue&,
                                                           sk_sp<sksg::RenderNode>) const;
    sk_sp<sksg::RenderNode> attachSharpenEffect            (const skjson::ArrayValue&,
                                                           sk_sp<sksg::RenderNode>) const;
    sk_sp<sksg::RenderNode> attachShiftChannelsEffect     (const skjson::ArrayValue&,
                                                           sk_sp<sksg::RenderNode>) const;
    sk_sp<sksg::RenderNode> attachSkSLColorFilter         (const skjson::ArrayValue&,
                                                           sk_sp<sksg::RenderNode>) const;
    sk_sp<sksg::RenderNode> attachSkSLShader              (const skjson::ArrayValue&,
                                                           sk_sp<sksg::RenderNode>) const;
    sk_sp<sksg::RenderNode> attachSphereEffect            (const skjson::ArrayValue&,
                                                           sk_sp<sksg::RenderNode>) const;
    sk_sp<sksg::RenderNode> attachThresholdEffect         (const skjson::ArrayValue&,
                                                           sk_sp<sksg::RenderNode>) const;
    sk_sp<sksg::RenderNode> attachTintEffect              (const skjson::ArrayValue&,
                                                           sk_sp<sksg::RenderNode>) const;
    sk_sp<sksg::RenderNode> attachTransformEffect         (const skjson::ArrayValue&,
                                                           sk_sp<sksg::RenderNode>) const;
    sk_sp<sksg::RenderNode> attachTritoneEffect           (const skjson::ArrayValue&,
                                                           sk_sp<sksg::RenderNode>) const;
    sk_sp<sksg::RenderNode> attachVenetianBlindsEffect    (const skjson::ArrayValue&,
                                                           sk_sp<sksg::RenderNode>) const;

    sk_sp<sksg::RenderNode> attachDropShadowStyle(const skjson::ObjectValue&,
                                                  sk_sp<sksg::RenderNode>) const;
    sk_sp<sksg::RenderNode> attachInnerShadowStyle(const skjson::ObjectValue&,
                                                   sk_sp<sksg::RenderNode>) const;
    sk_sp<sksg::RenderNode> attachInnerGlowStyle(const skjson::ObjectValue&,
                                                 sk_sp<sksg::RenderNode>) const;
    sk_sp<sksg::RenderNode> attachOuterGlowStyle(const skjson::ObjectValue&,
                                                 sk_sp<sksg::RenderNode>) const;

    EffectBuilderT findBuilder(const skjson::ObjectValue&) const;

    const AnimationBuilder* fBuilder;
    CompositionBuilder*     fCompBuilder;
    const SkSize            fLayerSize;
};

// Syntactic sugar/helper.
class EffectBinder {
public:
    EffectBinder(const skjson::ArrayValue& jprops,
                 const AnimationBuilder& abuilder,
                 AnimatablePropertyContainer* acontainer)
        : fProps(jprops)
        , fBuilder(abuilder)
        , fContainer(acontainer) {}

    template <typename T>
    const EffectBinder& bind(size_t prop_index, T& value) const {
        fContainer->bind(fBuilder, EffectBuilder::GetPropValue(fProps, prop_index), value);

        return *this;
    }

private:
    const skjson::ArrayValue&    fProps;
    const AnimationBuilder&      fBuilder;
    AnimatablePropertyContainer* fContainer;
};

/**
 * Base class for mask-shader-related effects.
 */
class MaskShaderEffectBase : public AnimatablePropertyContainer {
public:
    const sk_sp<sksg::MaskShaderEffect>& node() const { return fMaskEffectNode; }

protected:
    MaskShaderEffectBase(sk_sp<sksg::RenderNode>, const SkSize&);

    const SkSize& layerSize() const { return  fLayerSize; }

    struct MaskInfo {
        sk_sp<SkShader> fMaskShader;
        bool            fVisible;
    };
    virtual MaskInfo onMakeMask() const = 0;

private:
    void onSync() final;

    const sk_sp<sksg::MaskShaderEffect> fMaskEffectNode;
    const SkSize                        fLayerSize;
};

} // namespace internal
} // namespace skottie

#endif // SkottieEffects_DEFINED
