﻿using log4net;
using Soldier.Extension.Model;
using Soldier.Framework.Utils;
using System;
using System.AddIn.Contract;
using System.AddIn.Pipeline;
using System.Collections.Generic;
using System.Diagnostics;
using System.IO;
using System.Linq;
using System.Reflection;
using System.Text;
using System.Threading.Tasks;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Threading;

namespace Soldier.Extension
{
    public class PluginLoader
    {
        private static Dictionary<string, PluginLoader> dict = new Dictionary<string, PluginLoader>();
        private static List<PluginModel> _Plugins;
        private static readonly object locker = new object();
        private static string UserPath = @"Plugin";
        private static string SystemPath = @"System";
        private static string Manifest = "Plugin.manifest";

        private static ILog logger = LogUtils.GetLogger(typeof(PluginLoader));

        private AppDomain domain;
        private PluginProxy proxy;


        public PluginLoader(PluginModel plugin)
        {
            AppDomainSetup setup = new AppDomainSetup();
            setup.ApplicationName = plugin.Title + "(" + plugin.Id + ")";
            setup.ApplicationBase = AppDomain.CurrentDomain.BaseDirectory;
            setup.LoaderOptimization = LoaderOptimization.MultiDomain;
            setup.PrivateBinPath = Path.Combine(plugin.PluginPath, "private");
            setup.CachePath = AppDomain.CurrentDomain.BaseDirectory + @"\.Soldier";
            setup.ShadowCopyFiles = "true";
            setup.ShadowCopyDirectories = plugin.PluginPath;
            setup.ConfigurationFile = AppDomain.CurrentDomain.SetupInformation.ConfigurationFile;

            domain = AppDomain.CreateDomain(setup.ApplicationName, null, setup);
            Dispatcher.CurrentDispatcher.UnhandledException += (obj, arg) =>
            {
                arg.Handled = true;
                MessageBox.Show(arg.Exception.Message);
            };
            string name = Assembly.GetExecutingAssembly().GetName().FullName;
            proxy = (PluginProxy)domain.CreateInstanceAndUnwrap(name,typeof(PluginProxy).FullName);
        }

        public static PluginLoader GetSingleton(PluginModel plugin)
        {
            if (!dict.ContainsKey(plugin.Id))
            {
                dict.Add(plugin.Id, new PluginLoader(plugin));
            }
            return dict[plugin.Id];
        }

        private static List<PluginModel> LoadPlugin(string path)
        {
            List<PluginModel> plugins = new List<PluginModel>();
            if (!FolderUtils.IsExists(path))
            {
                return plugins;
            }

            List<FileInfo> files = FileUtils.GetAllFiles(path, (folder, file) => { return file.Name.Equals(Manifest); });
            foreach (FileInfo file in files)
            {
                try
                {
                    var model = XmlUtils.FromXml<PluginModel>(XmlUtils.ReadXml(file.FullName));
                    model.Icon = ImageUtils.Read(file.DirectoryName + @"\" + model.IconPath);
                    model.PluginPath = file.DirectoryName;
                    plugins.Add(model);
                }
                catch (Exception ex)
                {
                    logger.Error(string.Format("从{0}加载插件失败", file.DirectoryName), ex);
                }
            }

            return plugins;
        }

        public static List<PluginModel> GetAllPlugin()
        {
            if (_Plugins == null)
            {
                lock (locker)
                {
                    if (_Plugins == null)
                    {
                        _Plugins = new List<PluginModel>();
                        foreach(var plugin in LoadPlugin(SystemPath))
                        {
                            plugin.Ownership = Ownership.System;
                            _Plugins.Add(plugin);
                        }
                        foreach (var plugin in LoadPlugin(UserPath))
                        {
                            plugin.Ownership = Ownership.User;
                            _Plugins.Add(plugin);
                        }
                    }
                }
            }

            return _Plugins;
        }

        public static PluginModel FindPlugin(string id)
        {
            foreach(var plugin in GetAllPlugin())
            {
                if (plugin.Id.Equals(id))
                {
                    return plugin;
                }
            }
            return null;
        }

        public static void UpdatePlugin()
        {
            lock (locker)
            {
                _Plugins = new List<PluginModel>();
                foreach (var plugin in LoadPlugin(SystemPath))
                {
                    plugin.Ownership = Ownership.System;
                    _Plugins.Add(plugin);
                }
                foreach (var plugin in LoadPlugin(UserPath))
                {
                    plugin.Ownership = Ownership.User;
                    _Plugins.Add(plugin);
                }
            }
        }

        public void LoadAssembly(string fullName)
        {
            proxy.LoadAssembly(fullName);
        }

        public void SetContext(string PluginPath)
        {
            proxy.SetContext(PluginPath);
        }

        public FrameworkElement RunPlugin()
        {
            INativeHandleContract iContract  = proxy.RunPlugin();
            
            if(iContract != null)
            {
                return FrameworkElementAdapters.ContractToViewAdapter(iContract);
            }
            
            return null;
        }

        public void Unload()
        {
            AppDomain.Unload(domain);
            domain = null;
        }
    }
}
