using CommonLang.Log;
using System;
using System.Collections.Generic;
using System.IO;
using System.Text;

namespace CommonLang.IO
{
    public static class Resource
    {
        private static IResourceLoader mCurLoader = new DefaultResourceLoader();

        public static void SetLoader(IResourceLoader loader)
        {
            mCurLoader = loader;
        }

        /// <summary>
        /// 判断资源是否存在
        /// </summary>
        /// <param name="path"></param>
        /// <returns></returns>
        public static bool ExistData(string path)
        {
            return mCurLoader.ExistData(path);
        }
        /// <summary>
        /// 读取资源
        /// </summary>
        /// <param name="path">子目录对应的资源路径</param>
        /// <returns></returns>
        public static byte[] LoadData(string path)
        {
            return mCurLoader.LoadData(path);
        }
        /// <summary>
        /// 列出所有子文件,不包换目录
        /// </summary>
        /// <param name="path"></param>
        /// <returns></returns>
        public static string[] ListFiles(string path)
        {
            return mCurLoader.ListFiles(path);
        }
        /// <summary>
        /// 读取资源
        /// </summary>
        /// <param name="path">子目录对应的资源路径</param>
        /// <returns></returns>
        public static Stream LoadDataAsStream(string path)
        {
            var stream = mCurLoader.LoadDataAsStream(path);
            if (stream == null)
            {
                var bin = mCurLoader.LoadData(path);
                if (bin != null) stream = new MemoryStream(bin);
            }
            return stream;
        }
        /// <summary>
        /// 读取文本资源
        /// </summary>
        /// <param name="path"></param>
        /// <returns></returns>
        public static string LoadAllText(string path)
        {
            byte[] data = LoadData(path);
            if (data != null)
            {
                return CUtils.DecodeUTF8(data);
            }
            return null;
        }
        public static string FormatPath(string path)
        {
            path = path.Replace('\\', '/');
            return path;
        }
    }

    public interface IResourceLoader
    {
        /// <summary>
        /// 判断一个文件是否存在
        /// </summary>
        /// <param name="path"></param>
        /// <returns></returns>
        bool ExistData(string path);
        /// <summary>
        /// 读取二进制数据
        /// </summary>
        /// <param name="path"></param>
        /// <returns></returns>
        byte[] LoadData(string path);
        /// <summary>
        /// 尝试以流的方式读取,返回空表示不支持流
        /// </summary>
        /// <param name="path"></param>
        /// <returns></returns>
        Stream LoadDataAsStream(string path);
        /// <summary>
        /// 列出所有子文件,不包换目录
        /// </summary>
        /// <param name="path"></param>
        /// <returns></returns>
        string[] ListFiles(string path);
    }

    public class DefaultResourceLoader : IResourceLoader
    {
        public const string PREFIX_FILE = "file://";
        public const string PREFIX_RES = "res://";

        protected readonly Logger log;
        protected static string mRoot = ".";
        public static void SetRoot(string root)
        {
            mRoot = root;
        }

        public DefaultResourceLoader()
        {
            log = LoggerFactory.GetLogger(GetType().Name);
        }
        public DefaultResourceLoader(string root)
        {
            mRoot = root;
            log = LoggerFactory.GetLogger(GetType().Name);
        }

        //-----------------------------------------------------------------------------------------------------
        #region FileSystem

        protected virtual bool _TryLoadFromFileSystem(string path, ref byte[] ret)
        {
            if (System.IO.File.Exists(path))
            {
                ret = System.IO.File.ReadAllBytes(path);
                return true;
            }
            try
            {
                string filepath = System.IO.Path.GetFullPath(mRoot + "/" + path);
                if (System.IO.File.Exists(filepath))
                {
                    ret = System.IO.File.ReadAllBytes(filepath);
                    return true;
                }
            }
            catch (Exception err)
            {
                log.Error(err.Message, err);
            }
            return false;
        }
        protected virtual bool _TryLoadFromFileSystem(string path, ref Stream ret)
        {
            if (System.IO.File.Exists(path))
            {
                ret = new FileStream(path, FileMode.Open, FileAccess.Read, FileShare.Read);
                return true;
            }
            try
            {
                string filepath = System.IO.Path.GetFullPath(mRoot + "/" + path);
                if (System.IO.File.Exists(filepath))
                {
                    ret = new FileStream(filepath, FileMode.Open, FileAccess.Read, FileShare.Read);
                    return true;
                }
            }
            catch (Exception err)
            {
                log.Error(err.Message, err);
            }
            return false;
        }
        protected virtual bool _TryListFileSystem(string path, ref string[] ret)
        {
            string dir = null;
            if (System.IO.Directory.Exists(path))
            {
                dir = System.IO.Path.GetFullPath(path);
            }
            else
            {
                dir = System.IO.Path.GetFullPath(mRoot + "/" + path);
            }
            if (System.IO.Directory.Exists(dir))
            {
                try
                {
                    var subs = Directory.GetFiles(dir);
                    var list = new List<string>(subs.Length);
                    foreach (var f in subs)
                    {
                        list.Add(f.Substring(dir.Length));
                    }
                    ret = list.ToArray();
                    return true;
                }
                catch (Exception err)
                {
                    log.Error(err.Message, err);
                }
            }
            return false;
        }
        protected virtual bool _ExistWithFileSystem(string path)
        {
            if (System.IO.File.Exists(path))
            {
                return true;
            }
            try
            {
                string filepath = System.IO.Path.GetFullPath(mRoot + "/" + path);
                return System.IO.File.Exists(filepath);
            }
            catch (Exception err)
            {
                log.Error(err.Message, err);
            }
            return false;
        }
        #endregion
        //-----------------------------------------------------------------------------------------------------


        public virtual bool ExistData(string path)
        {
            if (path.StartsWith(PREFIX_FILE))
            {
                return _ExistWithFileSystem(path.Substring(PREFIX_FILE.Length));
            }
            if (path.StartsWith(PREFIX_RES))
            {
                return false;
            }
            if (System.IO.File.Exists(path) && _ExistWithFileSystem(path))
            {
                return true;
            }
            return false;
        }

        public virtual byte[] LoadData(string path)
        {
            byte[] ret = null;
            if (path.StartsWith(PREFIX_FILE))
            {
                _TryLoadFromFileSystem(path.Substring(PREFIX_FILE.Length), ref ret);
                return ret;
            }
            if (path.StartsWith(PREFIX_RES))
            {
                return ret;
            }
            // File
            if (System.IO.File.Exists(path) && _TryLoadFromFileSystem(path, ref ret))
            {
                return ret;
            }
            return ret;
        }

        public virtual Stream LoadDataAsStream(string path)
        {
            Stream ret = null;
            if (path.StartsWith(PREFIX_FILE))
            {
                _TryLoadFromFileSystem(path.Substring(PREFIX_FILE.Length), ref ret);
                return ret;
            }
            if (path.StartsWith(PREFIX_RES))
            {
                return ret;
            }
            // File
            if (System.IO.File.Exists(path) && _TryLoadFromFileSystem(path, ref ret))
            {
                return ret;
            }
            return ret;
        }

        public virtual string[] ListFiles(string path)
        {
            string[] ret = null;
            if (path.StartsWith(PREFIX_FILE))
            {
                _TryListFileSystem(path.Substring(PREFIX_FILE.Length), ref ret);
                return ret;
            }
            if (path.StartsWith(PREFIX_RES))
            {
                return ret;
            }
            // File
            if (System.IO.Directory.Exists(path) && _TryListFileSystem(path, ref ret))
            {
                return ret;
            }
            return null;
        }
    }

}