using Cysharp.Threading.Tasks;
using Newtonsoft.Json;
using System;
using System.Collections;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Runtime.Serialization.Formatters.Binary;
using System.Text;
using System.Threading.Tasks;
using UnityEngine;

namespace TFramework
{
    /// <summary>
    /// 文件操作
    /// </summary>
    public static class FileOperations
    {
        /// <summary>
        /// 路径暂存列表
        /// </summary>
        static List<string> paths = new List<string>();

        #region Json
        static JsonSerializerSettings settings = new JsonSerializerSettings
        {
            TypeNameHandling = TypeNameHandling.Auto, // 或 TypeNameHandling.All
            Formatting = Formatting.Indented,
            ReferenceLoopHandling = ReferenceLoopHandling.Ignore,
        };

        /// <summary>
        /// 写入Json数据
        /// </summary>
        /// <param name="data">数据</param>
        /// <param name="path">文件完整路径</param>
        public static void WriteJsonData(object data, string path)
        {
            string json = JsonConvert.SerializeObject(data, settings);
            WriteData(json, path);
        }

        /// <summary>
        /// 异步写入Json数据
        /// </summary>
        /// <param name="data">数据</param>
        /// <param name="path">文件完整路径</param>
        public async static UniTask WriteJsonDataAsync(object data, string path)
        {
            string json = JsonConvert.SerializeObject(data, settings);
            await WriteDataAsync(json, path);
        }

        /// <summary>
        /// 读取Json数据
        /// </summary>
        /// <typeparam name="T">数据类型</typeparam>
        /// <param name="path">数据完整路径</param>
        /// <returns></returns>
        public static T ReadJsonData<T>(string path)
        {
            if (FileExists(path))
                return JsonConvert.DeserializeObject<T>(ReadText(path), settings);
            else
                return default(T);
        }

        /// <summary>
        /// 异步读取Json数据
        /// </summary>
        /// <typeparam name="T">数据类型</typeparam>
        /// <param name="path">数据完整路径</param>
        /// <returns></returns>
        public static async UniTask<T> ReadJsonDataAsync<T>(string path)
        {
            if (FileExists(path))
                return JsonConvert.DeserializeObject<T>(await ReadTextAsync(path), settings);
            else
                return default(T);
        }
        #endregion

        /// <summary>
        /// 写入数据
        /// </summary>
        /// <param name="value">要写入的数据</param>
        /// <param name="path">数据完整路径</param>
        public static void WriteData(string value, string path)
        {
            //打开或者新建文档
            using (StreamWriter sw = new StreamWriter(path))
            {
                //保存数据
                sw.WriteLine(value);
                //关闭文档
                sw.Close();
                sw.Dispose();
            }
        }

        /// <summary>
        /// 异步写入数据
        /// </summary>
        /// <param name="value">要写入的数据</param>
        /// <param name="path">数据完整路径</param>
        public static async UniTask WriteDataAsync(string value, string path)
        {
            //打开或者新建文档
            using (StreamWriter sw = new StreamWriter(path))
            {
                //保存数据
                await sw.WriteAsync(value);
                //关闭文档
                sw.Close();
                sw.Dispose();
            }
        }

        /// <summary>
        /// 以二进制形式写入数据
        /// </summary>
        /// <param name="obj">数据</param>
        /// <param name="path">数据完整路径</param>
        public static void WriteBinaryData(System.Object obj, string path)
        {
            try
            {
                string folderPath = path.Remove(path.LastIndexOf('/'));
                if (!Directory.Exists(folderPath))
                    Directory.CreateDirectory(folderPath);
                BinaryFormatter binary = new BinaryFormatter();
                using (FileStream fs = File.Create(path))
                {
                    binary.Serialize(fs, obj);
                }
            }
            catch (Exception e)
            {
                Log.Error(e.Message);
            }

        }

        /// <summary>
        /// 读取二进制文件
        /// </summary>
        /// <typeparam name="T">文件类型</typeparam>
        /// <param name="path">文件路径</param>
        /// <returns></returns>
        public static T ReadBinaryData<T>(string path)
        {
            try
            {
                BinaryFormatter binary = new BinaryFormatter();
                using (FileStream fs = File.Open(path, FileMode.Open))
                {
                    return (T)binary.Deserialize(fs);
                }
            }
            catch (Exception e)
            {
                Log.Info(e.Message);
                return default(T);
            }
        }

        public static byte[] SafeReadAllBytes(string inFile)
        {
            try
            {
                if (string.IsNullOrEmpty(inFile) || !File.Exists(inFile))
                    return null;

                File.SetAttributes(inFile, FileAttributes.Normal);
                return File.ReadAllBytes(inFile);
            }
            catch (System.Exception ex)
            {
                Log.Error(string.Format("SafeReadAllBytes failed! path = {0} with err = {1}", inFile, ex.Message));
                return null;
            }
        }

        /// <summary>
        /// 读取文本数据
        /// </summary>
        /// <param name="path">数据完整路径</param>
        /// <returns></returns>
        public static string ReadText(string path)
        {
            if (!File.Exists(path)) return null;

            using (StreamReader str = new StreamReader(path, Encoding.UTF8))
            {
                if (str == null) return null;
                return str.ReadToEnd();
            }
        }

        /// <summary>
        /// 异步读取文本数据
        /// </summary>
        /// <param name="path">数据完整路径</param>
        /// <returns></returns>
        public async static UniTask<string> ReadTextAsync(string path)
        {
            if (!File.Exists(path)) return null;

            using (StreamReader str = new StreamReader(path, Encoding.UTF8))
            {
                if (str == null) return null;
                return await str.ReadToEndAsync();
            }
        }

        /// <summary>
        /// 读取Texture2D文件
        /// </summary>
        /// <param name="path"></param>
        /// <returns></returns>
        public static Texture2D ReadTexture2D(string path)
        {
            if (!File.Exists(path))
                return null;
            FileStream fs = new FileStream(path, FileMode.Open);
            byte[] buffer = new byte[fs.Length];
            fs.Read(buffer, 0, buffer.Length);
            fs.Close();
            Texture2D originalTex = new Texture2D(2, 2);
            var iSLoad = originalTex.LoadImage(buffer);
            originalTex.Apply();
            if (iSLoad)
                return originalTex;
            else
                return null;
        }

        /// <summary>
        /// 获取文件夹下所有文件路径
        /// </summary>
        /// <param name="dir">文件夹路径</param>
        /// <param name="excludeFile">要排除的文件的后缀</param>
        /// <returns></returns>
        public static string[] GetFolderAllFilePath(string dir, params string[] excludeFile)
        {
            paths.Clear();
            FindFile(dir, excludeFile);
            return paths.ToArray();
        }

        static void FindFile(string dir, params string[] excludeFile)
        {
            DirectoryInfo info = new DirectoryInfo(dir);
            FileSystemInfo[] file = info.GetFileSystemInfos();
            foreach (var item in file)
            {
                if (item is DirectoryInfo)
                    FindFile(item.FullName, excludeFile);
                else
                {
                    if (excludeFile != null)
                    {
                        string suffix = item.Extension;
                        bool isAdd = true;

                        foreach (var su in excludeFile)
                        {
                            if (suffix == su)
                                isAdd = false;
                        }
                        if (isAdd)
                            paths.Add(item.FullName);
                    }
                    else
                        paths.Add(item.FullName);
                }
            }
        }

        /// <summary>
        /// 获取路径下所有文件夹路径
        /// </summary>
        /// <param name="path">路径</param>
        /// <returns></returns>
        public static string[] GetPathAllFolders(string path)
        {
            paths.Clear();
            FindFolders(path);
            return paths.ToArray();
        }

        static void FindFolders(string path)
        {
            DirectoryInfo info = new DirectoryInfo(path);
            FileSystemInfo[] file = info.GetFileSystemInfos();
            foreach (var item in file)
            {
                if (item is DirectoryInfo)
                {
                    FindFolders(item.FullName);
                    paths.Add(item.FullName);
                }
            }
        }

        /// <summary>
        /// 拷贝文件夹
        /// </summary>
        /// <param name="sourceDirName"></param>
        /// <param name="destDirName"></param>
        /// <param name="isOverlay">是否覆盖已有同名文件</param>
        public static void CopyDirectory(string sourceDirName, string destDirName,bool isOverlay=false)
        {
            try
            {
                if (!Directory.Exists(destDirName))
                {
                    Directory.CreateDirectory(destDirName);
                    File.SetAttributes(destDirName, File.GetAttributes(sourceDirName));
                }

                if (destDirName[destDirName.Length - 1] != Path.DirectorySeparatorChar)
                    destDirName = destDirName + Path.DirectorySeparatorChar;

                string[] files = Directory.GetFiles(sourceDirName);
                foreach (string file in files)
                {
                    if (File.Exists(destDirName + Path.GetFileName(file))&&!isOverlay)
                            continue;
                    FileInfo fileInfo = new FileInfo(file);
                    if (fileInfo.Extension.Equals(".meta", StringComparison.CurrentCultureIgnoreCase))
                        continue;

                    File.Copy(file, destDirName + Path.GetFileName(file), true);
                    File.SetAttributes(destDirName + Path.GetFileName(file), FileAttributes.Normal);
                }

                string[] dirs = Directory.GetDirectories(sourceDirName);
                foreach (string dir in dirs)
                {
                    CopyDirectory(dir, destDirName + Path.GetFileName(dir),isOverlay);
                }
            }
            catch (Exception ex)
            {
                throw ex;
            }
        }

        /// <summary>
        /// 拷贝文件
        /// </summary>
        /// <param name="sourceFileNmae"></param>
        /// <param name="destFlieName"></param>
        public static void CopyFile(string sourceFileNmae,string destFlieName)
        {
            FileInfo file = new FileInfo(sourceFileNmae);
            if (file.Exists)
                file.CopyTo(destFlieName, true);
            else
                Log.Error($"目标文件【{sourceFileNmae}】不存在！");
        }

        /// <summary>
        /// 检查文件路径是否存在
        /// </summary>
        /// <param name="path"></param>
        /// <returns></returns>
        public static bool FileExists(string path) => File.Exists(path);

        /// <summary>
        /// 检查文件夹是否存在
        /// </summary>
        /// <param name="path"></param>
        /// <returns></returns>
        public static bool FolderExists(string path) => Directory.Exists(path);

        /// <summary>
        /// 获取文件夹下指定类型文件信息
        /// </summary>
        /// <param name="folder">文件夹</param>
        /// <param name="suffix">后缀</param>
        /// <returns></returns>
        public static FileInfo[] GetFiles(string folder, string suffix)
        {
            if (!Directory.Exists(folder)) return null;

            DirectoryInfo di = new DirectoryInfo(folder);
            return di.GetFiles().ToList().Where(p => p.Extension == suffix).Select(p => p).ToArray();
        }

        /// <summary>
        /// 获取文件夹下指定类型文件信息
        /// </summary>
        /// <param name="folder">文件夹</param>
        /// <param name="suffix">后缀</param>
        /// <returns></returns>
        public static FileInfo[] GetFiles(string folder, string suffix, IComparer comparer)
        {
            if (!Directory.Exists(folder)) return null;

            DirectoryInfo di = new DirectoryInfo(folder);
            FileInfo[] fileInfo = di.GetFiles().ToList().Where(p => p.Extension == suffix).Select(p => p).ToArray();
            Array.Sort(fileInfo, comparer);
            return fileInfo;
        }

        /// <summary>
        /// 按时间排序
        /// </summary>
        /// <returns></returns>
        public static IComparer SortToTime => new FileTimeComparer();

        public class FileTimeComparer : IComparer
        {
            public int Compare(object x, object y)
            {
                FileInfo file1 = x as FileInfo;
                FileInfo file2 = y as FileInfo;
                return file1.LastWriteTime.CompareTo(file2.LastWriteTime);
            }
        }

        public static void CreateFloder(string path) => Directory.CreateDirectory(path);

        /// <summary>
        /// 创建项目绝对路径文件夹
        /// </summary>
        /// <param name="path"></param>
        public static void CreateAbsoluteFloder(string path)
        {
            CreateFloder(GetAbsolutePath(path));
        }

        /// <summary>
        /// 获取相对项目路径的绝对路径
        /// </summary>
        /// <param name="path"></param>
        /// <returns></returns>
        public static string GetAbsolutePath(string path)
        {
            string _path = Application.dataPath.Remove(Application.dataPath.LastIndexOf('/') + 1);
            return _path + path;
        }
    }
}
