#if !USE_CUSTOM_VSTRING_SCRIPT
#if DISPOSE_LEAK_DETECT
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.IO;
using UnityEngine;
using System.Linq;

namespace Giant.Common
{
    /// <summary>
    /// 非托管内存泄漏检查器
    /// </summary>
    public class DisposeSentinel : IDisposable
    {
        private StackTrace m_StackTrace = new StackTrace(true);



        public DisposeSentinel()
        {
            DisposeSentinelManager.Instance.AddDisposeSentinel(this);
        }



        public void Dispose()
        {
            DisposeSentinelManager.Instance.RemoveDisposeSentinel(this);
        }

        public void PrintToString(StringWriter writer, Dictionary<string, int> stackMap)
        {
            writer.GetStringBuilder().Clear();
            int tStackDepth = 0;
            tStackDepth = 0;
            writer.WriteLine("------------------------------------------------------------------");
            for(int tIdx = 1, tCount = m_StackTrace.FrameCount; tIdx < tCount; ++tIdx)
            {
                StackFrame tFrame = m_StackTrace.GetFrame(tIdx);
                writer.WriteLine(string.Format(" [{0}]  {1}  {2}", tIdx, tFrame.GetFileName(), tFrame.GetFileLineNumber()));
                tStackDepth++;
                if(tStackDepth > 9)
                    break;
            }
            writer.WriteLine("------------------------------------------------------------------");
            writer.WriteLine("      ");

            string stackTraceKey = writer.ToString();
            int times = 0;
            if(stackMap.TryGetValue(stackTraceKey, out times))
            {
                times++;
                stackMap[stackTraceKey] = times;
            }
            else
            {
                stackMap.Add(stackTraceKey, 1);
            }
            
        }

        public void PrintToString(StringWriter writer)
        {
            int tStackDepth = 0;
            tStackDepth = 0;
            writer.WriteLine("------------------------------------------------------------------");
            for(int tIdx = 1, tCount = m_StackTrace.FrameCount; tIdx < tCount; ++tIdx)
            {
                StackFrame tFrame = m_StackTrace.GetFrame(tIdx);
                writer.WriteLine(string.Format(" [{0}]  {1}  {2}", tIdx, tFrame.GetFileName(), tFrame.GetFileLineNumber()));
                tStackDepth++;
                if(tStackDepth > 9)
                    break;
            }
            writer.WriteLine("------------------------------------------------------------------");
            writer.WriteLine("      ");
        }



        public void PrintStack()
        {
            int tStackDepth = 0;
            UnityEngine.Debug.LogError("----------------");
            tStackDepth = 0;
            for (int tIdx = 0, tCount = m_StackTrace.FrameCount; tIdx < tCount; ++tIdx)
            {
                StackFrame tFrame = m_StackTrace.GetFrame(tIdx);
                UnityEngine.Debug.LogError(string.Format("Dispose leaks: [{0}]  {1}  {2}", tIdx, tFrame.GetFileName(), tFrame.GetFileLineNumber()));
                tStackDepth++;
                if (tStackDepth > 9)
                    break;
            }
        }
    }



     public class DisposeSentinelManager
    {
        private static DisposeSentinelManager m_Instance;
        public static DisposeSentinelManager Instance
        {
            get
            {
                if (m_Instance == null)
                {
                    m_Instance = new DisposeSentinelManager();
                    m_Instance.Init();
                }
                   
                return m_Instance;
            }
        }

        StreamWriter m_fileWriter = null;
        StringWriter m_stringWriter = new StringWriter();
        string       m_filePath;
        Dictionary<string ,int> m_stackTraceMap = new Dictionary<string, int>(100);
       
        public void Init()
        {
            RuntimePlatform platform = Application.platform;
            string strDirPath = GetPath(platform);
            if(!Directory.Exists(strDirPath))
            {
                Directory.CreateDirectory(strDirPath);
            }
            m_filePath = strDirPath + "/leaks.txt";
            
             m_stringWriter = new StringWriter();
            try
            {
                m_fileWriter = new StreamWriter(m_filePath, false);
            }
            catch
            {
                UnityEngine.Debug.LogError("log stream writer create failed!");
            }
        }

        
        List<DisposeSentinel> m_DisposeClassList = new List<DisposeSentinel>(512);

        private string GetPath(RuntimePlatform platform)
        {
            string filePath = string.Empty;
            switch(platform)
            {
#if UNITY_EDITOR
                case RuntimePlatform.WindowsEditor:
                case RuntimePlatform.OSXEditor:
                    // Environment.CurrentDirectory: "C:\Users\jeluo\AppData\LocalLow\DefaultCompany\DemoApp"
                    filePath = Environment.CurrentDirectory + "/logs/";
                    break;
#endif
                case RuntimePlatform.WindowsPlayer:
                case RuntimePlatform.Android:
                case RuntimePlatform.IPhonePlayer:
                    // public directory on the device
                    filePath = Application.persistentDataPath;
                    break;
            }
            return filePath;
        }

        public void DoDestroy()
        {
            if(null != m_stringWriter)
            { 
                m_stringWriter.Close();
            }
            if(null != m_fileWriter)
            {
                m_fileWriter.Close();
            }
               
            m_stringWriter = null;
            m_fileWriter = null;
        }

        public void PrintDesc(string desc)
        {
            m_stringWriter.WriteLine(desc);
        }

        /// <summary>
        /// 写入每一个记录到的堆栈到string流，不管是否是一样
        /// </summary>
        public void PrintToString(DisposeSentinel sentinel = null)
        {  
            if(sentinel != null)
            {
                sentinel.PrintToString(m_stringWriter);
            }
            else
            {
                for(int i = 0; i < m_DisposeClassList.Count; ++i)
                {
                    m_DisposeClassList[i].PrintToString(m_stringWriter);
                }
            }
           
        }

        /// <summary>
        /// 写入每一个记录到的堆栈到文件，不管是否是一样
        /// </summary>
        public void WriteToFile()
        {
            if(null != m_fileWriter)
            {
                m_fileWriter.Write(m_stringWriter.ToString());
                m_fileWriter.Flush();
            }
            m_stringWriter.GetStringBuilder().Clear();
        }

        /// <summary>
        /// 写入每一个记录到的堆栈，不管是否是一样
        /// </summary>
        public void PrintLeaks()
        {
            PrintToString();
            WriteToFile();
        }

        public void Clear()
        {
            m_stackTraceMap.Clear();
        }


        /// <summary>
        /// 将统计的堆栈按数量统计，相同的堆栈将只写入一次，并计入数量
        /// </summary>
        public void PrintLeaksToStackTraceMap(DisposeSentinel sentinel = null)
        {
            if(sentinel != null)
            {             
                sentinel.PrintToString(m_stringWriter, m_stackTraceMap);
            }
        }

        /// <summary>
        /// 将统计的堆栈按数量统计，相同的堆栈将只写入一次，并写入数量
        /// </summary>
        public void WriteStackTraceMapToFile()
        {
            if(null == m_fileWriter)
                m_fileWriter = new StreamWriter(m_filePath, false);
            if(null != m_fileWriter)
            {
                var stackTraceMapSort = from objDic in m_stackTraceMap orderby objDic.Value descending select objDic;
                 //Dictionary<string, int> stackTraceMapSort = m_stackTraceMap.OrderByDescending(o => o.Value).ToDictionary(p => p.Key, o => o.Value);
                foreach(var info in stackTraceMapSort)
                {
                
                    m_fileWriter.Write("堆栈进入数量：" + info.Value.ToString() + "次\n");
                    m_fileWriter.Write(info.Key);
                    
                }
                m_fileWriter.Flush();
                m_fileWriter.Close();
                m_fileWriter = null;
            }
        }

        public void AddDisposeSentinel(DisposeSentinel _Sentinel)
        {
            m_DisposeClassList.Add(_Sentinel);
            PrintToString(_Sentinel);
        }



        public void RemoveDisposeSentinel(DisposeSentinel _Sentinel)
        {
            m_DisposeClassList.Remove(_Sentinel);
        }


        /// <summary>
        /// print stacktrace to console log
        /// </summary>
        public void PrintStack()
        {
            for (int tIdx = 0, tCount = m_DisposeClassList.Count; tIdx < tCount; ++tIdx)
                m_DisposeClassList[tIdx].PrintStack();
        }

    }

    class stDectectedInfo
    {
        public DisposeSentinel disposeSentinel;
        public int count;
    }

    public class DetectedLeaks<T> where T : class
    {
       
        Dictionary<T, stDectectedInfo> m_disposeSentinelList = new Dictionary<T, stDectectedInfo>(200);

        public void AddToDectectedList(T action)
        {
            stDectectedInfo info = null;
            if(m_disposeSentinelList.TryGetValue(action, out info))
            {
                info.count++;
            }
            else
            {
                info = new stDectectedInfo();
                info.disposeSentinel = new DisposeSentinel();
                info.count = 1;
                m_disposeSentinelList.Add(action, info);
            }
        }

        public void RemoveFromDecectedList(T action)
        {
            stDectectedInfo info;
            if(m_disposeSentinelList.TryGetValue(action, out info))
            {
                if(info.count <= 1)
                    m_disposeSentinelList.Remove(action);
                else
                    info.count--;
            }
        }

        public void PrintLeaksToFile()
        {
            DisposeSentinelManager.Instance.Clear();
            foreach(var info in m_disposeSentinelList)
            {
                DisposeSentinelManager.Instance.PrintLeaksToStackTraceMap(info.Value.disposeSentinel);
            }
            DisposeSentinelManager.Instance.WriteStackTraceMapToFile();
        }

        public void DoDestroy()
        {
            DisposeSentinelManager.Instance.DoDestroy();
        }
    }

    public class ISingltonDetectedLeaks<T> where T : class
    {
        static protected ISingltonDetectedLeaks<T> m_instance;

        public static ISingltonDetectedLeaks<T> Instance
        {
            get
            {
                if(m_instance == null)
                    m_instance = new ISingltonDetectedLeaks<T>();
                return m_instance;
            }
        }

        protected DetectedLeaks<T> m_detected = new DetectedLeaks<T>();
        public bool m_printLeaks = false;
        public void PrintLeaks()
        {
            m_detected.PrintLeaksToFile();
        }

        public void AddToDectectedList(T com)
        {
            m_detected.AddToDectectedList(com);
        }

        public void RemoveFromDecectedList(T com)
        {
            m_detected.RemoveFromDecectedList(com);
        }

        public void DoDestroy()
        {
            m_detected.DoDestroy();
        }

        public bool IsPrintLeaks
        {
            get { return m_printLeaks; }
            set { m_printLeaks = value; }
        }
    }


}
#endif
#endif