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

#include "include/core/SkPoint.h"
#include "include/core/SkRefCnt.h"
#include "include/core/SkScalar.h"
#include "modules/skottie/src/Adapter.h"
#include "modules/skottie/src/SkottiePriv.h"
#include "modules/skottie/src/SkottieValue.h"
#include "modules/skottie/src/Transform.h"
#include "modules/skottie/src/animator/Animator.h"
#include "modules/skottie/src/effects/Effects.h"
#include "modules/sksg/include/SkSGOpacityEffect.h"
#include "modules/sksg/include/SkSGRenderNode.h"
#include "modules/sksg/include/SkSGTransform.h"

#include <cstddef>
#include <utility>

namespace skjson {
class ArrayValue;
class ObjectValue;
}  // namespace skjson

namespace skottie {
namespace internal {

namespace  {

class TransformEffectAdapter final : public DiscardableAdapterBase<TransformEffectAdapter,
                                                                   sksg::OpacityEffect> {
public:
    TransformEffectAdapter(const AnimationBuilder& abuilder,
                           const skjson::ObjectValue* jopacity,
                           const skjson::ObjectValue* jscale_uniform,
                           const skjson::ObjectValue* jscale_width,
                           const skjson::ObjectValue* jscale_height,
                           sk_sp<TransformAdapter2D> tadapter,
                           sk_sp<sksg::RenderNode> child)
        : INHERITED(sksg::OpacityEffect::Make(std::move(child)))
        , fTransformAdapter(std::move(tadapter)) {
        this->bind(abuilder, jopacity      , fOpacity     );
        this->bind(abuilder, jscale_uniform, fUniformScale);
        this->bind(abuilder, jscale_width  , fScaleWidth  );
        this->bind(abuilder, jscale_height , fScaleHeight );

        this->attachDiscardableAdapter(fTransformAdapter);
    }

private:
    void onSync() override {
        this->node()->setOpacity(fOpacity * 0.01f);

        // In uniform mode, the scale is based solely in ScaleHeight.
        const auto scale = SkVector::Make(SkScalarRoundToInt(fUniformScale) ? fScaleHeight
                                                                            : fScaleWidth,
                                          fScaleHeight);

        // NB: this triggers an transform adapter -> SG sync.
        fTransformAdapter->setScale(scale);
    }

    const sk_sp<TransformAdapter2D> fTransformAdapter;

    ScalarValue fOpacity      = 100,
                fUniformScale =   0, // bool
                fScaleWidth   = 100,
                fScaleHeight  = 100;

    using INHERITED = DiscardableAdapterBase<TransformEffectAdapter, sksg::OpacityEffect>;
};

}  // namespace

sk_sp<sksg::RenderNode> EffectBuilder::attachTransformEffect(const skjson::ArrayValue& jprops,
                                                             sk_sp<sksg::RenderNode> layer) const {
    enum : size_t {
        kAnchorPoint_Index            =  0,
        kPosition_Index               =  1,
        kUniformScale_Index           =  2,
        kScaleHeight_Index            =  3,
        kScaleWidth_Index             =  4,
        kSkew_Index                   =  5,
        kSkewAxis_Index               =  6,
        kRotation_Index               =  7,
        kOpacity_Index                =  8,
        // kUseCompShutterAngle_Index =  9,
        // kShutterAngle_Index        = 10,
        // kSampling_Index            = 11,
    };

    auto transform_adapter = TransformAdapter2D::Make(*fBuilder,
                                                      GetPropValue(jprops, kAnchorPoint_Index),
                                                      GetPropValue(jprops, kPosition_Index),
                                                      nullptr, // scale is handled externally
                                                      GetPropValue(jprops, kRotation_Index),
                                                      GetPropValue(jprops, kSkew_Index),
                                                      GetPropValue(jprops, kSkewAxis_Index));
    if (!transform_adapter) {
        return nullptr;
    }

    auto transform_effect_node = sksg::TransformEffect::Make(std::move(layer),
                                                             transform_adapter->node());
    return fBuilder->attachDiscardableAdapter<TransformEffectAdapter>
            (*fBuilder,
             GetPropValue(jprops, kOpacity_Index),
             GetPropValue(jprops, kUniformScale_Index),
             GetPropValue(jprops, kScaleWidth_Index),
             GetPropValue(jprops, kScaleHeight_Index),
             std::move(transform_adapter),
             std::move(transform_effect_node)
             );
}

} // namespace internal
} // namespace skottie
