using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Threading;
using JLHHJSvr.Com.Model;
using LJLib.D;

namespace LJLib
{
    /// <summary>
    /// 缓存，超时自动清除，线程安全
    /// </summary>
    public sealed class LJCache<TKey, TValue> : Dictionary<TKey, TValue>
    {
        /// <summary>
        /// 自动延长分钟数
        /// </summary>
        public double DefaultAddMinutes
        {
            get { return _defaultAddMinutes;}
            set { _defaultAddMinutes = value; }
        }

        /// <summary>
        /// 添加键值对, 
        /// </summary>
        /// <param name="key"></param>
        /// <param name="value"></param>
        /// <param name="minutes">超时分钟数</param>
        public void Add(TKey key, TValue value, double minutes)
        {
            var deadline = DateTime.Now.AddMinutes(minutes);
            Add(key, value, deadline);
        }

        /// <summary>
        /// 添加永久对象或者按默认时间添加
        /// </summary>
        /// <param name="key"></param>
        /// <param name="value"></param>
        public new void Add(TKey key, TValue value)
        {
            Add(key, value, _defaultAddMinutes);
        }

        public new TValue this[TKey key]
        {
            get
            {
                lock (syncRoot)
                {
                    SetDeadLine(key, DateTime.Now.AddMinutes(DefaultAddMinutes));
                    return base[key];
                }
            }
            set
            {
                lock (syncRoot)
                {
                    SetDeadLine(key, DateTime.Now.AddMinutes(DefaultAddMinutes));
                    base[key] = value;
                }
            }
        }

        public new bool ContainsKey(TKey key)
        {
            lock (syncRoot)
            {
                var rslt = base.ContainsKey(key);
                if (rslt)
                {
                    SetDeadLine(key, DateTime.Now.AddMinutes(DefaultAddMinutes));
                }
                return rslt;
            }
        }

        public bool TryGetValue(TKey key, out TValue value)
        {
            lock (syncRoot)
            {
                if(base.TryGetValue(key,out var entry))
                {
                    value = entry;

                    SetDeadLine(key, DateTime.Now.AddMinutes(DefaultAddMinutes));
                    return true;
                }

                value = default(TValue);
                return false;
            }
        }


        #region 稀有成员实现逻辑

        /// <summary>
        /// 添加键值对，设置超时时间
        /// </summary>
        /// <param name="key"></param>
        /// <param name="value"></param>
        /// <param name="deadline">超时时间</param>
        private void Add(TKey key, TValue value, DateTime deadline)
        {
            lock (syncRoot)
            {
                base[key] = value;
                SetDeadLine(key, deadline);
            }
        }

        /// <summary>
        /// 手动设置超时时间
        /// </summary>
        /// <param name="key"></param>
        /// <param name="deadline"></param>
        /// <returns></returns>
        private void SetDeadLine(TKey key, DateTime deadline)
        {
            lock (syncRoot)
            {
                deadline = deadline.AddMilliseconds(-deadline.Millisecond);

                if (_deadlines == null)
                {
                    _deadlines = new Dictionary<TKey, DateTime>();
                    _ix_keys = new SortedList<DateTime, HashSet<TKey>>();
                }

                if (_deadlines.ContainsKey(key))
                {
                    var olddt = _deadlines[key];
                    _ix_keys[olddt].Remove(key);
                    if (_ix_keys[olddt].Count == 0)
                    {
                        _ix_keys.Remove(olddt);
                    }
                }

                _deadlines[key] = deadline;
                if (!_ix_keys.ContainsKey(deadline))
                {
                    _ix_keys[deadline] = new HashSet<TKey>();
                }
                if (!_ix_keys[deadline].Contains(key))
                {
                    _ix_keys[deadline].Add(key);
                }

                if (_timer == null)
                {
                    _timer = new Timer((state) =>
                    {
                        try
                        {
                            //DebugHelper.GetTotalMemory("LJCache.Remove.开始");
                            lock (syncRoot)
                            {
                                var dtNow = DateTime.Now;
                                List<DateTime> dts = new List<DateTime>();
                                for (int i = 0; _ix_keys != null && i < _ix_keys.Keys.Count && _ix_keys.Keys[i] <= dtNow; i++)
                                {
                                    dts.Add(_ix_keys.Keys[i]);
                                    foreach (var oldkey in _ix_keys.Values[i])
                                    {
                                        _deadlines.Remove(oldkey);
                                        base.Remove(oldkey);
                                    }
                                }
                                foreach (var dt in dts)
                                {
                                    _ix_keys.Remove(dt);
                                }
                            }
                            //DebugHelper.GetTotalMemory("LJCache.Remove.结束");
                        }
                        catch (Exception ex)
                        {
                            Trace.Write(ex);
                        }
                    }, null, 1000, 1000);
                }   
            }
        }

        /// <summary>
        /// 线路同步对象
        /// </summary>
        private object syncRoot = new object();

        /// <summary>
        /// 缓存清除定时器
        /// </summary>
        private Timer _timer;

        /// <summary>
        /// 各键值对的销毁时间
        /// </summary>
        private Dictionary<TKey, DateTime> _deadlines;
        /// <summary>
        /// 按时间顺序过期
        /// </summary>
        private SortedList<DateTime, HashSet<TKey>> _ix_keys;

        /// <summary>
        /// 自动延长分钟数
        /// </summary>
        private double _defaultAddMinutes = 5;

        public LJCache(IEqualityComparer<TKey> comparer) : base(comparer) { }

        public LJCache() : base() { }

        #endregion
    }
}
