using System;
using XGame.Framework.Interfaces;

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

        private int _delay;
        /// <summary>
        /// 间隔
        /// </summary>
        private int _interval;
        /// <summary>
        /// 时间轴
        /// </summary>
        private int _timeAxis;
        /// <summary>
        /// 循环次数上限
        /// </summary>
        private int _loopTimes;
        private Action _action;
        private Action<int> _actionLoop;

        public ITimeListener<ITimer> Listener { get; set; }
        public float TimeScale { get; set; } = 1;

        private int _passTimes;
        private TimerRunState _runState;
        public TimerRunState State
        {
            get => _runState;
            private set
            {
                _runState = value;
                if (value is TimerRunState.Stoped or TimerRunState.Finished or TimerRunState.Exception)
                {
                    try
                    {
                        Listener?.OnCompleted(this);
                    }
                    catch (Exception ex)
                    {
                        Log.Exception($"定时器事件监听执行异常.", ex);
                    }
                    Listener = null;
                }
            }
        }
        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;
            State = 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)
        {
            switch (State)
            {
                case TimerRunState.Stoped:
                case TimerRunState.Finished:
                case TimerRunState.Exception:
                    return true;
                case 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)
                {
                    State = TimerRunState.Exception;
                    Log.Exception($"定时器回调执行发生异常({_passTimes}/{_loopTimes})", e);
                    //throw new Exception($"定时器回调执行发生异常({_passTimes}/{_loopTimes}):{e}");
                }
                if (_passTimes == _loopTimes)
                {
                    State = TimerRunState.Finished;
                    return true;
                }
            }
            return false;
        }

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

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

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

        public void Dispose()
        {
            _action = null;
            _actionLoop = null;
            Listener = null;
        }
        /// <summary>
        /// 只对Running状态的定时器有效
        /// </summary>
        void IReset.Reset()
        {
            if (State != TimerRunState.Running)
            {
                return;
            }
            _timeAxis = 0;
            _passTimes = 0;
        }
        public int TimeAxis => _timeAxis;
        /// <summary>
        /// 下一次回调的剩余时间
        /// </summary>
        public int RemainTime
        {
            get
            {
                var interval = _passTimes == 0 ? _delay : _interval;
                return interval - _timeAxis;
            }
        }
        /// <summary>
        /// 只对Running状态的定时器有效
        /// </summary>
        /// <param name="delay"></param>
        public void ResetForAxis(int timeAsis)
        {
            if (State != TimerRunState.Running)
            {
                return;
            }
            _timeAxis = timeAsis;
            _passTimes = 0;
        }
    }
}
