using System.Collections;
using System.Collections.Generic;
using UnityEngine;

public class ScriptedAnimation : MonoBehaviour
{
    [Header("General Settings")]
    [SerializeField] bool m_animateOnStart = true;
    [SerializeField] bool m_loop = false;
    [SerializeField] bool m_loopInReverse = false;
    [SerializeField] float m_animationDuration = 1;

    [Header("Rotation Settings")]
    [SerializeField] Vector3 m_rotationAxis = Vector3.up;
    [SerializeField] float m_rotationValue = 1;
    [SerializeField] AnimationCurve m_animationCurve = new AnimationCurve()
    {
        keys = new Keyframe[]
        {
            new Keyframe(0, 0),
            new Keyframe(1, 1)
        }
    };

    float m_endTime
    {
        get
        {
            return m_animationCurve.Evaluate(m_animationCurve.keys[m_animationCurve.keys.Length - 1].time);
        }
    }

    private void OnValidate()
    {
        bool _hasZero = false;
        List<int> _keyIndices = new List<int>();

        for (int k = m_animationCurve.keys.Length - 1; k >= 0; k--)
        {
            if (m_animationCurve.keys[k].time == 0)
                _hasZero = true;

            if (m_animationCurve.keys[k].time < 0)
                _keyIndices.Add(k);
        }

        if (_keyIndices.Count > 0)
            foreach (int _key in _keyIndices)
                m_animationCurve.RemoveKey(_key);

        if (!_hasZero)
            m_animationCurve.AddKey(0, 0);
    }

    private void Start()
    {
        if (m_animateOnStart)
            StartAnimation();
    }

    public void StartAnimation()
    {
        StopAllCoroutines();

        StartCoroutine(RunAnimation(m_animationDuration));
    }

    IEnumerator RunAnimation(float _timescale = 1)
    {
        if (_timescale == 0)
            yield break;

        bool _reverse = _timescale < 0;

        Quaternion _startRotation = transform.rotation;

        Transform _rotateHelper = new GameObject().transform;
        _rotateHelper.rotation = _startRotation;
        _rotateHelper.RotateAround(transform.position, 
            transform.right * m_rotationAxis.x + transform.up * m_rotationAxis.y + transform.forward * m_rotationAxis.z, 
            m_rotationValue * 
            Mathf.Sign(_timescale));

        Quaternion _endRotation = _rotateHelper.rotation;

        Destroy(_rotateHelper.gameObject);
        
        for (float t = 0; t < m_animationCurve.Evaluate(m_endTime); t += Time.deltaTime * Mathf.Abs(_timescale))
        {
            transform.rotation = Quaternion.Slerp(
                _reverse ? _endRotation : _startRotation,
                _reverse ? _startRotation : _endRotation, 
                m_animationCurve.Evaluate(_reverse ? m_endTime - t : t));

            yield return null;
        }

        transform.rotation = _endRotation;

        yield return null;

        if (m_loop)
            StartCoroutine(RunAnimation(m_loopInReverse ? _timescale * -1 : _timescale));

        yield break;
    }
}
