using System;
using System.Collections;
using System.IO;
using System.Text;
#if UNITY_EDITOR
using UnityEditor;
#endif
using UnityEngine;
using UnityEngine.Networking;
using UnityEngine.Video;

namespace XRTool.Util
{
    /// <summary>
    /// 工具类，各种常用算法的集合脚本
    /// </summary>
    public static class UnityUtil
    {
        public const int MB = 1024 * 1024;
        public const long GB = 1024 * 1024 * 1024;
        public const string MBString = "MB";
        public const string GBString = "GB";
        /// <summary>
        /// 全局唯一id
        /// 自动自增
        /// </summary>
        private static int unicode = 10000000;
#if UNITY_EDITOR
        /// <summary>
        /// 尝试得到一个未命名的名称
        /// 下标从0开始检索，如果不存在此名字的文字，则返回此名字
        /// 如果存在，下标++
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <param name="path"></param>
        /// <param name="suffix"></param>
        /// <returns></returns>
        public static string TryGetName<T>(string path, string suffix = ".asset")
        {
            int index = 0;
            string confName = "";
            UnityEngine.Object obj = null;
            do
            {
                confName = path + "/" + typeof(T).Name + "_" + index + suffix;
                obj = AssetDatabase.LoadAssetAtPath(confName, typeof(T));
                index++;
            } while (obj);
            return confName;
        }
        public static string TryGetName(Type T, string path, string suffix = ".asset")
        {
            int index = 0;
            string confName = "";
            UnityEngine.Object obj = null;
            do
            {
                confName = path + "/" + T.Name + "_" + index + suffix;
                obj = AssetDatabase.LoadAssetAtPath(confName, T);
                index++;
            } while (obj);
            return confName;
        }
#endif
        /// <summary>
        /// 获取指定名称类型的对象
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <param name="childName"></param>
        /// <returns></returns>
        public static T GetChild<T>(Transform target, string childName)
        {
            var child = target.Find(childName);
            if (child)
            {
                return child.GetComponent<T>();
            }
            return default(T);
        }
        /// <summary>
        /// 深度优先搜索查找子物体
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <param name="childName"></param>
        /// <returns></returns>
        public static T GetDepthChild<T>(Transform transform, string childName)
        {
            Transform target = FindDepthTransf(transform, childName);
            if (target)
                return GetT<T>(target.gameObject);
            return default(T);
        }
        public static GameObject GetDepthChild(Transform transform, string childName)
        {
            Transform target = FindDepthTransf(transform, childName);
            if (target)
                return target.gameObject;
            return null;
        }
        /// <summary>
        /// 广度优先查找子物体
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <param name="childName"></param>
        /// <returns></returns>
        public static T GetBreadthChild<T>(Transform transform, string childName)
        {
            Transform target = FindBreadthTransf(transform, childName);
            if (target)
                return GetT<T>(target.gameObject);
            return default(T);
        }
        public static GameObject GetBreadthChild(Transform transform, string childName)
        {
            Transform target = FindBreadthTransf(transform, childName);
            if (target)
                return target.gameObject;
            return null;
        }
        public static T GetParent<T>(Transform transform)
        {
            return transform.GetComponentInParent<T>();
        }
        public static T GetChild<T>(Transform trans)
        {
            return trans.GetComponentInChildren<T>();
        }
        public static T GetT<T>(GameObject target)
        {
            return target.GetComponent<T>();
        }
        /// <summary>
        /// 深度优先检索子物体
        /// </summary>
        /// <param name="check"></param>
        /// <param name="childName"></param>
        /// <returns></returns>
        public static Transform FindDepthTransf(Transform check, string childName)
        {
            if (check.name == childName) return check;
            for (int i = 0; i < check.childCount; i++)
            {
                Transform obj = FindDepthTransf(check.GetChild(i), childName);
                if (obj)
                    return obj;
            }
            return null;
        }

        /// <summary>
        /// 广度优先检索子物体
        /// </summary>
        /// <param name="check"></param>
        /// <param name="childName"></param>
        /// <returns></returns>
        public static Transform FindBreadthTransf(Transform check, string childName)
        {
            Transform forreturn = check.Find(childName);
            if (forreturn)
            {
                return forreturn;
            }
            if (check.childCount > 0)
            {
                for (int i = 0; i < check.childCount; i++)
                {
                    var target = FindBreadthTransf(check.GetChild(i), childName);
                    if (target)
                    {
                        return target;
                    }
                }
            }
            return forreturn;
        }
        public static void SetParent(Transform parent, Transform child)
        {
            child.SetParent(parent);
            child.localPosition = Vector3.zero;
            child.localRotation = Quaternion.identity;
            child.localScale = Vector3.one;
        }
        /// <summary>
                /// 去除文件bom头后的字符
                /// </summary>
                /// <param name="buffer"></param>
                /// <returns></returns>
        public static string GetUTF8String(byte[] buffer)
        {
            if (buffer == null)
                return null;

            if (buffer.Length <= 3)
            {
                return Encoding.UTF8.GetString(buffer);
            }

            byte[] bomBuffer = new byte[] { 0xef, 0xbb, 0xbf };

            if (buffer[0] == bomBuffer[0]
              && buffer[1] == bomBuffer[1]
              && buffer[2] == bomBuffer[2])
            {
                return new UTF8Encoding(false).GetString(buffer, 3, buffer.Length - 3);
            }

            return Encoding.UTF8.GetString(buffer);
        }
        /// <summary>
        /// 获取距离放歌最近的数字
        /// 四舍五入
        /// </summary>
        /// <param name="num"></param>
        /// <param name="cell"></param>
        /// <returns></returns>
        public static float GetNearst(float num, float cell)
        {
            int dir = num < 0 ? -1 : 1;
            return ((int)(num + cell / 2 * dir) / (int)cell) * cell;
        }
        /// <summary>
        /// 判断两个矩形是否相交
        /// 如果两个矩形中心点在x、y轴的投影距离分别小于矩形的边长之和,则此矩形相交
        /// </summary>
        /// <param name="a"></param>
        /// <param name="b"></param>
        /// <returns></returns>
        public static bool IsCrossLine(Rect a, Rect b)
        {
            Vector2 dis = b.center - a.center;
            if (((int)a.width + (int)b.width) / 2 > Math.Abs(dis.x) && ((int)a.height + (int)b.height) / 2 > Math.Abs(dis.y))
            {
                return true;
            }
            return false;
        }
        public static void ChangeMateColor(Renderer render, Color color, string name = "_Color")
        {
            if (render)
            {
                MaterialPropertyBlock propertyBlock = new MaterialPropertyBlock();
                render.GetPropertyBlock(propertyBlock);
                propertyBlock.SetColor(name, color);
                render.SetPropertyBlock(propertyBlock);
                //ChangeMateValue(render, propertyBlock);
                propertyBlock = null;
            }
        }
        public static void ChangeMateColor(MaterialPropertyBlock propertyBlock, Renderer render, Color color, string name = "_Color")
        {
            if (render)
            {
                render.GetPropertyBlock(propertyBlock);
                propertyBlock.SetColor(name, color);
                render.SetPropertyBlock(propertyBlock);
                //ChangeMateValue(render, propertyBlock);
                //propertyBlock = null;
            }
        }
        public static void ChangeMateTexture2D(Renderer render, Texture2D img, string name = "_MainTex")
        {
            if (render)
            {
                MaterialPropertyBlock propertyBlock = new MaterialPropertyBlock();
                render.GetPropertyBlock(propertyBlock);
                propertyBlock.SetTexture(name, img);
                render.SetPropertyBlock(propertyBlock);
                propertyBlock = null;
            }
        }
        public static void ChangeMateVideo(GameObject obj, VideoClip video, string name = "_MainTex")
        {
            if (obj)
            {
                VideoPlayer vp = obj.GetComponent<VideoPlayer>();
                vp.clip = video;
            }
        }
        public static void ChangeMateTexture(Renderer render, Texture img, string name = "_MainTex")
        {
            if (render)
            {
                MaterialPropertyBlock propertyBlock = new MaterialPropertyBlock();
                render.GetPropertyBlock(propertyBlock);
                propertyBlock.SetTexture(name, img);
                render.SetPropertyBlock(propertyBlock);
                propertyBlock = null;
            }
        }
        public static void ChangeMateValue(Renderer render, float att, string name = "_Smoothness")
        {
            if (render)
            {
                MaterialPropertyBlock propertyBlock = new MaterialPropertyBlock();
                render.GetPropertyBlock(propertyBlock);
                propertyBlock.SetFloat(name, att);
                render.SetPropertyBlock(propertyBlock);
                //ChangeMateValue(render, propertyBlock);
                propertyBlock = null;
            }
        }
        public static void ChangeMateValue(Renderer render, Vector4 att, string name = "_Smoothness")
        {
            if (render)
            {
                MaterialPropertyBlock propertyBlock = new MaterialPropertyBlock();
                render.GetPropertyBlock(propertyBlock);
                propertyBlock.SetVector(name, att);
                render.SetPropertyBlock(propertyBlock);
                //ChangeMateValue(render, propertyBlock);
                propertyBlock = null;
            }
        }
        public static void ChangeMateValue(Renderer render, MaterialPropertyBlock propertyBlock)
        {
            if (render)
            {
                //MaterialPropertyBlock propertyBlock = new MaterialPropertyBlock();
                render.GetPropertyBlock(propertyBlock);
                //propertyBlock.SetVector(name, att);
                render.SetPropertyBlock(propertyBlock);
            }
        }
        /// <summary>
        /// 材质复制
        /// </summary>
        /// <param name="resMate"></param>
        /// <param name="targetMate"></param>
        public static void CopyMate(Material resMate, Material targetMate)
        {
            if (resMate && resMate != targetMate)
            {
                resMate.shader = targetMate.shader;
                resMate.CopyPropertiesFromMaterial(targetMate);
            }
        }
        ///// <summary>
        ///// 
        ///// </summary>
        ///// <returns></returns>
        //public static string ArtTransferInfo(ArtInfo info)
        //{
        //    Vector3 pos = GameSession.Instance.GetHeadForwadPos(info.Distance);
        //    if (GameNode.Instance)
        //    {
        //        pos = GameNode.Instance.transform.InverseTransformPoint(pos);
        //    }
        //    var angle = Vector3.zero;
        //    var scale = Vector3.one * info.Size;
        //    return TransferToString(pos, angle, scale, 2);
        //}
        public static string TransferToString(Transform tranfer, int state)
        {
            return TransferToString(tranfer.localPosition, tranfer.localEulerAngles, tranfer.localScale, state);
        }
        public static int maxTransfer = 1000;
        public static string TransferToString(Vector3 pos, Vector3 ang, Vector3 sca, int state)
        {
            string info = "";
            pos *= maxTransfer;
            ang *= maxTransfer;
            sca *= maxTransfer;
            string position = (int)(pos.x) + "," + (int)(pos.y) + "," + (int)(pos.z);
            string angle = (int)(ang.x) + "," + (int)(ang.y) + "," + (int)(ang.z);
            string scale = (int)(sca.x) + "," + (int)(sca.y) + "," + (int)(sca.z);
            info = position + "|" + angle + "|" + scale;
            //if (state == 2)
            //{
            //    info = pos + "|" + angle + "|" + scale;
            //}
            //else if (state == 1)
            //{
            //    info = pos + "|" + angle;
            //}
            //else if (state == 1)
            //{
            //    info = pos;
            //}
            return info;
        }
        //public static Posture GetPosture(Transform info)
        //{
        //    Posture posture = new Posture();
        //    posture.position = info.localPosition;
        //    posture.angle = info.localEulerAngles;
        //    posture.scale = info.localScale;
        //    posture.count = 2;
        //    return posture;
        //}
        //public static void SetPosture(Transform info, Posture posture, float time = -1)
        //{
        //    if (time <= 0)
        //    {
        //        info.localPosition = posture.position;
        //        info.localEulerAngles = posture.angle;
        //        info.localScale = posture.scale;
        //    }
        //    else
        //    {
        //        info.DOKill();
        //        if (posture.count >= 0)
        //        {
        //            info.DOLocalMove(posture.position, time);
        //        }
        //        if (posture.count >= 1)
        //        {
        //            info.DOLocalRotate(posture.angle, time);
        //        }
        //        if (posture.count >= 2)
        //        {
        //            info.DOScale(posture.scale, time);
        //        }
        //    }
        //}
        //public static Posture GetPosture(string info)
        //{
        //    Posture posture = new Posture();
        //    string[] arr = info.Split('|');
        //    for (int i = 0; i < arr.Length; i++)
        //    {
        //        if (string.IsNullOrEmpty(arr[i]))
        //        {
        //            continue;
        //        }
        //        string[] v = arr[i].Split(',');
        //        Vector3 vector = Vector3.zero;
        //        vector.x = float.Parse(v[0]);
        //        vector.y = float.Parse(v[1]);
        //        vector.z = float.Parse(v[2]);
        //        //for (int j = 0; j < v.Length; j++)
        //        //{
        //        //    float value = float.Parse(v[j]);
        //        //    if (j == 0)
        //        //    {
        //        //        vector.x = value;
        //        //    }
        //        //    else if (j == 1)
        //        //    {
        //        //        vector.y = value;
        //        //    }
        //        //    else if (j == 2)
        //        //    {
        //        //        vector.z = value;
        //        //    }
        //        //}
        //        vector /= maxTransfer;
        //        if (i == 0)
        //        {
        //            posture.position = vector;
        //        }
        //        else if (i == 1)
        //        {
        //            posture.angle = vector;
        //        }
        //        else if (i == 2)
        //        {
        //            posture.scale = vector;
        //        }
        //        posture.count = i;
        //    }

        //    return posture;
        //}
        //public static Posture GetPosture(string info)
        //{
        //    Posture posture = new Posture();
        //    string[] arr = info.Split('|');
        //    for (int i = 0; i < arr.Length; i++)
        //    {
        //        if (string.IsNullOrEmpty(arr[i]))
        //        {
        //            continue;
        //        }
        //        string[] v = arr[i].Split(',');
        //        Vector3 vector = Vector3.zero;
        //        for (int j = 0; j < v.Length; j++)
        //        {
        //            float value = float.Parse(v[j]);
        //            if (j == 0)
        //            {
        //                vector.x = value;
        //            }
        //            else if (j == 1)
        //            {
        //                vector.y = value;
        //            }
        //            else if (j == 2)
        //            {
        //                vector.z = value;
        //            }
        //        }
        //        vector *= maxTransfer;
        //        if (i == 0)
        //        {
        //            posture.position = vector;
        //        }
        //        else if (i == 1)
        //        {
        //            posture.angle = vector;
        //        }
        //        else if (i == 2)
        //        {
        //            posture.scale = vector;
        //        }
        //        posture.count = i;
        //    }

        //    return posture;
        //}
        public static Vector3 StringToVector3(string v)
        {
            Vector3 z = Vector3.zero;
            v = v.Replace("(", "").Replace(")", "");
            string[] s = v.Split(',');
            if (s.Length > 2)
            {
                z.x = float.Parse(s[0]);
                z.y = float.Parse(s[1]);
                z.z = float.Parse(s[2]);
                z /= maxTransfer;
            }
            return z;
        }
        public static string Vector3ToString(Vector3 v)
        {
            return v.ToString();
        }
        public static string QuaterToString(Quaternion q)
        {
            q.x *= maxTransfer;
            q.y *= maxTransfer;
            q.z *= maxTransfer;
            q.w *= maxTransfer;
            return q.ToString();
        }
        public static Quaternion StringToQuater(string v)
        {
            Quaternion q = Quaternion.identity;
            v = v.Replace("(", "").Replace(")", "");
            string[] s = v.Split(',');
            if (s.Length > 3)
            {
                q.x = float.Parse(s[0]) / maxTransfer;
                q.y = float.Parse(s[1]) / maxTransfer;
                q.z = float.Parse(s[2]) / maxTransfer;
                q.w = float.Parse(s[3]) / maxTransfer;
            }
            return q;
        }
        public static Vector3 RealForward(Transform body)
        {
            Vector3 right = body.right;
            right.y = 0;
            return Vector3.Cross(right, Vector3.up);
        }
        public static string CurTimeString
        {
            get { return DateTime.Now.ToString("MMddHHmmssf"); }
        }
        /// <summary>
        /// 通过时间生成一个绝对的唯一的id
        /// 此id的生成有可能存在重复，如果存在同时的调用方式时
        /// </summary>
        public static string TimeID
        {
            get { return DateTime.Now.ToString("HHmmssfffff"); }
        }
        /// <summary>
        /// 全局唯一id
        /// </summary>
        public static int Unicode
        {
            get
            {
                return ++unicode;
            }
        }
        /// <summary>
        /// 从URl下载指定的资源
        /// </summary>
        /// <param name="url">资源的地址，可以是网络路径，也可以是本地文件路径</param>
        /// <param name="updateProcess">下载的进度，当前已下载文件大小，以及进度</param>
        /// <param name="complete"></param>
        /// <param name="form">请求的参数，如果为空代表Get请求，不为空代表Post请求</param>
        /// <returns></returns>
        public static IEnumerator RequestDownData(string url, Action<string, UnityWebRequest> complete, DownloadHandler hander = null, Action<float, float> updateProcess = null, WWWForm form = null)
        {
            UnityWebRequest web;
            Debug.Log("RequestDownData" + url);

            if (form == null)
            {
                web = UnityWebRequest.Get(url);
            }
            else
            {
                web = UnityWebRequest.Post(url, form);
            }
            if (hander != null)
            {
                web.downloadHandler = hander;
            }
            web.SendWebRequest();
            while (!web.isDone)
            {
                updateProcess?.Invoke(web.downloadedBytes, web.downloadProgress);
                yield return 0;
            }
            if (web.isDone)
            {
                updateProcess?.Invoke(web.downloadedBytes, 1);
            }
            if (web.isNetworkError || web.isHttpError)
            {
                complete?.Invoke(web.error, web);
                Debug.LogError(web.error + url);
            }
            else
            {
                complete?.Invoke(null, web);
            }
            web.Dispose();
        }
        /// <summary>
        /// 拷贝文件
        /// </summary>
        /// <param name="SourcePath"></param>
        /// <param name="DestinationPath"></param>
        /// <param name="overwriteexisting">是否覆盖</param>
        /// <returns></returns>
        public static bool CopyDirectory(string SourcePath, string DestinationPath, bool overwriteexisting)
        {
            bool ret = false;
            try
            {
                SourcePath = SourcePath.EndsWith(@"\") ? SourcePath : SourcePath + @"\";
                DestinationPath = DestinationPath.EndsWith(@"\") ? DestinationPath : DestinationPath + @"\";

                if (Directory.Exists(SourcePath))
                {
                    if (Directory.Exists(DestinationPath) == false)
                        Directory.CreateDirectory(DestinationPath);

                    foreach (string fls in Directory.GetFiles(SourcePath))
                    {
                        FileInfo flinfo = new FileInfo(fls);
                        flinfo.CopyTo(DestinationPath + flinfo.Name, overwriteexisting);
                        UnityEngine.Debug.Log(flinfo.CreationTime.ToString());
                    }
                    foreach (string drs in Directory.GetDirectories(SourcePath))
                    {
                        DirectoryInfo drinfo = new DirectoryInfo(drs);
                        if (CopyDirectory(drs, DestinationPath + drinfo.Name, overwriteexisting) == false)
                            ret = false;
                    }
                }
                ret = true;
            }
            catch (Exception ex)
            {
                ret = false;
                UnityLog.LogError(ex.ToString());
            }
            return ret;
        }
        /// <summary>
        /// 广度优先，遍历文件
        /// </summary>
        /// <param name="path"></param>
        /// <param name="callBack"></param>
        public static void FindFileBreadth(string path, Action<string> callBack)
        {
            try
            {
                DirectoryInfo di = new DirectoryInfo(path);
                FileInfo[] fis = di.GetFiles();
                for (int i = 0; i < fis.Length; i++)
                {
                    callBack?.Invoke(fis[i].FullName);
                }
                DirectoryInfo[] dis = di.GetDirectories();
                for (int j = 0; j < dis.Length; j++)
                {
                    FindFileBreadth(dis[j].FullName, callBack);
                }
            }
            catch (Exception ex)
            {
                UnityLog.LogError(ex.ToString());
            }
        }
        /// <summary>
        /// 深度优先，遍历文件
        /// </summary>
        /// <param name="dir"></param>
        public static void FindFileByDepth(string dir, Action<string> callBack)
        {
            try
            {
                DirectoryInfo d = new DirectoryInfo(dir);
                FileSystemInfo[] fsinfos = d.GetFileSystemInfos();
                foreach (FileSystemInfo fsinfo in fsinfos)
                {
                    if (fsinfo is DirectoryInfo)
                    {
                        FindFileByDepth(fsinfo.FullName, callBack);
                    }
                    else
                    {
                        callBack?.Invoke(fsinfo.FullName);
                    }
                }
            }
            catch (Exception ex)
            {
                UnityLog.LogError(ex.ToString());
            }
        }
        /// <summary>
        /// 获取文件MD5值
        /// </summary>
        /// <param name="fileName">文件绝对路径</param>
        /// <returns>MD5值</returns>
        public static string GetMD5HashFromFile(string fileName)
        {
            try
            {
                FileStream file = new FileStream(fileName, FileMode.Open);
                System.Security.Cryptography.MD5 md5 = new System.Security.Cryptography.MD5CryptoServiceProvider();
                byte[] retVal = md5.ComputeHash(file);
                file.Close();
                StringBuilder sb = new StringBuilder();
                for (int i = 0; i < retVal.Length; i++)
                {
                    sb.Append(retVal[i].ToString("x2"));
                }
                return sb.ToString();
            }
            catch (Exception ex)
            {
                throw new Exception("GetMD5HashFromFile() fail,error:" + ex.Message);
            }
        }
        /// <summary>
        /// 重命名文件
        /// </summary>
        /// <param name="file"></param>
        /// <param name="newFile"></param>
        public static void ReNameFile(string file, string newFile)
        {
            if (File.Exists(file))
            {
                try
                {
                    string dir = Path.GetDirectoryName(newFile);
                    if (!Directory.Exists(dir))
                    {
                        Directory.CreateDirectory(dir);
                    }
                    if (File.Exists(newFile))
                    {
                        File.Delete(newFile);
                    }
                    FileInfo info = new FileInfo(file);
                    info.MoveTo(newFile);
                }
                catch (Exception ex)
                {
                    UnityLog.LogError(file + " is error" + ex.ToString());
                }
            }
        }
        /// <summary>
        /// 删除文件
        /// </summary>
        /// <param name="file"></param>
        /// <param name="newFile"></param>
        public static void DelFile(string file)
        {
            UnityLog.Log(file + " is Del", 3);
            if (File.Exists(file))
            {
                try
                {
                    File.Delete(file);
                }
                catch (Exception ex)
                {
                    UnityLog.LogError(file + " is error" + ex.ToString());
                }
            }
        }
        public static string ByteToMB(long bytes, int count = 2)
        {
            return TransNUM(bytes, MB, MBString, count);
        }
        public static string ByteToGB(long bytes, int count = 2)
        {
            return TransNUM(bytes, GB, GBString, count);
        }
        public static string TransNUM(long bytes, long jinzhi, string houzui = "", int count = 2)
        {
            float num = bytes * 1f / jinzhi;
            return num.ToString("f" + count) + houzui;
        }
        public static Sprite CreateSpriteByData(byte[] data)
        {
            Texture2D tex = new Texture2D(64, 64);
            tex.LoadImage(data);
            return Sprite.Create(tex, new Rect(0, 0, tex.width, tex.height), Vector2.zero);
        }
        /// <summary>
        /// 获取url对应的本地路径
        /// </summary>
        /// <param name="url"></param>
        /// <returns></returns>
        public static string GetFilePath(string url, int count = 2)
        {
            string[] arr = url.Split('/');
            string path = url;
            if (arr.Length > count)
            {
                path = "";
                for (int i = arr.Length - count; i < arr.Length; i++)
                {
                    if (i != arr.Length - 1)
                    {
                        path += arr[i] + "/";
                    }
                    else
                    {
                        path += arr[i];
                    }
                }
            }
            return path;
        }
    }
}
