using System;
using System.Collections;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Reflection;
using System.Security.Cryptography;
using System.Text;
using System.Text.RegularExpressions;
using UnityEngine;
using UnityEngine.UI;
using Object = UnityEngine.Object;
using Random = System.Random;
using System.Net.Sockets;
using System.Net.NetworkInformation;
using UnityEngine.Events;
using System.Threading.Tasks;

namespace Base.Common
{
    public class Util
    {

        public static int UID = 0;
        
        static string[] kSizeSuffix = new string[]
        {
            "B",
            "KB",
            "MB",
            "GB",
            "TB",
            "PB",
        };

        private static StringBuilder sTmpStringBuidler = new StringBuilder();

        private static Dictionary<string, Type> sPredefineComponentTypes = new Dictionary<string, Type>()
        {
        };

        public static void SetUID(int uid)
        {
            UID = uid;
        }
        
        public static string ColorToHex(Color32 color)
        {
            string hex = color.r.ToString("X2") + color.g.ToString("X2") + color.b.ToString("X2");
            return hex;
        }
        
        /// <summary>
        /// HashToMD5Hex
        /// </summary>
        public static string HashToMD5Hex(string sourceStr)
        {
            byte[] Bytes = Encoding.UTF8.GetBytes(sourceStr);
            using (MD5CryptoServiceProvider md5 = new MD5CryptoServiceProvider())
            {
                byte[] result = md5.ComputeHash(Bytes);
                StringBuilder builder = new StringBuilder();
                for (int i = 0; i < result.Length; i++)
                    builder.Append(result[i].ToString("x2"));
                return builder.ToString();
            }
        }

        public static string GetFileMD5(string filePath)
        {
            try
            {
                using (FileStream fs = new FileStream(filePath, FileMode.Open))
                {
                    using (MD5 md5 = new MD5CryptoServiceProvider())
                    {
                        byte[] result = md5.ComputeHash(fs);
                        StringBuilder sb = new StringBuilder();
                        foreach (byte b in result)
                        {
                            sb.Append(b.ToString("x2"));
                        }

                        return sb.ToString();
                    }
                }
            }
            catch (Exception e)
            {
                return string.Empty;
            }
        }

        public static string GetMD5(byte[] bytes)
        {
            using (MD5 md5 = new MD5CryptoServiceProvider())
            {
                byte[] result = md5.ComputeHash(bytes);
                StringBuilder sb = new StringBuilder();
                foreach (byte b in result)
                {
                    sb.Append(b.ToString("x2"));
                }

                return sb.ToString();
            }
        }
        
        public static GameObject LoadGameObject(string path)
        {
            GameObject prefab;
            prefab = Resources.Load<GameObject>(path);
            if (!prefab)
            {
                Debug.LogError("未读取到prefab:" + path);
                return null;
            }
            return prefab;
        }

        public static GameObject Instantiate(string path, Vector3 pos = default, Quaternion rotation = default, Transform parent = null)
        {
            GameObject prefab;
            
            prefab = Resources.Load<GameObject>(path);
            if (!prefab)
            {
                Debug.LogError("未读取到prefab:" + path);
                return null;
            }

            GameObject gameObject = null;
            if (prefab != null)
            {
                gameObject = PoolManager.Instantiate(prefab, pos, rotation, parent);
                gameObject.SetActive(true);
            }
            else
            {
                Debug.LogError("PrefabPathError:" + path);
            }

            return gameObject;
        }

        /// <summary>
        /// 判断物体是否为空，主要给Lua调用
        /// </summary>
        /// <param name="o"></param>
        /// <returns></returns>
        public static bool IsObjectNull(Object o)
        {
            return o == null;
        }

        private static Type GetComponentType(string _compname)
        {
            if (string.IsNullOrEmpty(_compname))
                return null;
            Type t = null;
            if (sPredefineComponentTypes.ContainsKey(_compname))
                return sPredefineComponentTypes[_compname];

            Assembly assb = Assembly.GetExecutingAssembly(); //.GetExecutingAssembly();
            t = assb.GetType(_compname);
            if (null == t)
            {
                return null;
            }

            sPredefineComponentTypes[_compname] = t;

            return t;
        }

        public static Component GetComponentInChildren(GameObject _go, string _compname)
        {
            if (_go == null)
                return null;
            Type t = GetComponentType(_compname);
            if (null == t)
            {
                return null;
            }

            return _go.GetComponentInChildren(t);
        }

        public static Component GetComponentInChildren(Component _comp, string _compname)
        {
            if (_comp == null)
                return null;
            Type t = GetComponentType(_compname);
            if (null == t)
            {
                return null;
            }

            return _comp.GetComponentInChildren(t);
        }

        public static string GetReadableSize(long size)
        {
            long step = 1024;
            double filesize = size;
            int size_suffix_index = 0;
            while (filesize > step)
            {
                if (size_suffix_index >= kSizeSuffix.Length - 1)
                {
                    size_suffix_index = kSizeSuffix.Length - 1;
                    break;
                }

                ++size_suffix_index;
                filesize /= step;
            }

            return string.Format("{0:F2}{1}", filesize, kSizeSuffix[size_suffix_index]);
        }

        /// <summary>
        /// 打开URL
        /// </summary>
        /// <param name="url"></param>
        public static void OpenUrl(string url)
        {
            Application.OpenURL(url);
        }

        #region PlayerPrefs

        public static int GetInt(string key, int value = default(int))
        {
            return PlayerPrefs.GetInt(key, value);
        }

        public static bool HasKey(string key)
        {
            return PlayerPrefs.HasKey(key);
        }

        public static bool HasString(string key)
        {
            return PlayerPrefs.HasKey(key);
        }

        public static void SetInt(string key, int value = default(int))
        {
            PlayerPrefs.DeleteKey(key);
            PlayerPrefs.SetInt(key, value);
        }

        public static string GetString(string key, string value = default(string))
        {
            return PlayerPrefs.GetString(key, value);
        }

        /// <summary>
        /// 保存数据
        /// </summary>
        public static void SetString(string key, string value = default(string))
        {
            PlayerPrefs.DeleteKey(key);
            PlayerPrefs.SetString(key, value);
        }

        /// <summary>
        /// 删除数据
        /// </summary>
        public static void RemoveKey(string key)
        {
            PlayerPrefs.DeleteKey(key);
        }

        public static bool CheckKeyAccord2User(string key)
        {
            string k = GetKeyAccord2User(key);
            return PlayerPrefs.HasKey(k);
        }

        public static string GetKeyAccord2User(string key)
        {
            return $"{key}@{UID}";
        }

        public static void SetIntAccord2User(string key, int value)
        {
            string k = GetKeyAccord2User(key);
            PlayerPrefs.DeleteKey(k);
            PlayerPrefs.SetInt(k, value);
        }

        public static int GetIntAccord2User(string key, int value = default(int))
        {
            return PlayerPrefs.GetInt(GetKeyAccord2User(key), value);
        }

        public static int GetInt4User(string key)
        {
            return PlayerPrefs.GetInt(key);
        }

        public static void SetStringAccord2User(string key, string value)
        {
            string k = GetKeyAccord2User(key);
            PlayerPrefs.DeleteKey(k);
            PlayerPrefs.SetString(k, value);
        }

        public static string GetStringAccord2User(string key, string value = default(string))
        {
            return PlayerPrefs.GetString(GetKeyAccord2User(key), value);
        }

        public static void RemoveKeyAccord2User(string key)
        {
            string k = GetKeyAccord2User(key);
            PlayerPrefs.DeleteKey(k);
        }

        #endregion

        /// <summary>
        /// 清理内存
        /// </summary>
        public static void ClearMemory()
        {
            Resources.UnloadUnusedAssets();
            GC.Collect();
        }

        public static void SetAllChild(Transform trParent, System.Action<Transform> SetChild)
        {
            Queue<Transform> childList = new Queue<Transform>();
            childList.Enqueue(trParent);
            while (childList.Count > 0)
            {
                Transform t = childList.Dequeue();

                if (null != SetChild) SetChild(t);

                for (int i = 0; i < t.childCount; i++)
                {
                    childList.Enqueue(t.GetChild(i));
                }
            }
        }
        
        public static bool TryParseEnum<T>(string value, out T result)
        {
            bool isSuccess = false;
            try
            {
                result = (T)Enum.Parse(typeof(T), value);
                isSuccess = true;
            }
            catch
            {
                result = default(T);
                isSuccess = false;
            }
            
            return isSuccess;
        }

        public static bool EnumEquals(Enum m, Enum n)
        {
            return Equals(m, n);
        }

        /// <summary>
        /// 设置对象层级
        /// </summary>
        /// <param name="obj"> 目标对象 </param>
        /// <param name="layer"> 层级值 </param>
        /// <param name="includeChildren"> 是否包含子节点标识，默认为true </param>
        public static void SetLayer(GameObject obj, int layer, bool includeChildren = true)
        {
            if (null == obj)
                return;

            obj.layer = layer;
            if (!includeChildren)
                return;
            
            var allTransform = obj.GetComponentsInChildren<Transform>(true);
            foreach (Transform trans in allTransform)
            {
                trans.gameObject.layer = layer;
            }
        }
        
        /// <summary>
        /// 设置对象层级，通过层级名称
        /// </summary>
        /// <param name="obj"> 目标对象 </param>
        /// <param name="layerName"> 层级名称 </param>
        /// <param name="includeChildren"> 是否包含子节点标识，默认为true </param>
        public static void SetLayerByName(GameObject obj, string layerName, bool includeChildren = true)
        {
            int layer = LayerMask.NameToLayer(layerName);
            SetLayer(obj, layer, includeChildren);
        }

        private static Random rand;

        public static void RandomSeed(int seed)
        {
            rand = new Random(seed);
        }
        public static float RandomFloat(float m, float n)
        {
            if (rand == null)
                rand = new Random();
            
            if (n <= m)
                return m;
            
            return m + ((float)rand.NextDouble()) * (n - m);
        }

        public static float RandomInt(int m, int n)
        {
            if (rand == null)
                rand = new Random();
            
            if (n <= m)
                return m;
            
            return m + rand.Next() % (m - n);
        }

        /// <summary>
        /// 设置Renderer的渲染层级
        /// </summary>
        /// <param name="renderer"></param>
        /// <param name="sortingLayer"></param>
        public static void SetSortingLayer(Renderer renderer, string sortingLayer)
        {
            renderer.sortingLayerName = sortingLayer;
        }

        /// <summary>
        /// 设置Renderer的渲染顺序
        /// </summary>
        /// <param name="renderer"></param>
        /// <param name="order"></param>
        public static void SetSortingOrder(Renderer renderer, int order)
        {
            renderer.sortingOrder = order;
        }
        
        //世界坐标转化为UI坐标
        public static Vector2 World2UIPos(Vector3 worldPos, RectTransform uiTrans, Canvas canvas)
        {
            if (uiTrans == null || canvas == null)
                return Vector2.zero;
            
            Vector3 screenPos = Camera.main.WorldToScreenPoint(worldPos);
            screenPos.z = 0;
            RectTransformUtility.ScreenPointToLocalPointInRectangle(uiTrans, 
                screenPos, canvas.worldCamera, out var uiPos);

            return uiPos;
        }

        public static void BinaryWriteInt(BinaryWriter bw, int num)
        {
            bw.Write(num);
        }
        
        public static void BinaryWriteLong(BinaryWriter bw, long num)
        {
            bw.Write(num);
        }
        
        public static void BinaryWriteDouble(BinaryWriter bw, double num)
        {
            bw.Write(num);
        }
        
        public static void BinaryWriteFloat(BinaryWriter bw, float num)
        {
            bw.Write(num);
        }
        
        public static SystemLanguage GetSystemLanguage()
        {
            return Application.systemLanguage;
        }

        public static bool CheckStringLegal(string str)
        {
            if (str == "null" || str == "nil")
                return false;
            
            string pattern = @"[@+%+""+'+\s+\n+$+#+\^+\*+]";
            var result = Regex.Match(str, pattern);
            if (result.Success)
                return false;

            return true;
        }

        /// <summary>
        /// 使用指定字符串连接多组字符串
        /// </summary>
        /// <param name="combineStr"> 用于连接的指定字符串 </param>
        /// <param name="stringParams"> 连接的内容 </param>
        /// <returns></returns>
        public static string CombineStrWith(string combineStr, params string[] stringParams)
        {
            if (stringParams.Length <= 0)
                return "";

            StringBuilder sb = new StringBuilder();
            int count = stringParams.Length;
            for (int i = 0; i < count; i++)
            {
                sb.Append(stringParams[i]);
                if (i < count - 1)
                    sb.Append(combineStr);
            }

            return sb.ToString();
        }

        public static string GetFileNameFromPath(string filePath, bool excludeSuffix = false)
        {
            var name = filePath.Split('/').Last();
            if (excludeSuffix)
                name = name.Split('.').First();
            return name;
        }

        public static string GenerateLocalDataPath(string fileName, string suffix = ".mp4")
        {
            string dataPath = "Data/" + fileName + suffix;
            return dataPath;
        }


        public static Vector3 RotateRound(Vector3 position, Vector3 center, Vector3 axis, float angle)
        {
            Vector3 point = Quaternion.AngleAxis(angle, axis) * (position - center);
            Vector3 resultVec3 = center + point;
            return resultVec3;
        }

        /// <summary>
        /// 把数据写到本地文件
        /// 因为 WriteAllBytes 好像会被代码裁剪掉，在这里实现一下调用
        /// </summary>
        public static void WriteToLocal(string filePath, byte[] fileBytes, bool cover)
        {
            if (!cover && File.Exists(filePath))
                return;
            
            File.WriteAllBytes(filePath, fileBytes);
        }

        public static void SetImage(Image image, string path)
        {
            //var handler = GlobalVariables.ResourceManager.LoadSpriteFromSpriteAtlas(path);
            //if (!handler.IsDone)
            //{
            //    Debug.Log(string.Format("Load Sprite %s Error", path));
            //    return;
            //}
            //
            //image.sprite = handler.Result;
            Sprite sprite = Resources.Load<Sprite>(path);
            if (!sprite)
            {
                Debug.LogError("未读取到图片:" + path);
                return;
            }
            image.sprite = sprite;
        }

        /// <summary>
        /// 一个点绕另一个点旋转
        /// </summary>
        /// <param name="point">要旋转的点</param>
        /// <param name="pivot">中心点</param>
        /// <param name="euler">旋转的角度</param>
        /// <returns></returns>
        public static Vector3 RotatePointAroundPivot(Vector3 point, Vector3 pivot, Vector3 euler)
        {
            Vector3 direction = point - pivot;
            Vector3 rotatedDirection = Quaternion.Euler(euler) * direction;
            Vector3 rotatedPoint = rotatedDirection + pivot;
            return rotatedPoint;
        }

        //设置屏幕常亮状态
        public static void SetScreenNeverSleep(bool neverSleep)
        {
            if (neverSleep)
            {
                Screen.sleepTimeout = SleepTimeout.NeverSleep;
            }
            else
            {
                Screen.sleepTimeout = SleepTimeout.SystemSetting;
            }
        }

        public static Vector2 GetScreenSize(CanvasScaler canvasScaler = null)
        {
            if (canvasScaler)
            {
                float realWidth = (Screen.width / ((Screen.height / canvasScaler.referenceResolution.y) * canvasScaler.referenceResolution.x)) * canvasScaler.referenceResolution.x;
                float realHeight = canvasScaler.referenceResolution.y;
                return new Vector2(realWidth, realHeight);
            }
            else
            {
                return new Vector2(Screen.width, Screen.height);
            }
            
        }

        public static Color List2Color(List<int> list)
        {
            int a = 255;
            if (list.Count > 3)
            {
                a = list[3];
            }
            return new Color(list[0] / 255f, list[1] / 255f, list[2] / 255f, a / 255f);
        }

        //public static async Task WaitUntil(Func<bool> predicate, int sleep = 50)
        //{
        //    while (!predicate())
        //    {
        //        await Task.Delay(sleep);
        //    }
        //}

        public static async void DelayDoFunc(int delay, UnityAction action)
        {
            await Task.Delay(delay);
            action?.Invoke();
        }

        public static void ChangeAllLayer(GameObject go, int layer)
        {
            go.layer = layer;
            for (int i = 0; i < go.transform.childCount; i++)
            {
                ChangeAllLayer(go.transform.GetChild(i).gameObject, layer);
            }
        }

        public static void ShuffleList<T>(List<T> list, int seed = 0)
        {
            System.Random rng;
            if (seed == 0)
            {
                rng = new System.Random();
            }
            else
            {
                rng = new System.Random(seed);
            }
            int n = list.Count;
            while (n > 1)
            {
                n--;
                int k = rng.Next(n + 1);
                T value = list[k];
                list[k] = list[n];
                list[n] = value;
            }
        }

        public static bool CheckCanHit(string selfTag, string otherTag)
        {
            switch (selfTag)
            {
                case "Player":
                    if (otherTag == "Enemy" || otherTag == "EnemyTower")
                    {
                        return true;
                    }
                    break;
                case "Demonic":
                    if (otherTag == "Enemy" || otherTag == "EnemyTower")
                    {
                        return true;
                    }
                    break;
                case "Tower":
                    if (otherTag == "Enemy" || otherTag == "EnemyTower")
                    {
                        return true;
                    }
                    break;
                case "Enemy":
                    if (otherTag == "Player" || otherTag == "Demonic" || otherTag == "Tower")
                    {
                        return true;
                    }
                    break;
                case "EnemyTower":
                    if (otherTag == "Player" || otherTag == "Demonic" || otherTag == "Tower")
                    {
                        return true;
                    }
                    break;
                default:
                    break;
            }
            return false;
        }
    }
}