using System;
using System.Collections.Generic;
using System.Globalization;
using System.IO;
using System.Linq;
using System.Text;
using System.Threading;
using System.Windows;
using System.Windows.Forms;
using Aitex.Core.RT.DataCenter;
using Aitex.Core.RT.Log;
using Aitex.Core.Util;
using MECF.Framework.Common.CommonData;
using MECF.Framework.Common.NotifyTrayIcons;
using MECF.Framework.Common.Utilities;
using MECF.Framework.RT.Core.Backend;
using Application = System.Windows.Application;

namespace MECF.Framework.RT.Core.Applications
{
    public class RtApplication: Singleton<RtApplication>
    {
        #region Variables

        private readonly PassWordView _loginWindow = new PassWordView();
        private static readonly ThreadExceptionEventHandler _threadHandler = new(Application_ThreadException);
        private ShowWindowNotifyIcon _trayIcon;
        private IRtInstance _instance;
        private static bool _ignoreError;
        private static Mutex _mutex;

        #endregion

        #region Properties

        public static BackendMainView MainView { get; set; }

        public static List<ReferencedAssemblyInfo> ReferencedAssemblies { get; private set; }

        public static string ReleaseNote { get; set; }

        public static ReleaseVersion RTVersion { get; set; }

        protected System.Windows.Application application { get; set; }

        #endregion


        #region Methods

        public void Initialize(IRtInstance instance)
        {
            application = System.Windows.Application.Current;
            application.DispatcherUnhandledException += OnUnhandledException;

            AppDomain.CurrentDomain.UnhandledException += new UnhandledExceptionEventHandler(CurrentDomain_UnhandledException);
            AppDomain.CurrentDomain.ProcessExit += new EventHandler(CurrentDomain_ProcessExit);
      
            //Because this is a static event, you must detach your event handlers when your application is disposed, or memory leaks will result.
            System.Windows.Forms.Application.ThreadException += _threadHandler;
            System.Windows.Forms.Application.SetUnhandledExceptionMode(UnhandledExceptionMode.CatchException);

            _instance = instance;

            CheckAnotherInstanceRunning(instance.SystemName);

            MainView = new BackendMainView();
            MainView.Icon = _instance.TrayIcon;
			MainView.Title = _instance.SystemName + " Console";

            _loginWindow.Title = _instance.SystemName + " RT Backend Login";
            _loginWindow.Icon = _instance.TrayIcon;
            _ignoreError = _instance.KeepRunningAfterUnknownException;

            if (_instance.EnableNotifyIcon)
            {
                _trayIcon = new ShowWindowNotifyIcon(_instance.SystemName, _instance.TrayIcon);
				_trayIcon.ExitWindow += TrayIconExitWindow;
				_trayIcon.ShowMainWindow += TrayIconShowMainWindow;
				_trayIcon.ShowBallon(_instance.SystemName, "Start running");
            }

            InitSystem();

            if (_instance.DefaultShowBackendWindow)
            {
                MainView.Show();
            }

            // 禁止自动休眠或关闭屏幕
            WIN32APIsWrapper.PreventSleep();

            // 获取RT依赖的Dll列表
            var assem = AppDomain.CurrentDomain.GetAssemblies();
            ReferencedAssemblies =
                assem.Where(x => (x.GetName().Name.StartsWith("sic", true, CultureInfo.CurrentCulture) 
                                  || x.GetName().Name.StartsWith("mecf", true, CultureInfo.CurrentCulture)))
                    .Select(x => new ReferencedAssemblyInfo()
                    {
                        Name = x.GetName().Name, 
                        Version = x.GetName().Version,
                        PublicKeyToken = x.GetName().GetPublicKeyToken()
                    }).OrderBy(x=>x.Name)
                    .ToList();

            // 读取ReleaseNote
            ReleaseNote = LoadReleaseNote();

            var ver = Application.Current.GetType().Assembly.GetName().Version;
            RTVersion = new ReleaseVersion(ver.Major, ver.Minor, ver.Build, ver.Revision);

            DATA.Subscribe("System.AssembliesInfo", ()=> ReferencedAssemblies);
            DATA.Subscribe("System.ReleaseNote", ()=> ReleaseNote);
            DATA.Subscribe("System.RTVersion", ()=> RTVersion);
        }

        private void OnUnhandledException(object sender, System.Windows.Threading.DispatcherUnhandledExceptionEventArgs e)
        {
            if (e.Exception != null)
            {
                LOG.Write(e.Exception);
                if (e.Exception.InnerException != null)
                    LOG.Write(e.Exception.InnerException);
            }

            e.Handled = true;
        }

        private void CheckAnotherInstanceRunning(string appName)
        {
            _mutex = new Mutex(true, appName, out bool createNew);

            if (!createNew)
            {
                if (!_mutex.WaitOne(1000))
                {
                    string msg = $"{appName} is already running，can not start again";

                    System.Windows.MessageBox.Show(msg, $"{appName} Launch Failed", System.Windows.MessageBoxButton.OK,  MessageBoxImage.Warning, System.Windows.MessageBoxResult.No,
                        System.Windows.MessageBoxOptions.DefaultDesktopOnly);
                    Environment.Exit(0);
                }
            }
        }

        private static void CurrentDomain_ProcessExit(object sender, EventArgs e)
        {
            System.Windows.Forms.Application.ThreadException -= _threadHandler;
        }

        private static void Application_ThreadException(object sender, ThreadExceptionEventArgs e)
        {
            ShowMessageDialog(e.Exception);
        }

        private static void CurrentDomain_UnhandledException(object sender, UnhandledExceptionEventArgs e)
        {
            ShowMessageDialog((Exception)e.ExceptionObject);
        }

        private static void ShowMessageDialog(Exception ex)
        {
            LOG.Write(ex);
            if (!_ignoreError)
            {
                string message = string.Format(" RT unknown exception：{0},\n", DateTime.Now);
                System.Windows.MessageBox.Show(message + ex.Message, "Unexpected exception", System.Windows.MessageBoxButton.YesNo,
                    System.Windows.MessageBoxImage.Exclamation,
                    System.Windows.MessageBoxResult.No,
                    System.Windows.MessageBoxOptions.DefaultDesktopOnly);
                //Environment.Exit(0);
            }

        }

        private void TrayIconExitWindow()
        {
            if (System.Windows.MessageBox.Show("Are you sure you want to exit system?", _instance.SystemName, System.Windows.MessageBoxButton.YesNo,
                    System.Windows.MessageBoxImage.Exclamation,
                    System.Windows.MessageBoxResult.No,
                    System.Windows.MessageBoxOptions.DefaultDesktopOnly) == System.Windows.MessageBoxResult.Yes)
            {
                if (_trayIcon != null)
                {
                    _trayIcon.ShowBallon(_instance.SystemName, "Stop running");
                }

                Terminate();
            }
        }

        private void TrayIconShowMainWindow(bool show)
        {
            if (MainView == null)
                return;

            if (show )
            {
                if (!MainView.IsVisible)
                {
                    if (!_loginWindow.IsVisible)
                    {
                        _loginWindow.Reset();

                        _loginWindow.ShowDialog();

                        if (_loginWindow.VerificationResult) 
                            MainView.Show();
                    }
                }
                else
                {
                    MainView.Show();
                }
            }
            else
            {
                MainView.Hide();
            }
        }


        private void InitSystem()
        {
            if (_instance.Loader != null)
            {
                _instance.Loader.Initialize();
            }
        }

        private string LoadReleaseNote()
        {
            const string fileName = "ReleaseNotes.md";
            var fullName = Path.Combine(AppDomain.CurrentDomain.BaseDirectory, fileName);
            if (File.Exists(fullName))
            {
                return File.ReadAllText(fullName, Encoding.Default);
            }
            else
            {
                return $"<h1>Unable to find {fullName}</h1>";
            }
        }

        public void Terminate()
        {
            if (_instance.Loader != null)
            {
                _instance.Loader.Terminate();
            }
 
            if (_trayIcon != null)
            {
                _trayIcon.Dispose();
            }
 
            if (MainView != null)
            {
                MainView.Close();
            }

            // 恢复自动休眠或关闭屏幕
            WIN32APIsWrapper.PreventSleep(true);

            System.Windows.Application.Current.Shutdown(0);

            Environment.Exit(0);
        }

        #endregion
    }
}
