using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using log4net;

namespace VIZ.Framework.Core
{
    /// <summary>
    /// 延时管理器
    /// </summary>
    public class DelayManager : IDelayManager, IDisposable
    {
        /// <summary>
        /// 日志
        /// </summary>
        private static ILog log = LogManager.GetLogger(typeof(DelayManager));

        /// <summary>
        /// 缓存池
        /// </summary>
        private Dictionary<string, DelayInfo> Pool = new Dictionary<string, DelayInfo>(100);

        /// <summary>
        /// 延时线程
        /// </summary>
        private System.Threading.Thread Thread;

        /// <summary>
        /// 是否已经被释放
        /// </summary>
        private bool IsDisposabled = false;

        public DelayManager()
        {
            this.Thread = new System.Threading.Thread(this.Execute);
            this.Thread.Start();
        }

        /// <summary>
        /// 延时执行
        /// </summary>
        /// <param name="key">键</param>
        /// <param name="delaySeconds">延时秒数</param>
        /// <param name="action">行为</param>
        public void Wait(string key, double delaySeconds, Action action)
        {
            this.Wait(key, TimeSpan.FromSeconds(delaySeconds), action);
        }

        /// <summary>
        /// 延时执行
        /// </summary>
        /// <param name="key">键</param>
        /// <param name="delayTime">延时时间</param>
        /// <param name="action">行为</param>
        public void Wait(string key, TimeSpan delayTime, Action action)
        {
            lock (this.Pool)
            {
                if (this.Pool.TryGetValue(key, out DelayInfo info))
                {
                    info.Action = action;
                    return;
                }

                info = new DelayInfo();
                info.Key = key;
                info.Action = action;
                info.BeginTime = DateTime.Now;
                info.DelayTime = delayTime;
                info.ExecuteTime = info.BeginTime + info.DelayTime;

                this.Pool.Add(key, info);
            }
        }

        /// <summary>
        /// 释放延时执行
        /// </summary>
        /// <param name="key">键</param>
        public void Release(string key)
        {
            lock (this.Pool)
            {
                if (!this.Pool.TryGetValue(key, out DelayInfo info))
                    return;

                this.Pool.Remove(key);

                info.Dispose();
            }
        }

        /// <summary>
        /// 释放资源
        /// </summary>
        public void Dispose()
        {
            try
            {
                lock (this.Pool)
                {
                    foreach (DelayInfo info in this.Pool.Values)
                    {
                        info.Dispose();
                    }

                    this.Pool.Clear();
                }

                this.IsDisposabled = true;
                this.Thread = null;
            }
            catch (Exception ex)
            {
                log.Error(ex);
            }
        }

        /// <summary>
        /// 执行延时任务
        /// </summary>
        private void Execute()
        {
            while (!this.IsDisposabled)
            {
                lock (this.Pool)
                {
                    DateTime now = DateTime.Now;
                    List<DelayInfo> removeList = new List<DelayInfo>();
                    foreach (DelayInfo info in this.Pool.Values)
                    {
                        if (this.IsDisposabled)
                            break;

                        if (info.ExecuteTime > now)
                            continue;

                        removeList.Add(info);

                        try
                        {
                            info.Action?.Invoke();
                        }
                        catch (Exception ex)
                        {
                            log.Error(ex);
                        }
                    }

                    foreach (DelayInfo info in removeList)
                    {
                        this.Pool.Remove(info.Key);
                    }
                }

                System.Threading.Thread.Sleep(200);
            }
        }
    }
}
