/*
 * Copyright (C) 2014 The Android Open Source Project
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
#ifndef ANIMATOR_H
#define ANIMATOR_H

#include "CanvasProperty.h"

#include <cutils/compiler.h>
#include <utils/RefBase.h>
#include <utils/StrongPointer.h>
#include <utils/Timers.h>
#include <memory>

#include "utils/Macros.h"

#include <vector>

namespace android {
namespace uirenderer {

class AnimationContext;
class BaseRenderNodeAnimator;
class Interpolator;
class RenderNode;
class RenderProperties;

class AnimationListener : public VirtualLightRefBase {
public:
    ANDROID_API virtual void onAnimationFinished(BaseRenderNodeAnimator*) = 0;

protected:
    ANDROID_API virtual ~AnimationListener() {}
};

enum class RepeatMode {
    // These are the same values as the RESTART and REVERSE in ValueAnimator.java.
    Restart = 1,
    Reverse = 2
};

class BaseRenderNodeAnimator : public VirtualLightRefBase {
    PREVENT_COPY_AND_ASSIGN(BaseRenderNodeAnimator);

public:
    ANDROID_API void setStartValue(float value);
    ANDROID_API void setInterpolator(Interpolator* interpolator);
    ANDROID_API void setDuration(nsecs_t durationInMs);
    ANDROID_API nsecs_t duration() { return mDuration; }
    ANDROID_API void setStartDelay(nsecs_t startDelayInMs);
    ANDROID_API nsecs_t startDelay() { return mStartDelay; }
    ANDROID_API void setListener(AnimationListener* listener) { mListener = listener; }
    AnimationListener* listener() { return mListener.get(); }
    ANDROID_API void setAllowRunningAsync(bool mayRunAsync) { mMayRunAsync = mayRunAsync; }
    bool mayRunAsync() { return mMayRunAsync; }
    ANDROID_API void start();
    ANDROID_API virtual void reset();
    ANDROID_API void reverse();
    // Terminates the animation at its current progress.
    ANDROID_API void cancel();

    // Terminates the animation and skip to the end of the animation.
    ANDROID_API virtual void end();

    void attach(RenderNode* target);
    virtual void onAttached() {}
    void detach() { mTarget = nullptr; }
    ANDROID_API void pushStaging(AnimationContext& context);
    ANDROID_API bool animate(AnimationContext& context);

    // Returns the remaining time in ms for the animation. Note this should only be called during
    // an animation on RenderThread.
    ANDROID_API nsecs_t getRemainingPlayTime();

    bool isRunning() {
        return mPlayState == PlayState::Running || mPlayState == PlayState::Reversing;
    }
    bool isFinished() { return mPlayState == PlayState::Finished; }
    float finalValue() { return mFinalValue; }

    ANDROID_API virtual uint32_t dirtyMask() = 0;

    void forceEndNow(AnimationContext& context);
    RenderNode* target() { return mTarget; }
    RenderNode* stagingTarget() { return mStagingTarget; }

protected:
    // PlayState is used by mStagingPlayState and mPlayState to track the state initiated from UI
    // thread and Render Thread animation state, respectively.
    // From the UI thread, mStagingPlayState transition looks like
    // NotStarted -> Running/Reversing -> Finished
    //                ^                     |
    //                |                     |
    //                ----------------------
    // Note: For mStagingState, the Finished state (optional) is only set when the animation is
    // terminated by user.
    //
    // On Render Thread, mPlayState transition:
    // NotStart -> Running/Reversing-> Finished
    //                ^                 |
    //                |                 |
    //                ------------------
    // Note that if the animation is in Running/Reversing state, calling start or reverse again
    // would do nothing if the animation has the same play direction as the request; otherwise,
    // the animation would start from where it is and change direction (i.e. Reversing <-> Running)

    enum class PlayState {
        NotStarted,
        Running,
        Reversing,
        Finished,
    };

    explicit BaseRenderNodeAnimator(float finalValue);
    virtual ~BaseRenderNodeAnimator();

    virtual float getValue(RenderNode* target) const = 0;
    virtual void setValue(RenderNode* target, float value) = 0;

    void callOnFinishedListener(AnimationContext& context);

    virtual void onStagingPlayStateChanged() {}
    virtual void onPlayTimeChanged(nsecs_t playTime) {}
    virtual void onPushStaging() {}

    RenderNode* mTarget;
    RenderNode* mStagingTarget;

    float mFinalValue;
    float mDeltaValue;
    float mFromValue;

    std::unique_ptr<Interpolator> mInterpolator;
    PlayState mStagingPlayState;
    PlayState mPlayState;
    bool mHasStartValue;
    nsecs_t mStartTime;
    nsecs_t mDuration;
    nsecs_t mStartDelay;
    bool mMayRunAsync;
    // Play Time tracks the progress of animation, it should always be [0, mDuration], 0 being
    // the beginning of the animation, will reach mDuration at the end of an animation.
    nsecs_t mPlayTime;

    sp<AnimationListener> mListener;

private:
    enum class Request { Start, Reverse, Reset, Cancel, End };

    // Defines different actions upon finish.
    enum class Action {
        // For animations that got canceled or finished normally. no more action needs to be done.
        None,
        // For animations that get reset, the reset will happen in the next animation pulse.
        Reset,
        // For animations being ended, in the next animation pulse the animation will skip to end.
        End
    };

    inline void checkMutable();
    virtual void transitionToRunning(AnimationContext& context);
    void doSetStartValue(float value);
    bool updatePlayTime(nsecs_t playTime);
    void resolveStagingRequest(Request request);

    std::vector<Request> mStagingRequests;
    Action mPendingActionUponFinish = Action::None;
};

class RenderPropertyAnimator : public BaseRenderNodeAnimator {
public:
    enum RenderProperty {
        TRANSLATION_X = 0,
        TRANSLATION_Y,
        TRANSLATION_Z,
        SCALE_X,
        SCALE_Y,
        ROTATION,
        ROTATION_X,
        ROTATION_Y,
        X,
        Y,
        Z,
        ALPHA,
    };

    ANDROID_API RenderPropertyAnimator(RenderProperty property, float finalValue);

    ANDROID_API virtual uint32_t dirtyMask();

protected:
    virtual float getValue(RenderNode* target) const override;
    virtual void setValue(RenderNode* target, float value) override;
    virtual void onAttached() override;
    virtual void onStagingPlayStateChanged() override;
    virtual void onPushStaging() override;

private:
    typedef bool (RenderProperties::*SetFloatProperty)(float value);
    typedef float (RenderProperties::*GetFloatProperty)() const;

    struct PropertyAccessors;
    const PropertyAccessors* mPropertyAccess;

    static const PropertyAccessors PROPERTY_ACCESSOR_LUT[];
    bool mShouldSyncPropertyFields = false;
    bool mShouldUpdateStagingProperties = false;
};

class CanvasPropertyPrimitiveAnimator : public BaseRenderNodeAnimator {
public:
    ANDROID_API CanvasPropertyPrimitiveAnimator(CanvasPropertyPrimitive* property,
                                                float finalValue);

    ANDROID_API virtual uint32_t dirtyMask();

protected:
    virtual float getValue(RenderNode* target) const override;
    virtual void setValue(RenderNode* target, float value) override;

private:
    sp<CanvasPropertyPrimitive> mProperty;
};

class CanvasPropertyPaintAnimator : public BaseRenderNodeAnimator {
public:
    enum PaintField {
        STROKE_WIDTH = 0,
        ALPHA,
    };

    ANDROID_API CanvasPropertyPaintAnimator(CanvasPropertyPaint* property, PaintField field,
                                            float finalValue);

    ANDROID_API virtual uint32_t dirtyMask();

protected:
    virtual float getValue(RenderNode* target) const override;
    virtual void setValue(RenderNode* target, float value) override;

private:
    sp<CanvasPropertyPaint> mProperty;
    PaintField mField;
};

class RevealAnimator : public BaseRenderNodeAnimator {
public:
    ANDROID_API RevealAnimator(int centerX, int centerY, float startValue, float finalValue);

    ANDROID_API virtual uint32_t dirtyMask();

protected:
    virtual float getValue(RenderNode* target) const override;
    virtual void setValue(RenderNode* target, float value) override;

private:
    int mCenterX, mCenterY;
};

} /* namespace uirenderer */
} /* namespace android */

#endif /* ANIMATOR_H */
