/********************************************************************
	created:	2017/11/6
	author:		LuoJiEn
	purpose:    Provides common utilities.
*********************************************************************/
using UnityEngine;
using System.Text;
using System;
using System.Text.RegularExpressions;
using System.Runtime.CompilerServices;
using System.Collections.Generic;

namespace Giant.Common
{
    // This class provides all utility functions
    public sealed class GiantUtilities
    {
        /// <summary>
        /// 是否是一个非法（C++的内存被释放,但C#的内存还在）的UnityEngine.Object
        /// </summary>
        public static bool IsInvalidUnityObject(object _Target)
        {
            return null != _Target && _Target is UnityEngine.Object && (_Target as UnityEngine.Object).Equals(null);
        }



        /// Provides all utility functions on string concatenation.
        /// global string builder, how to use it
        /// a. m_stringBuilder.Length = 0;     					   	// to clear previous string operation result
        /// b. m_stringBuilder.Append/m_stringBuilder.AppendFormat   // try to use Append as much as possible, since more efficient
        /// c. m_stringBuilder.ToString ();
        #region GLOBAL_STRING_BUILDER
        /// global string builder, to improve both memory efficiency and CPU efficiency.
        // (initially to allow 1024 characters, later on, if we need more, the string builder will automatically expand the internal buffer.)
        static private StringBuilder s_stringBuilder = new StringBuilder(1024 * 4);

        static public StringBuilder GetStringBuilder()
        {
            return s_stringBuilder;
        }

        /// Concatenate multiple strings together, and return the result.
        /// <param name="args">       all substrings to concatenate.</param>
        /// <param name="appendLine"> whether to append a return line between all given substrings.</param>
        /// <returns>                 the resulting concatenated string.</returns>
        static public string BuildString(string[] args, bool appendLine)
        {
            lock (s_stringBuilder)
            {
                s_stringBuilder.Length = 0;
                for (int i = 0; i < args.Length; i++)
                {
                    s_stringBuilder.Append(args[i]);
                    if (true == appendLine) s_stringBuilder.AppendLine();
                }
                return s_stringBuilder.ToString();
            }
        }

        /// Concatenate multiple strings together, and return the result.
		/// <param name="args">       all substrings to concatenate.</param>
		/// <returns>                 the resulting concatenated string.</returns>
        static public string BuildString(params string[] args)
        {
            return BuildString(false, args);
        }

        static public string BuildString(params object[] args)
        {
            return BuildString(false, args);
        }

        static public string BuildString(string arg1)
        {
            return BuildString(false, arg1);
        }
        static public string BuildString(string arg1, string arg2)
        {
            return BuildString(false, arg1, arg2);
        }
        static public string BuildString(string arg1, string arg2, string arg3)
        {
            return BuildString(false, arg1, arg2, arg3);
        }
        static public string BuildString(string arg1, string arg2, string arg3, string arg4)
        {
            return BuildString(false, arg1, arg2, arg3, arg4);
        }


        /// Concatenate multiple strings together, and return the result.
        /// <param name="appendLine"> whether to append a return line between all given substrings.</param>
        /// <param name="args">       all substrings to concatenate.</param>
        /// <returns>                 the resulting concatenated string.</returns>
        static public string BuildString(bool appendLine, params string[] args)
        {
            return BuildString(appendLine, (object[])args);
        }

        static public string BuildString(bool appendLine, params object[] args)
        {
            lock (s_stringBuilder)
            {
                s_stringBuilder.Length = 0;
                for (int i = 0; i < args.Length; i++)
                {
                    s_stringBuilder.Append(args[i]);
                    s_stringBuilder.Append(" ");
                    if (true == appendLine) s_stringBuilder.AppendLine();
                }
                return s_stringBuilder.ToString();
            }
        }

        static public string BuildString(bool appendLine, string arg1)
        {
            return appendLine ? BuildString(false, arg1, "\n") : arg1;
        }
        static public string BuildString(bool appendLine, string arg1, string arg2)
        {
            lock (s_stringBuilder)
            {
                s_stringBuilder.Length = 0;
                s_stringBuilder.Append(arg1);
                if (true == appendLine) s_stringBuilder.AppendLine();
                s_stringBuilder.Append(arg2);
                if (true == appendLine) s_stringBuilder.AppendLine();
                return s_stringBuilder.ToString();
            }

        }
        static public string BuildString(bool appendLine, string arg1, string arg2, string arg3)
        {
            lock (s_stringBuilder)
            {
                s_stringBuilder.Length = 0;
                s_stringBuilder.Append(arg1);
                if (true == appendLine) s_stringBuilder.AppendLine();
                s_stringBuilder.Append(arg2);
                if (true == appendLine) s_stringBuilder.AppendLine();
                s_stringBuilder.Append(arg3);
                if (true == appendLine) s_stringBuilder.AppendLine();
                return s_stringBuilder.ToString();
            }
        }
        static public string BuildString(bool appendLine, string arg1, string arg2, string arg3, string arg4)
        {
            lock (s_stringBuilder)
            {
                s_stringBuilder.Length = 0;
                s_stringBuilder.Append(arg1);
                if (true == appendLine) s_stringBuilder.AppendLine();
                s_stringBuilder.Append(arg2);
                if (true == appendLine) s_stringBuilder.AppendLine();
                s_stringBuilder.Append(arg3);
                if (true == appendLine) s_stringBuilder.AppendLine();
                s_stringBuilder.Append(arg4);
                if (true == appendLine) s_stringBuilder.AppendLine();
                return s_stringBuilder.ToString();
            }
        }

        /// Ten times faster than the String.Equals function with two string compare
        /// <returns>             the resulting two strings equals.</returns>
        static public bool StringEquals(string str1, string str2)
        {
            return string.Equals(str1, str2, StringComparison.Ordinal);
        }

        /// Ten times faster than the String.Equals function with two string compare
        /// IgnoreCase
        /// <returns>             the resulting two strings equals.</returns>
        static public bool StringEqualsIgnoreCase(string str1, string str2)
        {
            return string.Equals(str1, str2, StringComparison.OrdinalIgnoreCase);
        }

        /// 20 times faster than the String.StartsWith function with two string compare
        /// <returns>             the resulting two strings equals.</returns>
        static public bool StringStartsWith(string a, string b)
        {
            int aLen = a.Length;
            int bLen = b.Length;
            int ap = 0;
            int bp = 0;

            while (ap < aLen && bp < bLen && a[ap] == b[bp])
            {
                ap++;
                bp++;
            }

            return (bp == bLen && aLen >= bLen) || (ap == aLen && bLen >= aLen);
        }

        /// 100 times faster than the String.EndsWith function with two string compare
        /// <returns>             the resulting two strings equals.</returns>
        static public bool StringEndsWith(string a, string b)
        {
            int ap = a.Length - 1;
            int bp = b.Length - 1;

            while (ap >= 0 && bp >= 0 && a[ap] == b[bp])
            {
                ap--;
                bp--;
            }
            return (bp < 0 && a.Length >= b.Length) || (ap < 0 && b.Length >= a.Length);
        }

        /// low gc generated to use this function, compare with Regex.Match
        static public Match RegexMatch(string strRegExp, string strMatch)
        {
            var myRegExp = new Regex(strRegExp);
            return myRegExp.Match(strMatch);
        }

        /// Build a formated string.
        /// <param name="format"> string format.</param>
        /// <param name="args">   all substrings to concatenate.</param>
        /// <returns>             the resulting concatenated string.</returns>
        static public string BuildFormatString(string format, params object[] args)
        {
            s_stringBuilder.Length = 0;
            s_stringBuilder.AppendFormat(format, args);
            return s_stringBuilder.ToString();
        }

        #endregion GLOBAL_STRING_BUILDER

        #region Profiler
        [MethodImplAttribute(MethodImplOptions.AggressiveInlining)]
        static public void ProfilerBegin(string desc)
        {
#if !RELEASE || UNITY_EDITOR
            UnityEngine.Profiling.Profiler.BeginSample(desc);
#endif
        }


        [MethodImplAttribute(MethodImplOptions.AggressiveInlining)]
        static public void ProfilerEnd()
        {
#if !RELEASE || UNITY_EDITOR
            UnityEngine.Profiling.Profiler.EndSample();
#endif
        }
        #endregion

        static public void DoGarbageCollection()
        {
            System.GC.Collect();
            Res.AssetsMgr.UnloadUnusedAssets();
        }

    }

    #region Extension
    public struct DampingValue
    {
        float m_dest;
        float m_p;
        float m_cur;
        public DampingValue(float v, float p)
        {
            m_p = p;
            m_cur = v;
            m_dest = v;
        }
        public float Set(float dest)
        {
            m_dest = dest;
            return Tick();
        }
        public float Tick()
        {
            var d = (m_dest - m_cur);
            if (Mathf.Abs(d) > m_p * 0.03f)
                m_cur += d * m_p;
            else
                m_cur = m_dest;
            return m_cur;
        }
        public bool Finish()
        {
            if (Mathf.Abs(m_dest - m_cur) < 0.0001f)
                return true;
            return false;
        }
        public float RawSet(float dest)
        {
            m_cur = dest;
            return m_cur;
        }
    }
    public struct LerpValue
    {
        float m_dest;
        float m_v;
        float m_cur;
        public LerpValue(float from, float to, float v)
        {
            m_v = v;
            m_cur = from;
            m_dest = to;

        }
        public float Tick()
        {
            var d = (m_dest - m_cur);
            if (Mathf.Abs(d) > m_v)
                m_cur += Mathf.Sign(d) * m_v;
            else
                m_cur = m_dest;
            return m_cur;
        }
        public bool Finish()
        {
            if (Mathf.Abs(m_dest - m_cur) < 0.0001f)
                return true;
            return false;
        }
        public float Set(float dest)
        {
            m_dest = dest;
            return Tick();
        }
    }
    #endregion


    public class ProcessTickTimer : IDisposable
    {
        public static ObjectPool<ProcessTickTimer> Pool = new ObjectPool<ProcessTickTimer>();

        protected static Dictionary<int, float> statistics = new Dictionary<int, float>();

        protected string _desc;
        protected long _start;
        protected int _statistics = 0;
        public ProcessTickTimer() { _start = System.DateTime.Now.Ticks; }
        public ProcessTickTimer(string desc)
        {
            _desc = desc;
            _start = System.DateTime.Now.Ticks;
        }

        public static ProcessTickTimer Get(string desc, int statistics = 0)
        {
            var timer = Pool.Get();
            timer._desc = desc;
            timer._start = System.DateTime.Now.Ticks;
            timer._statistics = statistics;
            return timer;
        }
        public void Dispose()
        {
            var delta = (System.DateTime.Now.Ticks - _start) / 1E+07f;
            if (_statistics != 0)
            {
                float total = 0;
                if (!statistics.TryGetValue(_statistics, out total))
                    statistics.Add(_statistics, delta);
                else
                    statistics[_statistics] = total + delta;
                ILogSystem.Log(LogCategory.Profile, String.Format("{0} duration:{1}s total:{2}", _desc, delta, total + delta), LogType.Log);
            }
            else
                ILogSystem.Log(LogCategory.Profile, String.Format("{0} duration:{1}s ", _desc, delta), LogType.Log);
            Pool.Release(this);
        }
    }
}