using System;
using JetBrains.Annotations;
using UnityEngine;
using UnityEngine.Playables;
using UnityEngine.Timeline;

[Serializable]
public class TransformTweenBehaviour : PlayableBehaviour
{
    public enum TweenType
    {
        Linear,
        Deceleration,
        Harmonic,
        Custom,
    }

    public Transform startLocation;
    public Transform endLocation;
    public bool tweenPosition = true;
    public bool tweenRotation = true;
    public TweenType tweenType;
    public AnimationCurve customCurve = AnimationCurve.Linear(0f, 0f, 1f, 1f);
    
    public Vector3 startingPosition;
    public Quaternion startingRotation = Quaternion.identity;

    AnimationCurve m_LinearCurve = AnimationCurve.Linear(0f, 0f, 1f, 1f);
    AnimationCurve m_DecelerationCurve = new AnimationCurve
    (
        new Keyframe(0f, 0f, -k_RightAngleInRads, k_RightAngleInRads),
        new Keyframe(1f, 1f, 0f, 0f)
    );
    AnimationCurve m_HarmonicCurve = AnimationCurve.EaseInOut(0f, 0f, 1f, 1f);

    const float k_RightAngleInRads = Mathf.PI * 0.5f;

    public override void PrepareFrame (Playable playable, FrameData info)
    {
        if (startLocation)
        {
            startingPosition = startLocation.position;
            startingRotation = startLocation.rotation;
        }
    }

    public float EvaluateCurrentCurve (float time)
    {
        if (tweenType == TweenType.Custom && !IsCustomCurveNormalised ())
        {
            Debug.LogError("Custom Curve is not normalised.  Curve must start at 0,0 and end at 1,1.");
            return 0f;
        }
        
        switch (tweenType)
        {
            case TweenType.Linear:
                return m_LinearCurve.Evaluate (time);
            case TweenType.Deceleration:
                return m_DecelerationCurve.Evaluate (time);
            case TweenType.Harmonic:
                return m_HarmonicCurve.Evaluate (time);
            default:
                return customCurve.Evaluate (time);
        }
    }

    bool IsCustomCurveNormalised ()
    {
        if (!Mathf.Approximately (customCurve[0].time, 0f))
            return false;
        
        if (!Mathf.Approximately (customCurve[0].value, 0f))
            return false;
        
        if (!Mathf.Approximately (customCurve[customCurve.length - 1].time, 1f))
            return false;
        
        return Mathf.Approximately (customCurve[customCurve.length - 1].value, 1f);
    }
}