using System;
using System.Collections.Generic;
using Fort23.Core;

namespace Fort23.Core
{
    public enum TimerType
    {
        None,
        Wait, // 等待
        Count, // 次数
    }


    public class TimerEntity : Entity
    {
        public TimerType timerType;

        /// <summary>
        /// 计时时间，毫秒
        /// </summary>
        public long time;

        /// <summary>
        /// 完成回调
        /// </summary>
        public object callback1;

        /// <summary>
        /// 次数
        /// </summary> 
        public int count;

        /// <summary>
        /// 针对多次的，每次Hit回调
        /// </summary>
        public object callback2;

        [CustomMethod(CustomMethodType.Awake)]
        public void Awake(TimerType timerType, long time, object callback1, int count, object callback2)
        {
            this.timerType = timerType;
            this.time = time;
            this.callback1 = callback1;
            this.count = count;
            this.callback2 = callback2;
        }

        [CustomMethod(CustomMethodType.Destroy)]
        public void Destroy()
        {
            this.timerType = TimerType.None;
            this.time = 0;
            this.callback1 = null;
            this.count = 0;
            this.callback2 = null;
        }
    }


    public class TimerComponent : Entity
    {
        public static TimerComponent Instance { get; set; }

        /// <summary>
        /// key: time, value: timer id
        /// </summary>
        private readonly MultiMap<long, long> _timeID = new MultiMap<long, long>();

        private readonly Queue<long> _timeOutTime = new Queue<long>();

        private readonly Queue<long> _timeOutTimerIds = new Queue<long>();

        // 记录最小时间，不用每次都去MultiMap取第一个值
        private long _minTime;

        [CustomMethod(CustomMethodType.Awake)]
        public void Awake()
        {
            Instance = this;
        }

        [CustomMethod(CustomMethodType.Update)]
        public void Update()
        {
            if (this._timeID.Count == 0)
            {
                return;
            }

            long timeNow = TimeHelper.ServerNow();
            if (timeNow < this._minTime)
            {
                return;
            }

            foreach (KeyValuePair<long, List<long>> kv in this._timeID)
            {
                long k = kv.Key;
                if (k > timeNow)
                {
                    _minTime = k;
                    break;
                }

                this._timeOutTime.Enqueue(k);
            }
            
            // 2.找出失效时间对应的ID
            while (this._timeOutTime.Count > 0)
            {
                long time = this._timeOutTime.Dequeue();
                // 3.取出对应的TimerEntity执行逻辑
                for (int i = 0; i < this._timeID[time].Count; i++)
                {
                    TimerEntity timerEntity = this.GetChild<TimerEntity>(_timeID[time][i]);
                    if (timerEntity != null)
                    {
                        Run(timerEntity, time);
                    }
                }
                // foreach (long timerId in this._timeID[time])
                // {
                //     TimerEntity timerEntity = this.GetChild<TimerEntity>(timerId);
                //     if (timerEntity != null)
                //     {
                //         Run(timerEntity, time);
                //     }
                // }

                this._timeID.Remove(time);
            }

            // while (this._timeOutTime.Count > 0)
            // {
            //     long time = this._timeOutTime.Dequeue();
            //     foreach (long timerId in this._timeID[time])
            //     {
            //         this._timeOutTimerIds.Enqueue(timerId);
            //     }
            //
            //     this._timeID.Remove(time);
            // }
            //
            // while (this._timeOutTimerIds.Count > 0)
            // {
            //     long timerId = this._timeOutTimerIds.Dequeue();
            //     TimerEntity timerEntity = this.GetChild<TimerEntity>(timerId);
            //     if (timerEntity == null)
            //     {
            //         continue;
            //     }
            //
            //     Run(timerEntity, timerId);
            // }
        }

        private void Run(TimerEntity timerEntity, long timestamp)
        {
            switch (timerEntity.timerType)
            {
                case TimerType.Wait:
                {
                    CTask<bool> tcs = timerEntity.callback1 as CTask<bool>;
                    this.Remove(timerEntity.ID);
                    tcs.SetResult(true);
                    break;
                }
                case TimerType.Count:
                {
                    if (timerEntity.count <= 1)
                    {
                        Action action = timerEntity.callback1 as Action;
                        this.Remove(timerEntity.ID);
                        action?.Invoke();
                        break;
                    }
                    else
                    {
                        long currTime = TimeHelper.ServerNow();
                        long tillTime = currTime - currTime % timestamp + timerEntity.time;
                        this.AddTimer(tillTime, timerEntity);
                        Action action = timerEntity.callback2 as Action;
                        action?.Invoke();
                        timerEntity.count--;
                        break;
                    }
                }
            }
        }

        private void AddTimer(long untillTime, TimerEntity timerEntity)
        {
            this._timeID.Add(untillTime, timerEntity.ID);
            if (untillTime < this._minTime)
            {
                this._minTime = untillTime;
            }
        }

        /// <summary>
        /// 等待1帧(因为实在下一次Update里面刷新，所以WaitAsync传的值小于一帧的间隔就可以)
        /// </summary>
        /// <returns></returns>
        public async CTask<bool> WaitFrameAsync()
        {
            return await WaitAsync(1);
        }

        /// <summary>
        /// 等待time毫秒
        /// </summary>
        /// <param name="time">毫秒</param>
        /// <returns></returns>
        public async CTask<bool> WaitAsync(long time)
        {
            if (time == 0)
            {
                return true;
            }

            long tillTime = TimeHelper.ServerNow() + time;
            CTask<bool> tcs = CTask<bool>.Create(false);
            TimerEntity timerEntity = this.AddChild<TimerEntity, TimerType, long, object, int, object>(TimerType.Wait, 0, tcs, 1, null, true);
            this.AddTimer(tillTime, timerEntity);
            return await tcs;
        }

        /// <summary>
        /// 等待到指定毫秒的时间戳
        /// </summary>
        /// <param name="untillTime">毫秒</param>
        /// <returns></returns>
        public async CTask<bool> WaitTillAsync(long untillTime)
        {
            if (TimeHelper.ServerNow() >= untillTime)
            {
                return true;
            }

            CTask<bool> tcs = CTask<bool>.Create();
            TimerEntity timerEntity = this.AddChild<TimerEntity, TimerType, long, object, int, object>(TimerType.Wait, 0, tcs, 1, null, true);
            this.AddTimer(untillTime, timerEntity);
            return await tcs;
        }

        /// <summary>
        /// 
        /// </summary>
        /// <param name="time">计时器Hit一次的时间(毫秒)</param>
        /// <param name="action1">全部count完成时候回调</param>
        /// <param name="count">次数</param>
        /// <param name="action2">针对多次执行的Timer，每一次Hit时候的回调</param>
        /// <returns>ID, 可以通过Remove来手动中止Timer</returns>
        public TimerEntity AddTimer(long time, Action action1, int count = 1, Action action2 = null)
        {
            TimerEntity timerEntity = this.AddChild<TimerEntity, TimerType, long, object, int, object>(TimerType.Count, time, action1, count, action2, true);
            this.AddTimer(TimeHelper.ServerNow() + time, timerEntity);
            return timerEntity;
        }

        public void Remove(ref long id)
        {
            this.Remove(id);
            id = 0;
        }

        public bool Remove(TimerEntity timerEntity)
        {
            if (timerEntity == null)
            {
                return false;
            }

            timerEntity.Dispose();
            return true;
        }

        public bool Remove(long id)
        {
            if (id == 0)
            {
                return false;
            }

            TimerEntity timerEntity = this.GetChild<TimerEntity>(id);
            return Remove(timerEntity);
        }
    }
}