/********************************************************************
	created:	2018/05/31
	created:	31:5:2018   23:07
	filename: 	C:\X2M\x2m\mainline\dev\client\Assets\Script\X2Interface\Core\Pool\ObjectPool.cs
	file path:	C:\X2M\x2m\mainline\dev\client\Assets\Script\X2Interface\Core\Pool
	file base:	ObjectPool
	file ext:	cs
	author:		zhang qiang
	
	purpose:	object pool
*********************************************************************/


namespace Giant.Common
{
using System.Collections.Generic;

    public class ObjectPool<T> : IObjectPool where T : new()
    {
        private readonly Stack<T> m_Stack = new Stack<T>();

        public int CountAll { get; private set; }
        public int CountActive { get { return CountAll - CountInactive; } }
        public int CountInactive { get { return m_Stack.Count; } }

        /// <summary>
        /// 
        /// </summary>
        /// <param name="clearCpacity">当空闲对象数量大于等于此值时才清理对象池</param>
        /// <param name="checkInterval">清理对象池的最小间隔时间</param>
        public ObjectPool(int clearCpacity = 1, float checkInterval = 600f)
        {
            m_ClearCpacity = clearCpacity;
            m_CheckInterval = checkInterval;
            m_NextCheckTime = UnityEngine.Time.time + m_CheckInterval;
            ObjectPoolManager.Instance.AddPool(this);
        }

        public T Get()
        {
            m_LastGetTime = UnityEngine.Time.time;
            T element;
            if (m_Stack.Count == 0)
            {
                element = new T();
                CountAll++;
            }
            else
            {
                element = m_Stack.Pop();
            }        
            return element;
        }

        public void Release(T element)
        {
            //if (m_Stack.Count > 0 && ReferenceEquals(m_Stack.Peek(), element))
            //  LogHelper.LogError(LogCategory.Framework, "Internal error. Trying to destroy object that is already released to pool.");        
            m_Stack.Push(element);
        }

        private int m_ClearCpacity;              
        private float m_CheckInterval;
        /// <summary>
        /// 下次检查清理空闲对象的时刻
        /// </summary>
        private float m_NextCheckTime;
        /// <summary>
        /// 最近一次从池中请求对象的时刻
        /// </summary>
        private float m_LastGetTime;

        private readonly float m_ClearGetInterval = 5f;

        public bool CheckClearInactive()
        {
            float time = UnityEngine.Time.time;
            //还未到达检查时间，直接返回
            if (time < m_NextCheckTime)
                return false;
            m_NextCheckTime = time + m_CheckInterval;
            //距离上次从池中获取对象时间较短，可能即将有创建对象的需求，不清理
            if (time - m_LastGetTime < m_ClearGetInterval)
                return false;
            //空闲对象数量较少，不清理
            if (m_Stack.Count < m_ClearCpacity)
                return false;
            return true;
        }
        
        public void ClearInactive()
        {
            //if (m_Stack.Count > 0)
            //{
            //    T element = m_Stack.Peek();
            //    string typeStr = element.GetType().ToString();
            //    LogHelper.Log("ObjectPool Clear. Type:" + typeStr + ", Stack Count:" + m_Stack.Count, LogCategory.Framework);
            //}
            m_Stack.Clear();
        }
    }

    public interface IObjectPool
    {
        /// <summary>
        /// 检查是否需要清理
        /// </summary>
        /// <returns></returns>
        bool CheckClearInactive();
        
        /// <summary>
        /// 清理空闲对象，释放内存
        /// </summary>
        void ClearInactive();
    }

    public class ObjectPoolManager
    {
        static protected ObjectPoolManager m_instance;
        public static ObjectPoolManager Instance
        {
            get
            {
                if (m_instance == null)
                    m_instance = new ObjectPoolManager();
                return m_instance;
            }
        }

        private List<IObjectPool> m_PoolList = new List<IObjectPool>(68);

        public void AddPool(IObjectPool pool)
        {
            m_PoolList.Add(pool);
            //string stackInfo = new System.Diagnostics.StackTrace().ToString();
            //LogHelper.Log("Add ObjectPool. Count:" + m_PoolList.Count + ", \n call stack:" + stackInfo, LogCategory.Framework);
        }

        /// <summary>
        /// 非静态类或单例对象的对象池，当对象析构时调用此方法从管理器中移除对象池
        /// </summary>
        /// <param name="pool"></param>
        public void RemovePool(IObjectPool pool)
        {
            m_PoolList.Remove(pool);
        }
        
        public void Update()
        {
            for (int i = 0; i < m_PoolList.Count; i++)
            {
                if (m_PoolList[i].CheckClearInactive())
                    m_PoolList[i].ClearInactive();
            }
        }
    }
}
