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

namespace mgaia.Utils
{
    public abstract class AbstractCodeAnimation
    {
        private ValueType[] mBaseKeyValues;
        private ValueType[] mKeyValues;
        private ValueType mInitialValue;
        private ValueType mCurrentFromValue;
        private ValueType mCurrentToValue;
        private float[] mKeyTimes;
        private float mElapsedTime;
        private float mCurrentTargetTime;
        private int mCurrentIndex;
        private object[] mCurrentlyAnimatingObjs;
        private bool mLoop;

        protected abstract void Lerp(ValueType fromValue, ValueType toValue, float normalizedTime);
        protected abstract void OnActivateAnimation(object[] objs);

        public event EventHandler<AnimationEndedEventArgs> AnimationEnded;

        public class AnimationEndedEventArgs : EventArgs
        {
            public object[] Objects { get; private set; }
            public AbstractCodeAnimation Animation { get; private set; }
            public AnimationEndedEventArgs(object[] objs, AbstractCodeAnimation anim)
            {
                Objects = objs;
                Animation = anim;
            }
        }

        public AbstractCodeAnimation(float[] keyTimes, ValueType[] keyValues, ValueType initialValue, bool loop)
        {
            Assert.AssertTrue((keyTimes.Length) >= 1 && (keyTimes.Length == keyValues.Length), "KeyTimes and Values must have, at least, 1 elements and have the same length each other");
            mKeyTimes = keyTimes;
            mKeyValues = keyValues;
            mBaseKeyValues = keyValues;
            mInitialValue = initialValue;
            mLoop = loop;
        }

        public void ActivateAnimation(object[] objs)
        {
            mElapsedTime = 0;
            mCurrentIndex = 0;
            mCurrentTargetTime = mKeyTimes[0];
            mCurrentFromValue = mInitialValue;
            mCurrentToValue = mKeyValues[0];
            mCurrentlyAnimatingObjs = objs;
            OnActivateAnimation(objs);
            Lerp(mCurrentFromValue, mCurrentToValue, 0.0f);
        }

        /// <returns>True if the effect ended, false otherwise</returns>
        public bool Update(float deltaTime)
        {
            Lerp(mCurrentFromValue, mCurrentToValue, mElapsedTime / mCurrentTargetTime);
            mElapsedTime += deltaTime;
            if (mElapsedTime >= mCurrentTargetTime)
            {
                mCurrentIndex++;
                if (mCurrentIndex >= mKeyTimes.Length)
                {
                    Lerp(mCurrentFromValue, mCurrentToValue, 1.0f);
                    if (mLoop)
                    {
                        ActivateAnimation(mCurrentlyAnimatingObjs);
                        return false;
                    }
                    else
                    {
                        if (AnimationEnded != null)
                        {
                            AnimationEnded(this, new AnimationEndedEventArgs(mCurrentlyAnimatingObjs, this));
                        }
                        mCurrentlyAnimatingObjs = null;
                        return true;
                    }
                }
                mElapsedTime = 0;
                mCurrentTargetTime = mKeyTimes[mCurrentIndex] - mKeyTimes[mCurrentIndex - 1];
                mCurrentFromValue = mKeyValues[mCurrentIndex - 1];
                mCurrentToValue = mKeyValues[mCurrentIndex];
            }
            return false;
        }

        public object Clone()
        {
            return MemberwiseClone();
        }
    }
}