using UnityEngine;
using System;
using System.Text;
using System.Collections.Generic;
using System.Linq;
using System.Diagnostics;
using System.Security.Cryptography;
using System.Runtime.Serialization.Formatters.Binary;
using System.IO;
using System.Text.RegularExpressions;
using System.Reflection;
using Random = UnityEngine.Random;
using UnityEngine.AI;




#if UNITY_EDITOR
using UnityEditor;
#endif

public delegate void VoidDelegate();

public static partial class DSUtil
{
    // --------------------------------------------------------------------
    // 로그 관련 함수
    // String객체
    private static StringBuilder m_strBuilder = new StringBuilder(1024);
    public static StringBuilder DSString
    {
        get
        {
            m_strBuilder.Remove(0, m_strBuilder.Length);
            return m_strBuilder;
        }
    }

    // String조립
    public static string Format(string strMessage, params object[] args)
    {
        StringBuilder strBuilder = DSString;
        strBuilder.AppendFormat(strMessage, args);
        return strBuilder.ToString();
    }
    // 로그 : 에러
    public static void LogError(string strMessage, params object[] args)
    {
        Verify(false, Format(strMessage, args));
    }

    // 로그 : 워닝
    public static void LogWarning(string strMessage, params object[] args)
    {
#if UNITY_EDITOR
        UnityEngine.Debug.LogWarning(Format(strMessage, args));
#endif
    }

    // 로그 : 일반
    public static void Log(string strMessage, params object[] args)
    {
#if UNITY_EDITOR
        UnityEngine.Debug.Log(Format(strMessage, args));
#endif
    }

    // 서버로그 : 에러값
    public static void LogErrorToServer(int iErrorCode, string strMessage = "")
    {
        string strErrorMessage = string.Empty;
        switch (iErrorCode)
        {
            // MAP_CONST[5] = "Http 프로토콜 에러";
            // TimeOut 도 포함.
            case 5:
                //strErrorMessage = "Timed Out.. Please Retry..";
                strErrorMessage = "시간이 초과되었습니다. 다시 시도해주세요.";
                break;

            default:
                break;
        }

        // 메시지가 있으면 덮어 씀.
        if (false == string.IsNullOrEmpty(strMessage))
        {
            strErrorMessage = strMessage;
        }

        DSUtil.LogError(strErrorMessage);
    }

    // 예외 : try-Catch문에서 예외전달메시지
    public static System.ArgumentException GetExceptionMsg(string strMessage, params object[] args)
    {
        return new System.ArgumentException(Format(strMessage, args));
    }

    // 예외 : 에러로그와 리턴값을 하나로 묶음( EX : return ErrorReturn<bool>(false, "{0} 예외야!!", "나쁜"); )
    public static T ErrorReturn<T>(T tReturnValue, string strMessage, params object[] args)
    {
        LogError(strMessage, args);
        return tReturnValue;
    }

    // 정보 : 콜스택을 얻습니다.
    public static StackFrame[] GetCallStack()
    {
        StackTrace stackTrace = new StackTrace();
        return stackTrace.GetFrames();
    }


    // --------------------------------------------------------------------
    // 형 변환 관련
    // 형변환 : String을 Enum으로
    // Enum.Parse 엄청느립니다. 가급적 사용금지!!!
    public static T FormatStringToEnum<T>(string _strTypeName, params object[] args)
    {
        return StringToEnum<T>(Format(_strTypeName, args));
    }
    public static T StringToEnum<T>(string _strTypeName)
    {
        _strTypeName = _strTypeName.Replace(" ", "");
        if (true == string.IsNullOrEmpty(_strTypeName) || false == Enum.IsDefined(typeof(T), _strTypeName))
        {
            LogWarning("[Not Defined " + typeof(T) + " ] " + _strTypeName);
            return default(T);
        }

        return (T)Enum.Parse(typeof(T), _strTypeName);
    }

    public static T CheerSoundStringToEnum<T>(string _strTypeName)
    {
        _strTypeName = _strTypeName.Replace(" ", "");
        if (true == string.IsNullOrEmpty(_strTypeName) || false == Enum.IsDefined(typeof(T), _strTypeName))
            return default(T);

        return (T)Enum.Parse(typeof(T), _strTypeName);
    }

    // 형변환 : String을 Enum으로
    // Enum.Parse 엄청느립니다. 가급적 사용금지!!!
    public static T StringToEnum<T>(string strEnum, string strErrorLog)
    {
        strEnum = strEnum.Replace(" ", "");
        if (true == string.IsNullOrEmpty(strEnum) || false == Enum.IsDefined(typeof(T), strEnum))
        {
            LogError("{0}(Enum:{1})", strErrorLog, strEnum);
        }

        // 없는 Enum이면 크래시되도록 놔두자!!
        return (T)Enum.Parse(typeof(T), strEnum);
    }

    // 형변환 : Object을 String으로
    public static string ObjectToString(System.Object _cObject)
    {
        if (_cObject == null)
            return null;

        var binaryFormatter = new BinaryFormatter();
        var memoryStream = new MemoryStream();
        binaryFormatter.Serialize(memoryStream, _cObject);

        return Convert.ToBase64String(memoryStream.GetBuffer());
    }

    // 형변환 : String을 Object로
    public static System.Object StringToObject(string _strValue)
    {
        if (true == string.IsNullOrEmpty(_strValue))
            return null;

        var binaryFormatter = new BinaryFormatter();
        var memoryStream = new MemoryStream(Convert.FromBase64String(_strValue));
        System.Object obj = binaryFormatter.Deserialize(memoryStream);

        return obj;
    }


    // --------------------------------------------------------------------
    // 컨테이너 관련
    // Foreach : Enum
    public static void ForeachToEnum<T>(Action<T> pLambda)
    {
        foreach (T eEnum in Enum.GetValues(typeof(T)))
            pLambda(eEnum);
    }

    // Foreach : List
    public static void ForeachToList<T>(List<T> list, Action<T> pLambda)
    {
        if (null == list)
            return;

        foreach (T tList in list)
            pLambda(tList);
    }

    // Foreach : List
    public static void ForeachToListOfBreak<T>(List<T> list, bool bBreakCondition, Func<T, bool> pLambda)
    {
        if (null == list)
            return;

        foreach (T tList in list)
        {
            if (bBreakCondition == pLambda(tList))
                break;
        }
    }

    // Foreach Dictionary
    public static void ForeachToDic<TKey, TValue>(Dictionary<TKey, TValue> dic, Action<TKey, TValue> pLambda)
    {
        if (null == dic)
            return;

        foreach (KeyValuePair<TKey, TValue> kvp in dic)
            pLambda(kvp.Key, kvp.Value);
    }



    //-------------------------------------------------------------------------
    // FirstKey
    // if (.NET 3.5) return dic.Keys.First();
    public static Key FirstKey<Key, Value>(Dictionary<Key, Value> dic, Key defaultVal)
    {
        foreach (var pair in dic)
            return pair.Key;
        return defaultVal;
    }

    // dictionary 램덤키 뽑는다.
    public static Key RandomKey<Key, Value>(Dictionary<Key, Value> dic, Key defaultVal)
    {
        if (null == dic)
            return defaultVal;

        if (0 == dic.Count)
            return defaultVal;

        System.Random rand = new System.Random();
        var listKey = dic.ToList()[rand.Next(0, dic.Count)];
        return listKey.Key;
    }

    //-------------------------------------------------------------------------
    // test_code
    public static void TestRandomTrue()
    {
        List<bool> list = new List<bool>();
        var p = 0.3f;
        for (int i = 0; i < 1000; i++)
            list.Add(DSUtil.RandomTrue(p));

        var countTrue = list.Count(e => e == true);
        var countTotal = list.Count();
        var ratio = (float)countTrue / (float)countTotal;
        Log("  RandomTrue(" + p + "); " +
            "result ratio [" + ratio + "], " +
            "true count [" + countTrue + "], " +
            "total  count [" + countTotal + "]");
    }


    //-------------------------------------------------------------------------
    // RandomTrue 
    // 확률 P% 이하로 true 리턴. (단, 100% == 1.0f)
    public static bool RandomTrue(float p)
    {
        // 유니티 랜덤함수 직접 사용[blueasa / 2014-10-30]
        return p > UnityEngine.Random.Range(0f, 1f);
    }

    public static bool RandomFalse(float p)
    {
        // 유니티 랜덤함수 직접 사용[blueasa / 2014-10-30]
        return RandomTrue(1.0f - p);
    }

    public static bool RandomTrue(double p)
    {
        // 유니티 랜덤함수 직접 사용[blueasa / 2014-10-30]
        return p > UnityEngine.Random.Range(0f, 1f);
    }


    //-------------------------------------------------------------------------
    // RandomW<T> 
    // 가중치에 따라 랜덤하게 원소 선택 
    public static T RandomW<T>(List<T> values, List<float> weights)
    {
        if ((null == values) || (0 == values.Count))
            return default(T);

        if ((null == weights) || (0 == weights.Count))
            return RandomN(values);

        // ex: weights = { 0.1, 0.3, 0.6};

        // assert(weight.Sum > 0);
        var subsums = new List<float>(weights.Count);

        var sum = weights.Aggregate(0.0f, (acc, f) =>
                                    { acc += f; subsums.Add(acc); return acc; });
        // ex : subsums = {0.1, 0.4, 1.0}, sum = 1.0 

        var r = UnityEngine.Random.Range(0.0f, sum);
        // ex : r = 0.35

        var index = subsums.FindIndex(f => (r < f));
        // ex : index = 1 

        // 2017. 08. 29. 정인호. 크래쉬 리포트(ArgumentOutOfRangeException: Argument is out of range.)에 의해 로그 추가
        if (values.Count <= index || index < 0)
        {
            string valuesvalue = "";
            for (int iLoop = 0; iLoop < values.Count; ++iLoop)
                valuesvalue += values[iLoop] + ", ";
            string weightsvalue = "";
            for (int iLoop = 0; iLoop < weights.Count; ++iLoop)
                weightsvalue += weights[iLoop] + ", ";
            Assert(false, Format("DSUtil RandomW Error : values : {0}, weights : {1}, index : {2}, ", valuesvalue, weightsvalue, index));
        }

        return values[index];
    }


    //-------------------------------------------------------------------------
    // RandomOneW<T> 
    // 가중치에 따라 랜덤하게 원소 선택 ( 무조건 하나는 선택되도록 )
    // 가중치를 정규화해서 합이 1인 값으로 만들고, 의미없는 원소는 제거하여 RandomW처리
    public static T RandomOneW<T>(List<T> values, List<float> weights)
    {
        if ((null == values) || (0 == values.Count))
            return default(T);

        if ((null == weights) || (0 == weights.Count))
            return RandomN(values);

        // 전체 가중치 합
        float fSum = 0.0f;
        foreach (float fValue in weights)
            fSum += fValue;

        // 예외처리 : 가중치 합이 0이면 선택할 원소가 없다...
        if (0.0f == fSum)
            return default(T);

        // 데이터 복사용 변수
        List<T> CopyValues = new List<T>();
        List<float> CopyWeights = new List<float>();

        // 가중치 정규화하면서 필요없는 원소는 제거
        for (int iLoop = 0; iLoop < weights.Count; ++iLoop)
        {
            if (0.0f == weights[iLoop])
                continue;

            CopyValues.Add(values[iLoop]);
            CopyWeights.Add(weights[iLoop] / fSum);
        }

        return RandomW(CopyValues, CopyWeights);
    }


    //-------------------------------------------------------------------------
    // RandomN<T>
    // RandomW<T> 와 스팩은 동일하나, 모든 원소의 가중치가 동일한 경우로 제한함. 
    public static T RandomN<T>(List<T> values)
    {
        if ((null == values) || (0 == values.Count))
            return default(T);

        var index = (int)UnityEngine.Random.Range(0.0f, values.Count);
        if (index == values.Count)
            index = values.Count - 1;
        return values[index];
    }

    //-------------------------------------------------------------------------
    // RandomRange
    // int일 경우, Max값도 나오도록 하기 위해 Max + 1을 함.
    public static int RandomRange(int _iMin, int _iMax)
    {
        if (_iMin == _iMax) return _iMin;
        return UnityEngine.Random.Range(_iMin, _iMax + 1);
    }
    public static float RandomRange(float _fMin, float _fMax)
    {
        if (_fMin == _fMax) return _fMin;
        return UnityEngine.Random.Range(_fMin, _fMax);
    }
    public static Vector3 RandomVector(float fMin, float fMax)
    {
        return RandomVector(fMin, fMax, fMin, fMax, fMin, fMax);
    }
    public static Vector3 RandomVector(float fMinX, float fMaxX, float fMinY, float fMaxY, float fMinZ, float fMaxZ)
    {
        return new Vector3(RandomRange(fMinX, fMaxX), RandomRange(fMinY, fMaxY), RandomRange(fMinZ, fMaxZ));
    }
    public static Vector3 RandomVector_Exception_y(float minX, float maxX, float y, float minZ, float maxZ)
    {
        return new Vector3(RandomRange(minX, maxX), y, RandomRange(minZ, maxZ));
    }

    //-------------------------------------------------------------------------
    // 
    public static List<int> RandomIndex(int _iCount)
    {
        List<int> listResult = new List<int>();
        List<int> listCount = new List<int>();

        for (int i = 0; i < _iCount; i++)
            listCount.Add(i);

        while (0 < listCount.Count)
        {
            int iRand = UnityEngine.Random.Range(0, listCount.Count);
            listResult.Add(listCount[iRand]);
            listCount.RemoveAt(iRand);
        }

        return listResult;
    }

    //========================================================================
    // 폴링 함수 getter 를 이용해 이벤트 드리븐 함수 onChange 를 호출 
    // prev - 이전값
    // getter - 폴링함수.
    // onChange := Action<PrevValue, CurrentValue> - 이벤트 함수.
    public static void PollingToEvent(float prev, Func<float> getter, Action<float, float> onChange)
    {
        // 값이 변했는지 검사. 다를 때만 호출 
        var current = getter();
        if (prev != current)
            onChange(prev, current);
    }


    /// <summary>
    /// GUID
    /// </summary>
    /// <returns></returns>
    public static string GetGUID()
    {
        System.Guid uid = System.Guid.NewGuid();
        return uid.ToString();
    }

    /// <summary>
    /// UUID(이걸 사용)
    /// </summary>
    /// <returns></returns>
    public static string GetUUID()
    {
        return SystemInfo.deviceUniqueIdentifier;
    }

    // 프리팹에 Component가 Missing된 것이 있는지 체크
    public static void CheckMissingComponent()
    {
        UnityEngine.Object[] pObjects = Resources.FindObjectsOfTypeAll(typeof(GameObject));
        foreach (UnityEngine.Object pObject in pObjects)
        {
            GameObject pGameObject = pObject as GameObject;
            if (null == pGameObject)
                continue;

            Component[] pComponents = pGameObject.GetComponents<Component>();
            foreach (Component pComponent in pComponents)
            {
                if (null == pComponent)
                    DSUtil.LogError("MissingComponent!!(GameObject{0})", pObject.name);
            }
        }
    }

    // 유니티 에디터의 Pause를 Toggle합니다.
    public static void EditorPauseOfToggle(bool bToggle)
    {
#if UNITY_EDITOR
        EditorApplication.isPaused = bToggle;
#endif
    }

    public static void ContainsDictionary<TKey, TValue>(Dictionary<TKey, TValue> dicContainer, TKey tKey)
    {
        if (false == dicContainer.ContainsKey(tKey))
            dicContainer[tKey] = default(TValue);
    }

    // 클래스 Null체크
    /*
     * null for classes
     * null (empty) for Nullable<T>
     * zero/false/etc for other structs
    */
    public static bool IsNull<T>(T value)
    {
        if (EqualityComparer<T>.Default.Equals(value, default(T)))
            return true;

        return false;
    }

    /*
	*Assert 관련
	*/
    public static void Assert(bool condition)
    {
        //		if (Debug.isDebugBuild && !condition) throw new Exception();
        if (!condition) throw new Exception();
    }

    public static void Assert(bool condition, string log)
    {
        //		if (Debug.isDebugBuild && !condition) {
        //			Debug.Log (log);
        //			throw new Exception ();
        //		}
        if (!condition)
        {
            UnityEngine.Debug.LogError(log);
            throw new Exception();
        }
    }

    public static void Assert(bool condition, string strMessage, params object[] args)
    {
        //		if (Debug.isDebugBuild && !condition) {
        //			Debug.Log (Format (strMessage, args));
        //			throw new Exception ();
        //		}
        if (!condition)
        {
            UnityEngine.Debug.Log(Format(strMessage, args));
            throw new Exception();
        }
    }

    public static void Verify(bool condition)
    {
        if (!condition)
        {
            //EditorPauseOfToggle (true);
        }
    }

    public static void Verify(bool condition, string log)
    {
        if (!condition)
        {
            UnityEngine.Debug.LogError(log);
            //EditorPauseOfToggle (true);
        }
    }

    public static void Verify(bool condition, string strMessage, params object[] args)
    {
        if (!condition)
        {
            UnityEngine.Debug.LogError(Format(strMessage, args));
            //EditorPauseOfToggle (true);
        }
    }

    public static void Search(string strPath, Action<FileInfo> pCallBack)
    {
        DirectoryInfo pDirInfo = new DirectoryInfo(strPath);
        SearchFiles(pDirInfo, pCallBack);
        SearchDirs(pDirInfo, pCallBack);
    }

    static void SearchDirs(DirectoryInfo pDirInfo, Action<FileInfo> pCallBack)
    {
        DirectoryInfo[] pDirs = pDirInfo.GetDirectories();
        foreach (DirectoryInfo pDir in pDirs)
        {
            SearchFiles(pDir, pCallBack);
            SearchDirs(pDir, pCallBack);
        }
    }

    static void SearchFiles(DirectoryInfo pDirInfo, Action<FileInfo> pCallBack)
    {
        FileInfo[] pFiles = pDirInfo.GetFiles();
        foreach (FileInfo pFile in pFiles)
        {
            pCallBack(pFile);
        }
    }

    public static string Encrypt(string toEncrypt, string key = "12345678901234567890123456789012") /*key limit 32byte 12345678901234567890123456789012*/
    {
        byte[] keyArray = UTF8Encoding.UTF8.GetBytes(key);
        byte[] toEncryptArray = UTF8Encoding.UTF8.GetBytes(toEncrypt);
        RijndaelManaged rDel = new RijndaelManaged();
        rDel.Key = keyArray;
        rDel.Mode = CipherMode.ECB;
        rDel.Padding = PaddingMode.PKCS7;
        ICryptoTransform cTransform = rDel.CreateEncryptor();
        byte[] resultArray = cTransform.TransformFinalBlock(toEncryptArray, 0, toEncryptArray.Length);
        return Convert.ToBase64String(resultArray, 0, resultArray.Length);
    }
    public static string Decrypt(string toDecrypt, string key = "12345678901234567890123456789012") /*key limit 32byte 12345678901234567890123456789012*/
    {
        byte[] keyArray = UTF8Encoding.UTF8.GetBytes(key);
        byte[] toEncryptArray = Convert.FromBase64String(toDecrypt);
        RijndaelManaged rDel = new RijndaelManaged();
        rDel.Key = keyArray;
        rDel.Mode = CipherMode.ECB;
        rDel.Padding = PaddingMode.PKCS7;
        ICryptoTransform cTransform = rDel.CreateDecryptor();
        byte[] resultArray = cTransform.TransformFinalBlock(toEncryptArray, 0, toEncryptArray.Length);
        return UTF8Encoding.UTF8.GetString(resultArray);
    }

    public static void Activate(GameObject _goTarget, bool _bActive)
    {
        if (null != _goTarget)
        {
            _goTarget.SetActive(_bActive);
        }
    }

    public static void ActivateGameObjects(GameObject[] _gos, int _inactiveIndex = -1)
    {
        for (int iLoop = 0; iLoop < _gos.Length; ++iLoop)
        {
            if (_gos[iLoop] != null)
                _gos[iLoop].SetActive(true);
        }
        if (_inactiveIndex > -1 && _gos[_inactiveIndex] != null)
            _gos[_inactiveIndex].SetActive(false);
    }

    public static void InActivateGameObjects(GameObject[] _gos, int _activeIndex = -1)
    {
        for (int iLoop = 0; iLoop < _gos.Length; ++iLoop)
        {
            if (_gos[iLoop] != null)
                _gos[iLoop].SetActive(false);
        }
        if (_activeIndex > -1 && _gos[_activeIndex] != null)
            _gos[_activeIndex].SetActive(true);
    }

    public static void ActivateCollider(BoxCollider _collider, bool _bActive)
    {
        if (null != _collider)
        {
            _collider.enabled = _bActive;
        }
    }

    private static Stopwatch m_cStopwatch = new Stopwatch();
    public static void StartStopWatch()
    {
        m_cStopwatch.Reset();
        m_cStopwatch.Start();
    }

    public static TimeSpan StopStopWatch(bool debugtime)
    {
        m_cStopwatch.Stop();
        if (debugtime) Log("{0}", m_cStopwatch.Elapsed);
        return m_cStopwatch.Elapsed;
    }

    public static int GetNumberLen(int iNum)
    {
        return iNum.ToString().Length;
    }

    public static void Quit()
    {
#if UNITY_EDITOR
        EditorApplication.isPlaying = false;
#else
		Application.Quit();
#endif
    }

    /// <summary>
    /// 문자열에서 한글만 얻기
    /// </summary>
    /// <param name="_strText"></param>
    /// <returns></returns>
    public static string GetStringOnlyKorean(string _strText)
    {
        return Regex.Replace(_strText, @"[^가-힣]", "");
    }

    /// <summary>
    /// 문자열에서 영어만 얻기
    /// </summary>
    /// <param name="_strText"></param>
    /// <returns></returns>
    public static string GetStringOnlyEnglish(string _strText)
    {
        return Regex.Replace(_strText, @"[^a-zA-Z]", "");
    }

    /// <summary>
    /// 텍스트의 문자열이 영어 대소문자/숫자/한글인지 체크하는 정규식
    /// </summary>
    /// <param name="_strText">체크할 텍스트</param>
    /// <returns>유효 여부</returns>
    public static bool IsValidString(string _strText)
    {
        string strPattern = @"^[a-zA-Z0-9가-힣]*$";
        return Regex.IsMatch(_strText, strPattern);
    }

    /// <summary>
    /// int -> 세자리마다 콤마가 있는 숫자
    /// </summary>
    public static string GetThousandCommaText(double data, bool _incluePercent = false)
    {
        if (_incluePercent) return Format("{0:#,##0}%", data);
        else                return Format("{0:#,##0}", data);
    }
    public static string GetCommaText_N2(double data)
    {
        return Format("{0:#,##0.##}", data);
    }
    public static string GetCommaText_N4(double data)
    {
        return Format("{0:#,##0.####}", data);
    }

    /// <summary>
    /// 00:00:00 형식
    /// </summary>
    public static string Get_TimeText_HMS(DateTime _dt) { return Format("{0:00}:{1:00}:{2:00}", _dt.Hour, _dt.Minute, _dt.Second); }
    public static string Get_TimeText_HMS(TimeSpan _ts) { return Format("{0:00}:{1:00}:{2:00}", _ts.Hours, _ts.Minutes, _ts.Seconds); }
    public static string Get_TimeText_MS(TimeSpan _ts) { return Format("{0:00}:{1:00}", _ts.Minutes, _ts.Seconds); }
    public static string Get_TimeText_HMS(bool _text, double _totalseconds)
    {
        if (_text)
            return Format("{0:00}시간 {1:00}분 {2:00}초", _totalseconds / 60 / 60, _totalseconds / 60 % 60, _totalseconds % 60);
        return Format("{0:00}:{1:00}:{2:00}", _totalseconds / 60 / 60, _totalseconds / 60 % 60, _totalseconds % 60);
    }
    public static string Get_TimeText_HM(double _totalminutes) { return Format("{0:00}:{1:00}", (int)(_totalminutes / 60), (int)(_totalminutes % 60)); }
    public static string Get_TimeText_MS(double _totalseconds) { return Format("{0:00}:{1:00}", (int)(_totalseconds / 60), (int)(_totalseconds % 60)); }
    public static string Get_TimeText_MSms(double _tick)
    {
        var min = (int)(_tick / 60);
        var sec = (int)(_tick % 60);
        float ms = ((float)_tick - (int)_tick) * 100f;
        return Format("{0:00}분 {1:00}초 {2:00}", min, sec, Mathf.Approximately(ms, 100f) ? 0 : ms);
    }

    public static bool IsUnityObjectDestroyed(object target)
    {
        if ((target is UnityEngine.Object) == false)
            return false;

        if ((target is UnityEngine.Object) && (target.Equals(null)) == true)
            return true;

        return false;
    }
    public static bool IsActionUnityObjectDestroyed(object target, MethodInfo method)
    {
        if (target == null && method == null)
            return true;

        if (IsUnityObjectDestroyed(target))
            return true;

        return false;
    }
    public static bool IsActionUnityObjectDestroyed(Action action)
    {
        if (action == null)
            return true;
        return IsActionUnityObjectDestroyed(action.Target, action.Method);
    }

    public static bool IsActionUnityObjectDestroyed<T>(Action<T> action)
    {
        if (action == null)
            return true;
        return IsActionUnityObjectDestroyed(action.Target, action.Method);
    }

    public static bool IsActionUnityObjectDestroyed<T1, T2>(Action<T1, T2> action)
    {
        if (action == null)
            return true;
        return IsActionUnityObjectDestroyed(action.Target, action.Method);
    }

    public static void Set_Transform_withParent(Transform _tf, Transform _parent, Vector3? _pos = null, Vector3? _scale = null)
    {
        _tf.SetParent(_parent);
        _tf.localPosition = _pos == null ? Vector3.zero : (Vector3)_pos;
        _tf.localScale = _scale == null ? Vector3.one : (Vector3)_scale;
        _tf.localRotation = Quaternion.identity;
    }

    public static GameObject Get_Clone(string _path, Transform _parent = null, Vector3? _pos = null, Vector3? _scale = null)
    {
        var temp = GameObject.Instantiate(Resources.Load(_path)) as GameObject;
        Set_Transform_withParent(temp.transform, _parent, _pos, _scale);
        return temp;
    }
    public static GameObject Get_Clone(GameObject _obj, Transform _parent = null, Vector3? _pos = null, Vector3? _scale = null)
    {
        var temp = GameObject.Instantiate(_obj);
        Set_Transform_withParent(temp.transform, _parent, _pos, _scale);
        return temp;
    }
    public static T Get_Clone<T>(GameObject _obj, Transform _parent = null, Vector3? _pos = null, Vector3? _scale = null)
    {
        var temp = GameObject.Instantiate(_obj);
        Set_Transform_withParent(temp.transform, _parent, _pos, _scale);
        return temp.GetComponent<T>();
    }
    public static T Get_Clone<T>(string _path, Transform _parent = null, Vector3? _pos = null, Vector3? _scale = null)
    {
        var res = Resources.Load(_path);
        if (res == null) UnityEngine.Debug.Log(Format("no exist res : {0}", _path));
        return Get_Clone<T>(res as GameObject, _parent, _pos, _scale);
    }

    public static bool Check_ObjectInMainCamera(Transform _target)
    {
        var screenP = Camera.main.WorldToViewportPoint(_target.position);
        return screenP.z > 0 && screenP.x > 0 && screenP.x < 1 && screenP.y > 0 && screenP.y < 1;
    }

    //public static DateTime Get_NextWeekMonday()
    //{
    //    DateTime NextWeek;
    //    switch (ServerInfo.ServerTime.DayOfWeek)
    //    {
    //        case DayOfWeek.Monday: NextWeek = ServerInfo.ServerTime.AddDays(7); break;
    //        case DayOfWeek.Tuesday: NextWeek = ServerInfo.ServerTime.AddDays(6); break;
    //        case DayOfWeek.Wednesday: NextWeek = ServerInfo.ServerTime.AddDays(5); break;
    //        case DayOfWeek.Thursday: NextWeek = ServerInfo.ServerTime.AddDays(4); break;
    //        case DayOfWeek.Friday: NextWeek = ServerInfo.ServerTime.AddDays(3); break;
    //        case DayOfWeek.Saturday: NextWeek = ServerInfo.ServerTime.AddDays(2); break;
    //        default: NextWeek = ServerInfo.ServerTime.AddDays(1); break;
    //    }
    //    return new DateTime(NextWeek.Year, NextWeek.Month, NextWeek.Day);
    //}

    public static void ChangeLayersRecursively(Transform trans, string name)
    {
        trans.gameObject.layer = LayerMask.NameToLayer(name);
        foreach (Transform child in trans)
        {
            ChangeLayersRecursively(child, name);
        }
    }

    public static Vector3 StringToVector3(string str)
    {
        string[] s = str.Split(',');
        return new Vector3(float.Parse(s[0]), float.Parse(s[1]), float.Parse(s[2]));
    }

    public static int Get_RandomIndex<T>(List<T> probs) where T : struct, IConvertible
    {
        float total = 0f;
        foreach (var p in probs)
            total += Convert.ToSingle(p);

        float randomPoint = Random.value * total;

        for (int i = 0; i < probs.Count; i++)
        {
            float val = Convert.ToSingle(probs[i]);
            if (randomPoint < val)
                return i;
            randomPoint -= val;
        }
        return probs.Count - 1;
    }

    public static Vector3 Get_RandomPos_onNavMesh(Vector3 origin, float minradius = 0f, float maxradius = 3f, int _trycount = 50)
    {
        // Set the parameters for the NavMesh.SamplePosition method
        NavMeshHit hit;
        Vector3 randomPoint = origin;
        int maxTries = _trycount;
        int currentTry = 0;
        minradius = Math.Max(0f, minradius);

        // Try to find a random location on the NavMesh
        while (currentTry < maxTries)
        {
            Vector3 randomDirection = Random.insideUnitSphere * maxradius;
            randomDirection += origin;
            NavMesh.SamplePosition(randomDirection, out hit, maxradius, NavMesh.AllAreas);

            // If a valid location is found, check if it is reachable and far enough
            if (hit.hit && Vector3.Distance(origin, hit.position) >= minradius)
            {
                NavMeshPath path = new NavMeshPath();
                if (NavMesh.CalculatePath(origin, hit.position, NavMesh.AllAreas, path))
                {
                    // If a path can be found, return the random location
                    randomPoint = hit.position;
                    break;
                }
            }
            currentTry++;
        }

        // Return the random reachable location on the NavMesh
        return randomPoint;
    }
    public static bool IsValidPos_onNaveMesh(Vector3 pos, float maxDistance = 0f)
    {
        NavMeshHit hit;
        // maxDistance 안에서 pos에 가장 가까운 NavMesh 위치 찾기
        if (NavMesh.SamplePosition(pos, out hit, maxDistance, NavMesh.AllAreas))
        {
            // 유효한 위치가 있고 너무 멀리 떨어지지 않았으면 true
            return true;
        }
        return false;
    }

    public static bool CheckNull(UnityEngine.Object obj)
    {
        return ReferenceEquals(obj, null) || obj.Equals(null);
    }
    public static bool CheckNull(object obj)
    {
        return ReferenceEquals(obj, null);
    }

    public static float Get_SliderValue(float f) { return float.IsNaN(f) ? 0f : f; }

    public static bool WithInDistance(Vector3 a, Vector3 b, float dist) { return Get_Distance(a, b) <= dist * dist; }
    public static float Get_Distance(Vector3 a, Vector3 b) { return (a - b).sqrMagnitude; }
}

public class NumberFormatter
{
    // 알파벳 시작값 (a = 97 in ASCII)
    private const char startChar = 'a';

    public static string FormatNumber(double number)
    {
        if (number < 1000)
            return number.ToString("0.##"); // 1,000 미만은 그냥 숫자로 반환

        int suffixIndex = 0;
        while (number >= 1000)
        {
            number /= 1000;
            suffixIndex++;
        }

        // 소수점 두 자리까지 표시하고, 알파벳 단위 추가 (suffixIndex에 따라 동적 알파벳 생성)
        char suffix = (char)(startChar + (suffixIndex - 1));
        return $"{number:F2}{suffix}";
    }
}

public struct ErrorString
{
	public string error;
	public ErrorString(string str){error=str;}
};

public class DSTime_HMS
{
    public int m_Hour, m_Min, m_Sec;

    public DSTime_HMS(int _second)
    {
        m_Hour = _second / 3600;
        m_Min = _second % 3600 / 60;
        m_Sec = _second % 3600 % 60;
    }
}