using System;
using System.Collections.Generic;
using System.Threading.Tasks;
using MrWu.Debug;

namespace Server.Core
{
    public class TimerManager : ITimerManager
    {
        private enum TimerClass
        {
            None,
            OnceTimer,
            OnceWaitTimer,
            RepeatedTimer,
        }

        private struct TimerAction
        {
            public TimerClass TimerClass;

            public Action<object> Action;

            public object Object;

            public long StartTime;

            public long Time;

            public TimerAction(TimerClass timerClass, long startTime, long time, Action<object> action, object obj)
            {
                this.TimerClass = timerClass;
                this.StartTime = startTime;
                this.Time = time;
                this.Action = action;
                this.Object = obj;
            }
        }

        /// <summary>
        /// key: time 下次要运行的时间, value: timer id   KEY 有排序功能
        /// </summary>
        private readonly NativeCollection.MultiMap<long, long> timeId = new NativeCollection.MultiMap<long, long>(1000);

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

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

        /// <summary>
        /// 所有定时器
        /// </summary>
        private readonly Dictionary<long, TimerAction> timerActions = new Dictionary<long, TimerAction>();

        private long idGenerator;

        /// <summary>
        /// 最小定时器时间
        /// </summary>
        private long minTime = long.MaxValue;

        private long GetId()
        {
            return ++this.idGenerator;
        }

        public void Update()
        {
            if (this.timeId.Count == 0)
            {
                return;
            }

            //Debug.Info($"定时器模块:{timeId.Count}");

            long timeNow = this.GetNow();
            if (timeNow < this.minTime)
            {
                return;
            }

            //Debug.Info($"minTime:{minTime}");

            foreach (var kv in this.timeId)
            {
                long k = kv.Key;
                if (k > timeNow)
                {
                    this.minTime = k;
                    break;
                }

                timeOutTime.Enqueue(k);
            }

            while (this.timeOutTime.Count > 0)
            {
                long time = this.timeOutTime.Dequeue();
                var list = this.timeId[time];
                for (int i = 0; i < list.Length; i++)
                {
                    long timeOutId = list[i];
                    this.timeOutTimerIds.Enqueue(timeOutId);
                }

                this.timeId.Remove(time);
            }

            if (this.timeId.Count == 0)
            {
                this.minTime = long.MaxValue;
            }

            while (this.timeOutTimerIds.Count > 0)
            {
                long timeOutId = this.timeOutTimerIds.Dequeue();

                if (!this.timerActions.TryGetValue(timeOutId, out TimerAction timerAction))
                {
                    continue;
                }

                this.timerActions.Remove(timeOutId);

                Run(timeOutId, ref timerAction);
            }
        }

        private void Run(long timeOutId, ref TimerAction timerAction)
        {
            switch (timerAction.TimerClass)
            {
                case TimerClass.OnceTimer:
                {
                    InvokeTimer(ref timerAction);
                }
                    break;
                case TimerClass.OnceWaitTimer:
                {
                    TaskCompletionSource<bool> tcs = timerAction.Object as TaskCompletionSource<bool>;
                    tcs.SetResult(true);
                }
                    break;
                case TimerClass.RepeatedTimer:
                {
                    long timeNow = this.GetNow();
                    timerAction.StartTime = timeNow;
                    this.AddTimer(timeOutId, ref timerAction); //添加到定时器，继续执行

                    //执行定时器
                    InvokeTimer(ref timerAction);
                }
                    break;
            }
        }

        private void InvokeTimer(ref TimerAction timerAction)
        {
            try
            {
                timerAction.Action?.Invoke(timerAction.Object);
            }
            catch (Exception e)
            {
                Debug.Error($"定时器出错:{e.Message}");
            }
        }

        private long GetNow()
        {
            return TimeInfo.Instance.ServerFrameTime();
        }

        private long NewRepeatedTimerInner(long time, Action<object> action, object args)
        {
            Debug.Info($"NewRepeatedTimerInner {time}");
            if (time < 100)
            {
                Debug.Warning($"time too small:{time}");
            }

            long timeNow = GetNow();
            long timerId = GetId();

            TimerAction timer = new TimerAction(TimerClass.RepeatedTimer, timeNow, time, action, args);
            this.AddTimer(timerId, ref timer);
            return timerId;
        }

        private void AddTimer(long timerId, ref TimerAction timer)
        {
            long tillTime = timer.StartTime + timer.Time;
            this.timeId.Add(tillTime, timerId);
            this.timerActions.Add(timerId, timer);
            if (tillTime < minTime)
            {
                minTime = tillTime;
            }

            //Debug.Info($"添加定时器:{tillTime} {timer.StartTime} {minTime}");
        }

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

            if (!this.timerActions.Remove(id))
            {
                return false;
            }

            return true;
        }

        #region ITimerManager

        public async Task WaitTillAsync(long tillTime)
        {
            long timeNow = GetNow();
            if (timeNow > tillTime)
            {
                return;
            }

            TaskCompletionSource<bool> tcs = new TaskCompletionSource<bool>();
            long timerId = this.GetId();
            TimerAction timer = new TimerAction(TimerClass.OnceWaitTimer, timeNow, tillTime - timeNow, null, tcs);
            this.AddTimer(timerId, ref timer);
            await tcs.Task;
        }

        public async Task WaitFrameAsync()
        {
            await this.WaitAsync(1);
        }

        public async Task WaitAsync(long time)
        {
            if (time == 0)
            {
                return;
            }

            long timeNow = GetNow();
            TaskCompletionSource<bool> tcs = new TaskCompletionSource<bool>();
            long timerId = this.GetId();
            TimerAction timer = new TimerAction(TimerClass.OnceWaitTimer, timeNow, time, null, tcs);
            this.AddTimer(timerId, ref timer);
            await tcs.Task;
        }

        public long NewOnceTimer(long tillTime, Action<object> action, object args)
        {
            long timeNow = GetNow();

            if (tillTime < timeNow)
            {
                Debug.Error($"now once time too small:{tillTime}");
            }

            long timeId = GetId();
            TimerAction timer = new TimerAction(TimerClass.OnceTimer, timeNow, tillTime - timeNow, action, args);
            this.AddTimer(timeId, ref timer);
            return timeId;
        }

        public long NewFrameTimer(Action<object> action, object args)
        {
            return NewRepeatedTimerInner(0, action, args);
        }

        public long NewRepeatedTimer(long time, Action<object> action, object args)
        {
            return NewRepeatedTimerInner(time, action, args);
        }

        public bool Remove(ref long id)
        {
            long i = id;
            id = 0;
            return Remove(i);
        }

        #endregion
    }
}