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

namespace NBC
{
    class TimerHandler
    {
        public string key;
        public bool repeat;
        public float delay;
        public bool userFrame;
        public float exeTime;
        public object caller;
        public Action<object> methodWithArgs; // 带参数的回调
        public Action methodWithoutArgs; // 不带参数的回调
        public object args;
        public bool jumpFrame;

        public void Clear()
        {
            caller = null;
            methodWithArgs = null;
            methodWithoutArgs = null;
            args = null;
        }

        public void Run(bool withClear)
        {
            if (caller == null)
            {
                Clear();
                return;
            }

            // 优先执行无参数回调
            methodWithoutArgs?.Invoke();
            // 如果没有无参数回调，则执行带参数回调
            methodWithArgs?.Invoke(args);

            if (withClear) Clear();
        }
    }

    public static class Timer
    {
        static Timer()
        {
            MonoManager.Inst.OnUpdate += Update;
        }

        private static void Update()
        {
            _timer.Update();
        }

        private static readonly TimerData _timer = new TimerData();

        #region 无参数回调版本

        /// <summary>
        /// 定时执行一次(无参数)
        /// </summary>
        public static void Once(float delay, object caller, Action method, bool coverBefore = true)
        {
            _timer.Once(delay, caller, method, coverBefore);
        }

        /// <summary>
        /// 定时重复执行(无参数)
        /// </summary>
        public static void Loop(float delay, object caller, Action method, bool coverBefore = true,
            bool jumpFrame = false)
        {
            _timer.Loop(delay, caller, method, coverBefore, jumpFrame);
        }

        /// <summary>
        /// 定时执行一次(基于帧率，无参数)
        /// </summary>
        public static void FrameOnce(int delay, object caller, Action method, bool coverBefore = true)
        {
            _timer.FrameOnce(delay, caller, method, coverBefore);
        }

        /// <summary>
        /// 定时重复执行(基于帧率，无参数)
        /// </summary>
        public static void FrameLoop(int delay, object caller, Action method, bool coverBefore = true)
        {
            _timer.FrameLoop(delay, caller, method, coverBefore);
        }

        /// <summary>
        /// 清理定时器(无参数版本)
        /// </summary>
        public static void Clear(object caller, Action method)
        {
            _timer.Clear(caller, method);
        }

        #endregion

        #region 带参数回调版本

        /// <summary>
        /// 定时执行一次(带参数)
        /// </summary>
        public static void Once(float delay, object caller, Action<object> method, object args = null,
            bool coverBefore = true)
        {
            _timer.Once(delay, caller, method, args, coverBefore);
        }

        /// <summary>
        /// 定时重复执行(带参数)
        /// </summary>
        public static void Loop(float delay, object caller, Action<object> method, object args = null,
            bool coverBefore = true, bool jumpFrame = false)
        {
            _timer.Loop(delay, caller, method, args, coverBefore, jumpFrame);
        }

        /// <summary>
        /// 定时执行一次(基于帧率，带参数)
        /// </summary>
        public static void FrameOnce(int delay, object caller, Action<object> method, object args = null,
            bool coverBefore = true)
        {
            _timer.FrameOnce(delay, caller, method, args, coverBefore);
        }

        /// <summary>
        /// 定时重复执行(基于帧率，带参数)
        /// </summary>
        public static void FrameLoop(int delay, object caller, Action<object> method, object args = null,
            bool coverBefore = true)
        {
            _timer.FrameLoop(delay, caller, method, args, coverBefore);
        }

        /// <summary>
        /// 清理定时器(带参数版本)
        /// </summary>
        public static void Clear(object caller, Action<object> method)
        {
            _timer.Clear(caller, method);
        }

        #endregion

        /// <summary>
        /// 清理对象身上的所有定时器
        /// </summary>
        public static void ClearAll(object caller)
        {
            _timer.ClearAll(caller);
        }
    }

    public class TimerData
    {
        private static readonly Queue<TimerHandler> _pool = new Queue<TimerHandler>();
        private static int _mid = 1;

        public float CurrTimer = Time.time;
        public int CurrFrame = 0;
        private float _delta = 0;
        private float _lastTimer = Time.time;

        private Dictionary<string, TimerHandler> _map = new Dictionary<string, TimerHandler>();
        private List<TimerHandler> _handlers = new List<TimerHandler>();
        private List<TimerHandler> _temp = new List<TimerHandler>();
        private int _count = 0;

        public float delta => _delta;

        internal void Update()
        {
            var frame = CurrFrame += 1;
            var now = Time.time;
            var awake = (now - _lastTimer) > 30000;

            _delta = (now - _lastTimer);
            var timer = CurrTimer += _delta;
            _lastTimer = now;

            _count = 0;
            for (int i = 0, n = _handlers.Count; i < n; i++)
            {
                var handler = _handlers[i];
                if (handler.methodWithArgs != null || handler.methodWithoutArgs != null)
                {
                    var t = handler.userFrame ? frame : timer;
                    if (t >= handler.exeTime)
                    {
                        if (handler.repeat)
                        {
                            if (!handler.jumpFrame || awake)
                            {
                                handler.exeTime += handler.delay;
                                handler.Run(false);
                                if (t > handler.exeTime)
                                {
                                    handler.exeTime += Mathf.Ceil((t - handler.exeTime) / handler.delay) *
                                                       handler.delay;
                                }
                            }
                            else
                            {
                                while (t >= handler.exeTime)
                                {
                                    handler.exeTime += handler.delay;
                                    handler.Run(false);
                                }
                            }
                        }
                        else
                        {
                            handler.Run(true);
                        }
                    }
                }
                else
                {
                    _count++;
                }
            }

            if (_count > 30 || frame % 200 == 0) _clearHandlers();
        }

        private void _clearHandlers()
        {
            var handlers = _handlers;
            for (int i = 0, n = handlers.Count; i < n; i++)
            {
                var handler = handlers[i];
                if (handler.methodWithArgs != null || handler.methodWithoutArgs != null)
                    _temp.Add(handler);
                else
                    _recoverHandler(handler);
            }

            _handlers = _temp;
            handlers.Clear();
            _temp = handlers;
        }

        private void _recoverHandler(TimerHandler handler)
        {
            if (_map.TryGetValue(handler.key, out var h) && h == handler)
            {
                _map.Remove(handler.key);
                handler.Clear();
                _pool.Enqueue(handler);
            }
        }

        #region 无参数回调版本

        public void Once(float delay, object caller, Action method, bool coverBefore = true)
        {
            _create(false, false, delay, caller, null, method, null, coverBefore);
        }

        public void Loop(float delay, object caller, Action method, bool coverBefore = true, bool jumpFrame = false)
        {
            var handler = _create(false, true, delay, caller, null, method, null, coverBefore);
            if (handler != null) handler.jumpFrame = jumpFrame;
        }

        public void FrameOnce(int delay, object caller, Action method, bool coverBefore = true)
        {
            _create(true, false, delay, caller, null, method, null, coverBefore);
        }

        public void FrameLoop(int delay, object caller, Action method, bool coverBefore = true)
        {
            _create(true, true, delay, caller, null, method, null, coverBefore);
        }

        public void Clear(object caller, Action method)
        {
            var handler = _getHandler(caller, null, method);
            handler?.Clear();
        }

        #endregion

        #region 带参数回调版本

        public void Once(float delay, object caller, Action<object> method, object args = null, bool coverBefore = true)
        {
            _create(false, false, delay, caller, method, null, args, coverBefore);
        }

        public void Loop(float delay, object caller, Action<object> method, object args = null, bool coverBefore = true,
            bool jumpFrame = false)
        {
            var handler = _create(false, true, delay, caller, method, null, args, coverBefore);
            if (handler != null) handler.jumpFrame = jumpFrame;
        }

        public void FrameOnce(int delay, object caller, Action<object> method, object args = null,
            bool coverBefore = true)
        {
            _create(true, false, delay, caller, method, null, args, coverBefore);
        }

        public void FrameLoop(int delay, object caller, Action<object> method, object args = null,
            bool coverBefore = true)
        {
            _create(true, true, delay, caller, method, null, args, coverBefore);
        }

        public void Clear(object caller, Action<object> method)
        {
            var handler = _getHandler(caller, method, null);
            handler?.Clear();
        }

        #endregion

        private TimerHandler _create(bool useFrame, bool repeat, float delay, object caller,
            Action<object> methodWithArgs, Action methodWithoutArgs,
            object args, bool coverBefore)
        {
            // 如果延迟为0，则立即执行
            if (delay <= 0)
            {
                if (methodWithoutArgs != null)
                    methodWithoutArgs.Invoke();
                else
                    methodWithArgs?.Invoke(args);
                return null;
            }

            TimerHandler handler;
            // 先覆盖相同函数的计时
            if (coverBefore)
            {
                handler = _getHandler(caller, methodWithArgs, methodWithoutArgs);
                if (handler != null)
                {
                    handler.repeat = repeat;
                    handler.userFrame = useFrame;
                    handler.delay = delay;
                    handler.caller = caller;
                    handler.methodWithArgs = methodWithArgs;
                    handler.methodWithoutArgs = methodWithoutArgs;
                    handler.args = args;
                    handler.exeTime = delay + (useFrame ? CurrFrame : CurrTimer + Time.time - _lastTimer);
                    return handler;
                }
            }

            // 找到一个空闲的timerHandler
            handler = _pool.Count > 0 ? _pool.Dequeue() : new TimerHandler();
            handler.repeat = repeat;
            handler.userFrame = useFrame;
            handler.delay = delay;
            handler.caller = caller;
            handler.methodWithArgs = methodWithArgs;
            handler.methodWithoutArgs = methodWithoutArgs;
            handler.args = args;
            handler.exeTime = delay + (useFrame ? CurrFrame : CurrTimer + Time.time - _lastTimer);

            // 索引handler
            _indexHandler(handler);

            // 插入数组
            _handlers.Add(handler);

            return handler;
        }

        private TimerHandler _getHandler(object caller, Action<object> methodWithArgs, Action methodWithoutArgs)
        {
            var key = caller.GetHashCode() + "_" +
                      (methodWithArgs?.GetHashCode() ?? methodWithoutArgs.GetHashCode());
            return _map.GetValueOrDefault(key);
        }

        private void _indexHandler(TimerHandler handler)
        {
            var key = handler.caller.GetHashCode() + "_" +
                      (handler.methodWithArgs?.GetHashCode() ?? handler.methodWithoutArgs.GetHashCode());
            handler.key = key;
            _map[key] = handler;
        }

        public void ClearAll(object caller)
        {
            if (caller == null) return;
            for (int i = 0, n = _handlers.Count; i < n; i++)
            {
                var handler = _handlers[i];
                if (handler.caller == caller)
                {
                    handler.Clear();
                }
            }
        }

        public override string ToString()
        {
            return "handlers:" + _handlers.Count + "pool:" + _pool.Count;
        }
    }
}