using System;
using System.Threading;
using System.Collections.Concurrent;
using System.Diagnostics;
using System.Runtime.CompilerServices;
using System.Text;
using MrWu.Time;

namespace MrWu.Debug
{
    /// <summary>
    /// 日志系统
    /// </summary>
    public class Debug : IDebug
    {

        private Debug() {

        }

        private static IDebug m_instance;
        private static IDebug instance {
            get {
                if (m_instance == null) {
                    instanceCnt++;
                    m_instance = new Debug();
                }
                return m_instance;
            }
            set {
                m_instance = value;
                instanceCnt++;
            }
        }

        //程序运行时间
        private static long _timevalue;

        /// <summary>
        /// 从运行Start方法开始 记录的程序运行时间秒钟数
        /// </summary>
        public static long timevalue {
            get {
                return Interlocked.Read(ref _timevalue);
            }
        }

        private static Timer timer;

        /// <summary>
        /// 实例数量
        /// </summary>
        private static int instanceCnt;

        static Debug() {
            timer = new Timer(OnTimer, null, 0, 1000);
        }

        /// <summary>
        /// 
        /// </summary>
        ~Debug() {
            instanceCnt--;
            if (instanceCnt <= 0)
                if (timer != null)
                    timer.Dispose();
        }

        private static volatile DebugLevel _logLevel = DebugLevel.Log;

        /// <summary>
        /// 事件
        /// </summary>
        public static event Action<string, DebugLevel> logEvent;

        /// <summary>
        /// 缓存等级
        /// </summary>
        public static DebugLevel logLevel {
            get {
                return _logLevel;
            }
            set {
                _logLevel = value;
                if (LogLevelChange != null)
                    LogLevelChange(_logLevel);
            }
        }

        /// <summary>
        /// 日志显示等级变化事件
        /// </summary>
        public static event Action<DebugLevel> LogLevelChange;

        /// <summary>
        /// 启动
        /// </summary>
        /// <param name="debug">日志接收体</param>
        /// <param name="logLevel">日志等级</param>
        public static void Start(IDebug debug, DebugLevel logLevel = DebugLevel.Log | DebugLevel.Warning | DebugLevel.Error) {
            if (debug != null) {
                instance = debug;
                _logLevel = logLevel;
            }
        }

        private static void OnTimer(object sender) {
            //if (Thread.CurrentThread.Name != "Debug线程") {
            //    Thread.CurrentThread.Name = "Debug线程";
            //    Debug.Log("Debug线程:" + Thread.CurrentThread.IsBackground);
            //}
            //Debug.Log("Debug 定时器!");
            Interlocked.Increment(ref _timevalue);
        }

        /// <summary>
        /// 输出日志
        /// </summary>
        /// <param name="obj">物体</param>
        [MethodImpl(MethodImplOptions.AggressiveInlining)]
        public static void Log(object obj) {
            Print(obj, DebugLevel.Log);
        }

        /// <summary>
        /// 输出日志
        /// </summary>
        /// <param name="format">需要格式的日志</param>
        /// <param name="args"></param>
        [MethodImpl(MethodImplOptions.AggressiveInlining)]
        public static void Log(string format, params object[] args) {
            Print(format, DebugLevel.Log, args);
        }

        /// <summary>
        /// 输出信息
        /// </summary>
        /// <param name="obj"></param>
        [MethodImpl(MethodImplOptions.AggressiveInlining)]
        public static void Info(object obj) {
            Print(obj, DebugLevel.Info);
        }

        /// <summary>
        /// 输出信息
        /// </summary>
        /// <param name="format"></param>
        /// <param name="args"></param>
        [MethodImpl(MethodImplOptions.AggressiveInlining)]
        public static void Info(string format, params object[] args) {
            Print(format, DebugLevel.Info, args);
        }

        /// <summary>
        /// 警告
        /// </summary>
        /// <param name="obj"></param>
        [MethodImpl(MethodImplOptions.AggressiveInlining)]
        public static void Warning(object obj) {
            Print(obj, DebugLevel.Warning);
        }

        /// <summary>
        /// 警告
        /// </summary>
        /// <param name="format"></param>
        /// <param name="args"></param>
        [MethodImpl(MethodImplOptions.AggressiveInlining)]
        public static void Warning(string format, params object[] args) {
            Print(format, DebugLevel.Warning, args);
        }

        /// <summary>
        /// 重要日志
        /// </summary>
        /// <param name="obj">物体信息</param>
        [MethodImpl(MethodImplOptions.AggressiveInlining)]
        public static void ImportantLog(object obj) {
            Print(obj, DebugLevel.ImportantLog);
        }

        /// <summary>
        /// 重要日志
        /// </summary>
        /// <param name="format"></param>
        /// <param name="args"></param>
        [MethodImpl(MethodImplOptions.AggressiveInlining)]
        public static void ImportantLog(string format, params object[] args) {
            Print(format, DebugLevel.ImportantLog, args);
        }

        /// <summary>
        /// 错误
        /// </summary>
        /// <param name="obj"></param>
        [MethodImpl(MethodImplOptions.AggressiveInlining)]
        public static void Error(object obj) {
            Print(obj, DebugLevel.Error);
        }

        /// <summary>
        /// 错误
        /// </summary>
        /// <param name="format"></param>
        /// <param name="args"></param>
        [MethodImpl(MethodImplOptions.AggressiveInlining)]
        public static void Error(string format, params object[] args) {
            Print(format, DebugLevel.Error, args);
        }

        /// <summary>
        /// 致命的错误
        /// </summary>
        /// <param name="obj"></param>
        public static void Fatal(object obj) {
            Print(obj, DebugLevel.Fatal);
        }

        /// <summary>
        /// 致命的错误
        /// </summary>
        /// <param name="format"></param>
        /// <param name="args"></param>
        public static void Fatal(string format, params object[] args) {
            Print(format, DebugLevel.Fatal, args);
        }

        private const string LOGNAME = "LOG";
        private const string INFONAME = "INFO";
        private const string WARNINGNAME = "WRANING";
        private const string IMPORTANTLOGNAME = "IMPORTANTLOG";
        private const string ERRORNAME = "ERROR";
        private const string FATALNAME = "FATAL";

        /// <summary>
        /// 设置消息
        /// </summary>
        /// <param name="msg">消息</param>
        /// <param name="level">等级</param>
        private static void setMsg(StringBuilder msg, DebugLevel level) {

            string GetLevelStr(DebugLevel _level) {
                switch (_level) {
                    case DebugLevel.Log:
                        return LOGNAME;
                    case DebugLevel.Info:
                        return INFONAME;
                    case DebugLevel.Warning:
                        return WARNINGNAME;
                    case DebugLevel.ImportantLog:
                        return IMPORTANTLOGNAME;
                    case DebugLevel.Error:
                        return ERRORNAME;
                    case DebugLevel.Fatal:
                        return FATALNAME;
                    default:
                        return string.Empty;
                }
            }

            msg.Append("[");
            msg.Append(DateTime.Now.TimeToString(TimeStyle.MMddhhmmssfff));
            msg.Append("]");
            msg.Append("[");
            msg.Append(GetLevelStr(level));
            msg.Append("]:");
        }

        private static void Print(object obj, DebugLevel level) {
            if ((logLevel & level) == 0)
                return;
            StringBuilder msg = StringBuilderPool.GetStringBuilder();
            setMsg(msg, level);
            msg.Append(obj?.ToString() ?? "NULL");
            PrintAdd(msg, level);
        }

        private static void Print(string format, DebugLevel level, params object[] args) {
            if ((logLevel & level) == 0)
                return;
            StringBuilder msg = StringBuilderPool.GetStringBuilder();
            setMsg(msg, level);
            if (args.Length > 0)
                msg.Append(string.Format(format, args));
            else
                msg.Append(format);
            
            // #if DEBUG
            // StackTrace stackTrace = new StackTrace();
            // msg.Append(stackTrace.ToString());
            // #endif
            
            PrintAdd(msg, level);
        }

        private static void PrintAdd(StringBuilder msg, DebugLevel level) {
            string logstr = msg.ToString();
            instance.AddLog(logstr, level);
            if (logEvent != null)
                logEvent(logstr, level);
            StringBuilderPool.PutStringBuilder(msg);
        }

        void IDebug.AddLog(string str, DebugLevel level) {
            Console.WriteLine(str);
        }

        /// <summary>
        /// 测量运行时间的池
        /// </summary>
        private readonly static ConcurrentDictionary<long, System.Diagnostics.Stopwatch> sws = new ConcurrentDictionary<long, System.Diagnostics.Stopwatch>();

        /// <summary>
        /// 运行时间id
        /// </summary>
        private static long runtimeid = 0;

        /// <summary>
        /// 测速数量
        /// </summary>
        public int StopwatchCount => sws.Count;
        
        /// <summary>
        /// 程序开始运行计时
        /// </summary>
        /// <returns></returns>
        public static long StartTiming() {
            System.Diagnostics.Stopwatch sw = new System.Diagnostics.Stopwatch();
            long uid = Interlocked.Increment(ref runtimeid);
            sws.TryAdd(uid, sw);
            sw.Start();
            return uid;
        }

        /// <summary>
        /// 获取代码的运行时间
        /// </summary>
        /// <param name="key"></param>
        /// <returns></returns>
        public static double GetRunTime(long key) {
            System.Diagnostics.Stopwatch sw;
            if (sws.TryRemove(key, out sw)) {
                sw.Stop();
                return sw.Elapsed.TotalMilliseconds;
            }
            return 0;
        }

        class StringBuilderPool
        {
            private static ConcurrentQueue<StringBuilder> builds = new ConcurrentQueue<StringBuilder>();

            public static StringBuilder GetStringBuilder() {
                StringBuilder result = null;
                if (builds.TryDequeue(out result))
                    return result;
                result = new StringBuilder();
                return result;
            }

            public static void PutStringBuilder(StringBuilder build) {
                if (build != null) {
                    build.Clear();
                    builds.Enqueue(build);
                }
            }
        }
    }
}
