using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Reflection;
using UnityEditor;

namespace XGame.Editor.Build
{
    internal class ContextBuilder
    {
        private BuildContext _context;
        private BuildConfig _config;
        private bool _collectCmd;
        public ContextBuilder(BuildConfig config, bool collectCmd)
        {
            _config = config;
            _collectCmd = collectCmd;
        }
        public BuildContext GetProduct()
        {
            return _context;
        }
        public bool Build()
        {
            //创建文件夹
            if (!Directory.Exists(_config.project.outputPath))
            {
                try
                {
                    Directory.CreateDirectory(_config.project.outputPath);
                }
                catch (Exception ex)
                {
                    BuildLog.Exception(ex);
                    BuildLog.Error($"打包输出目录错误,无法创建文件夹。{_config.project.outputPath}");
                    OnFailed(BuildErrorCode.OutputPathInvalid);
                    return false;
                }
            }
            //转换Version
            var versionName = _config.project.bundleVersion;
            if (!Version.TryParse(versionName, out var version))
            {
                var idx = versionName.LastIndexOf('.');
                if (idx > 0)
                {
                    //第一次解析失败，去掉最后一个"."后面的字符，再重试一次
                    versionName = versionName.Substring(0, idx);
                    try
                    {
                        version = Version.Parse(versionName);
                    }
                    catch (Exception ex)
                    {
                        BuildLog.Exception(ex);
                        BuildLog.Error($"Bundle Version failure. Version:{_config.project.bundleVersion}");
                        OnFailed(BuildErrorCode.BundleVersionFailure);
                        return false;
                    }
                }
                else
                {
                    BuildLog.Error($"Bundle Version Can't Find Separator. Version:{_config.project.bundleVersion}");
                    OnFailed(BuildErrorCode.BundleVersionFailure);
                    return false;
                }
            }
            //string callback = null;
            //if (_config.callback != null)
            //{
            //    //回调转成字符串
            //    callback = $"<callback type:{_config.callback.Method.ReflectedType.AssemblyQualifiedName} method:{_config.callback.Method.Name} /callback>";
            //    //清空原回调
            //    _config.callback = null;
            //}
            //app输出路径
            var output = GetBuildAppPath(_config, version);
            _context = new BuildContext()
            {
                config = _config,
                appOutputPath = output,
                version = version,
                //complatedEvent = callback,
            };
            if (_collectCmd)
            {
                _context.cmdTypeNames = CollectCmdTypeNames();
            }
            return true;
        }
        private void OnFailed(BuildErrorCode errorCode)
        {
            BuildUtils.BuildCompleted(_config.callback, errorCode);
            if (_config.editor.autoExit)
            {
                BuildUtils.ExitApplication(errorCode);
            }
        }
        private string GetBuildAppPath(BuildConfig config, Version version)
        {
            var output = Path.Combine(config.project.outputPath, $"{config.project.productName}_v{version}");
            string ext;
            switch (config.target)
            {
                case BuildTarget.Android:
                    ext = config.android.exportAsGoogleAndroidProject ? string.Empty : ".apk";
                    break;
                case BuildTarget.iOS:
                    ext = string.Empty;
                    break;
                case BuildTarget.StandaloneWindows:
                case BuildTarget.StandaloneWindows64:
                    if (Directory.Exists(output))
                    {
                        UnityEditor.FileUtil.DeleteFileOrDirectory(output);
                    }
                    Directory.CreateDirectory(output);
                    ext = $"{config.project.productName}.exe";
                    break;
                case BuildTarget.StandaloneOSX:
                    if (Directory.Exists(output))
                    {
                        UnityEditor.FileUtil.DeleteFileOrDirectory(output);
                    }
                    Directory.CreateDirectory(output);
                    ext = $"{config.project.productName}";
                    break;
                default:
                    ext = string.Empty;
                    break;
            }
            if (!string.IsNullOrEmpty(ext))
            {
                output = Path.Combine(output, ext);
            }
            return output;
        }
        private List<string> CollectCmdTypeNames()
        {
            var cmdType = typeof(IBuildCommand);
            var assembly = cmdType.Assembly;
            var types = assembly.GetTypes();
            var cmdTypesMap = new Dictionary<uint, Type>();
            foreach (var type in types)
            {
                if (type.IsClass && cmdType.IsAssignableFrom(type))
                {
                    if (type.IsAbstract) continue;

                    var attribute = type.GetCustomAttribute<BuildCommandAttribute>();
                    if (attribute != null)
                    {
                        var priority = attribute.Priority;
                        if (cmdTypesMap.ContainsKey(priority))
                        {
                            BuildLog.Error($"BuildCommand Priority Repeated. Last:{cmdTypesMap[priority]} Next:{type}");
                        }
                        else
                        {
                            cmdTypesMap.Add(attribute.Priority, type);
                        }
                    }
                }
            }
            var prioritys = cmdTypesMap.Keys.ToList();
            prioritys.Sort((a, b) => a.CompareTo(b));

            var typeNames = new List<string>();
            foreach (var priority in prioritys)
            {
                typeNames.Add(cmdTypesMap[priority].AssemblyQualifiedName);
            }
            return typeNames;
        }
    }
}
