using System;

namespace XGame.Framework.Time
{
    public class Timer : ITimer
    {
        public enum TimerRunState : SByte
        {
            None,
            Running,
            Paused,
            Stoped,
            Finished,
            Exception,
        }

        private int _delay;
        private int _interval;
        private int _timeAxis;
        private int _loopTimes;
        private Action _action;
        private Action<int> _actionLoop;

        public float TimeScale { get; set; } = 1;
        private int _passTimes;
        private TimerRunState _runState;

        internal Timer(int delay, int interval, int looptimes, Action action)
        {
#if UNITY_EDITOR
            UnityEngine.Assertions.Assert.IsTrue(delay >= 0, "延迟不能<0");
            UnityEngine.Assertions.Assert.IsTrue(!(delay == 0 && looptimes == 1), "立即执行的延迟定时器次数不能小于2");

            UnityEngine.Assertions.Assert.IsTrue(interval > 0, "定时器间隔不能<=0");
            UnityEngine.Assertions.Assert.IsTrue(looptimes != 0, "定时器执行次数不能为0,如果要无限循环请设置为-1");
            UnityEngine.Assertions.Assert.IsNotNull(action, "定时器回调不能为null");
#endif
            _action = action;
            InnerInit(delay, interval, looptimes);
        }

        internal Timer(int delay, int interval, int looptimes, Action<int> action)
        {
#if UNITY_EDITOR
            UnityEngine.Assertions.Assert.IsTrue(delay >= 0, "延迟不能<0");
            UnityEngine.Assertions.Assert.IsTrue(!(delay == 0 && looptimes == 1), "立即执行的延迟定时器次数不能小于2");

            UnityEngine.Assertions.Assert.IsTrue(interval > 0, "定时器间隔不能<=0");
            UnityEngine.Assertions.Assert.IsTrue(looptimes != 0, "定时器执行次数不能为0,如果要无限循环请设置为-1");
            UnityEngine.Assertions.Assert.IsNotNull(action, "定时器回调不能为null");
#endif

            _actionLoop = action;
            InnerInit(delay, interval, looptimes);
        }

        private void InnerInit(int delay, int interval, int looptimes)
        {
            _delay = delay;
            _interval = interval;
            _loopTimes = looptimes;
            _passTimes = 0;
            _runState = TimerRunState.Running;
        }


        //校准时间
        private float addingValue = 0;
        private int CorrectTime(int deltaTime)
        {
            float updateTime = (float)deltaTime * TimeScale;
            int elapse = (int)updateTime;
            float timeDecimals = updateTime - elapse;
            addingValue += timeDecimals;
            if (addingValue >= 1.0f)
            {
                elapse += 1;
                addingValue -= 1.0f;
            }
            return elapse;
        }

        internal bool Update(int deltaTime)
        {
            if (_runState == TimerRunState.Stoped ||
                _runState == TimerRunState.Finished ||
                _runState == TimerRunState.Exception)
            {
                return true;
            }

            if (_runState == TimerRunState.Paused)
            {
                return false;
            }

            int correctedTime = CorrectTime(deltaTime);
            _timeAxis += correctedTime;

            var interval = _passTimes == 0 ? _delay : _interval;
            if (_timeAxis > interval)
            {
                _timeAxis -= interval;
                _passTimes += 1;
                try
                {
                    _action?.Invoke();
                    _actionLoop?.Invoke(_passTimes);
                }
                catch (Exception e)
                {
                    _runState = TimerRunState.Exception;
                    Log.Exception($"定时器回调执行发生异常({_passTimes}/{_loopTimes})", e);
                    //throw new Exception($"定时器回调执行发生异常({_passTimes}/{_loopTimes}):{e}");
                }
                if (_passTimes == _loopTimes)
                {
                    _runState = TimerRunState.Finished;
                    return true;
                }
            }
            return false;
        }

        public void Cancel()
        {
            _runState = TimerRunState.Stoped;
            _action = null;
            _actionLoop = null;
        }

        public void Pause()
        {
            if (_runState == TimerRunState.Running)
                _runState = TimerRunState.Paused;
        }

        public void Resume()
        {
            if (_runState == TimerRunState.Paused)
                _runState = TimerRunState.Running;
        }

        public void Dispose()
        {
            _action = null;
            _actionLoop = null;
        }
    }
}
