//******************************************************************************//
// App.xaml.cs
// 12/4/2023
//
// RAYTHEON PROPRIETARY: THIS DOCUMENT CONTAINS DATA OR INFORMATION
// PROPRIETARY TO RAYTHEON COMPANY AND IS RESTRICTED TO USE ONLY BY PERSONS
// AUTHORIZED BY RAYTHEON COMPANY IN WRITING TO USE IT. DISCLOSURE TO
// UNAUTHORIZED PERSONS WOULD LIKELY CAUSE SUBSTANTIAL COMPETITIVE HARM TO
// RAYTHEON COMPANY'S BUSINESS POSITION. NEITHER SAID DOCUMENT NOR ITS
// CONTENTS SHALL BE FURNISHED OR DISCLOSED TO OR COPIED OR USED BY PERSONS
// OUTSIDE RAYTHEON COMPANY WITHOUT THE EXPRESS WRITTEN APPROVAL OF RAYTHEON
// COMPANY.
//
// THIS PROPRIETARY NOTICE IS NOT APPLICABLE IF DELIVERED TO THE U.S. GOVERNMENT
//
// UNPUBLISHED WORK - COPYRIGHT RAYTHEON COMPANY.
//
// WARNING: THIS DOCUMENT CONTAINS TECHNICAL DATA AND / OR TECHNOLOGY WHOSE
// EXPORT OR DISCLOSURE TO NON-U.S. PERSONS, WHEREVER LOCATED, IS RESTRICTED
// BY THE INTERNATIONAL TRAFFIC IN ARMS REGULATIONS (ITAR) (22 C.F.R. SECTION
// 120-130) OR THE EXPORT ADMINISTRATION REGULATIONS (EAR) (15 C.F.R. SECTION
// 730-774). THIS DOCUMENT CANNOT BE EXPORTED (E.G., PROVIDED TO A SUPPLIER
// OUTSIDE OF THE UNITED STATES) OR DISCLOSED TO A NON-U.S. PERSON, WHEREVER
// LOCATED, UNTIL A FINAL JURISDICTION AND CLASSIFICATION DETERMINATION HAS
// BEEN COMPLETED AND APPROVED BY RAYTHEON, AND ANY REQUIRED U.S. GOVERNMENT
// APPROVALS HAVE BEEN OBTAINED. VIOLATIONS ARE SUBJECT TO SEVERE CRIMINAL
// PENALTIES.
//
// DOD 5220.22-M, INDUSTRIAL SECURITY MANUAL, CHAPTER 5, SECTION 1 THROUGH 9 :
// FOR CLASSIFIED DOCUMENTS FOLLOW THE PROCEDURES IN OR DOD 5200.1-R,
// INFORMATION SECURITY PROGRAM, CHAPTER 6. FOR UNCLASSIFIED, LIMITED DOCUMENTS
// DESTROY BY ANY  METHOD THAT WILL PREVENT DISCLOSURE OF CONTENTS OR
// RECONSTRUCTION OF THE DOCUMENT.
// POC: Alex Kravchenko (1118268)
//******************************************************************************//
using Microsoft.Win32;
using NLog;
using Raytheon.LogDashboard.Helpers;
using System;
using System.Diagnostics;
using System.Linq;
using System.Runtime.InteropServices;
using System.Text;
using System.Threading;
using System.Windows;
using System.IO;
using Raytheon.LogDashboard.View;
using GalaSoft.MvvmLight.Messaging;
using System.Reflection;

namespace LogDashboard
{
    /// <summary>
    /// Interaction logic for App.xaml
    /// </summary>
    public partial class App : Application
    {
        private static readonly Logger logger = LogManager.GetCurrentClassLogger();

        public static bool IsManualStartup { get; private set; } = false;
        public ResourceDictionary ThemeDictionary => Resources.MergedDictionaries[1];

        public void ChangeTheme(Uri uri)
        {
            ThemeDictionary.MergedDictionaries[1] = new ResourceDictionary { Source = uri };
        }

        static Mutex mutex = new Mutex(true, "{8F6F0AC4-B9A1-45fd-A8CF-72F04E6BDE8F}");

        [STAThread]
        protected override void OnStartup(StartupEventArgs e)
        {
            AppDomain.CurrentDomain.UnhandledException += CurrentDomain_UnhandledException;
            Current.DispatcherUnhandledException += Current_DispatcherUnhandledException;

            if(e.Args.Any(x => (x.EndsWith(".txt") || x.EndsWith(".log")) && File.Exists(x)))
            {
                IsManualStartup = true;

                try
                {
                    if(!mutex.WaitOne(TimeSpan.Zero, true))
                    {
                        Process proc = Process.GetCurrentProcess();
                        string processName = proc.ProcessName.Replace(".vshost", "");
                        var runningProcess = Process.GetProcesses()
                            .FirstOrDefault(x => (x.ProcessName == processName || x.ProcessName == proc.ProcessName || x.ProcessName == proc.ProcessName + ".vshost") && x.Id != proc.Id);

                        if(runningProcess == null)
                        {
                            App app = new App();
                            app.InitializeComponent();
                            MainWindow window = new MainWindow();
                            Raytheon.LogDashboard.View.MainWindow.HandleParameter(e.Args);
                            app.Run(window);
                            return;
                        }

                        UnsafeNative.SendMessage(runningProcess.MainWindowHandle, string.Join(" ", e.Args));

                        Environment.Exit(0);
                    }
                }
                catch(Exception exception)
                {
                    logger.Warn(exception, "OnStartup check exception (open with arguments)");
                }
            }

         string assemblyFolder = Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location);
         string logDashboardConfigFilePath = Path.Combine(assemblyFolder, "LogDashboard.xml");
         // load settings from configuration file
         Raytheon.LogDashboard.Model.Settings.SetInstance(logDashboardConfigFilePath).Load();

            if(Raytheon.LogDashboard.Model.Settings.Instance.OnlyOneAppInstance)
            {
                try
                {
                    if(mutex.WaitOne(TimeSpan.Zero, true))
                    {
                        mutex.ReleaseMutex();
                    }
                    else
                    {
                        logger.Trace("OnStartup: one instance of Log Viewer already started");
                        Messenger.Default.Send("OnStartup: one instance of Log Viewer already started", "DisplayCustomMessage");
                        Environment.Exit(0);
                    }
                }
                catch(Exception exception)
                {
                    logger.Warn(exception, "OnStartup check exception");
                }
            }

            try
            {
                if(!IsAssociated())
                {
                    string filePath = Process.GetCurrentProcess().MainModule.FileName;

                    SetAssociation(".txt", "LogDashboard", filePath);
                    SetAssociation(".log", "LogDashboard", filePath);
                    SetAssociation(".csv", "LogDashboard", filePath);
                }
            }
            catch(Exception exception)
            {
                logger.Warn(exception, "OnStartup set file association exception");
            }

            base.OnStartup(e);
        }
        private void CurrentDomain_UnhandledException(object sender, UnhandledExceptionEventArgs e)
        {
            logger.Fatal($"Unhandled exception {e.ExceptionObject as Exception}");
            Environment.Exit(1);
        }

        private void Current_DispatcherUnhandledException(object sender, System.Windows.Threading.DispatcherUnhandledExceptionEventArgs e)
        {
            logger.Fatal($"Unhandled Exception: {e.Exception}");
            Environment.Exit(2);
        }

        #region File Association

        private bool IsAssociated()
        {
            return Registry.CurrentUser.OpenSubKey(@"Software\Classes\LogViewer", false) != null;
        }

        [DllImport("Shell32.dll")]
        private static extern int SHChangeNotify(int eventId, int flags, IntPtr item1, IntPtr item2);

        private const int SHCNE_ASSOCCHANGED = 0x8000000;
        private const int SHCNF_FLUSH = 0x1000;

        private void SetAssociation(string extension, string progId, string applicationFilePath)
        {
            bool madeChanges = false;

            madeChanges |= SetKeyDefaultValue($@"Software\Classes\{progId}\shell\open\command", "\"" + applicationFilePath + "\" \"%1\"");
            madeChanges |= SetProgIdValue($@"Software\Microsoft\Windows\CurrentVersion\Explorer\FileExts\{extension}\OpenWithProgids", progId);

            if(madeChanges)
            {
                SHChangeNotify(SHCNE_ASSOCCHANGED, SHCNF_FLUSH, IntPtr.Zero, IntPtr.Zero);
            }
        }

        private bool SetProgIdValue(string path, string progId)
        {
            using(var key = Registry.CurrentUser.CreateSubKey(path))
            {
                if(key.GetValueNames().All(x => x != progId))
                {
                    key.SetValue(progId, Encoding.Unicode.GetBytes(string.Empty), RegistryValueKind.Binary);
                    return true;
                }
            }
            return false;
        }

        private bool SetKeyDefaultValue(string keyPath, string value)
        {
            using(var key = Registry.CurrentUser.CreateSubKey(keyPath))
            {
                if(key.GetValue(null) as string != value)
                {
                    key.SetValue(null, value);
                    return true;
                }
            }
            return false;
        }

        #endregion

    }
}
