using Microsoft.Win32;
using System;
using System.Diagnostics;
using System.Windows.Forms;
#if !DEBUG
using System.IO;
using System.Threading;
#endif

namespace PKHeX.WinForms
{
    internal static class Program
    {
        /// <summary>
        /// The main entry point for the application.
        /// </summary>
        [STAThread]
        private static void Main()
        {
#if !DEBUG
            // Add the event handler for handling UI thread exceptions to the event.
            Application.ThreadException += UIThreadException;

            // Set the unhandled exception mode to force all Windows Forms errors to go through our handler.
            Application.SetUnhandledExceptionMode(UnhandledExceptionMode.CatchException);

            // Add the event handler for handling non-UI thread exceptions to the event.
            AppDomain.CurrentDomain.UnhandledException += CurrentDomain_UnhandledException;
#endif
            if (!CheckNETFramework())
                return;
            // Run the application
            Application.EnableVisualStyles();
            Application.SetCompatibleTextRenderingDefault(false);
            Application.Run(new Main());
        }

        private static bool CheckNETFramework()
        {
            if (IsOnWindows())
            {
                #if MONO
                Error("Mono version should not be used on a Windows system.");
                #endif
                if (GetFrameworkVersion() >= 393295)
                    return true;
                Error(".NET Framework 4.6 needs to be installed for this version of PKHeX to run.");
                Process.Start("https://www.microsoft.com/download/details.aspx?id=48130");
                return false;
            }

            //CLR Version 4.0.30319.42000 is equivalent to .NET Framework version 4.6
            if (Environment.Version.CompareTo(Version.Parse("4.0.30319.42000")) >= 0)
                return true;
            Error("Your version of Mono needs to target the .NET Framework 4.6 or higher for this version of PKHeX to run.");
            return false;
        }
        private static bool IsOnWindows()
        {
            // 4 -> UNIX, 6 -> Mac OSX, 128 -> UNIX (old)
            int p = (int)Environment.OSVersion.Platform;
            return p != 4 && p != 6 && p != 128;
        }
        private static int GetFrameworkVersion()
        {
            const string subkey = @"SOFTWARE\Microsoft\NET Framework Setup\NDP\v4\Full\";
            using (RegistryKey ndpKey = RegistryKey.OpenBaseKey(RegistryHive.LocalMachine, RegistryView.Registry32).OpenSubKey(subkey))
            {
                if (ndpKey == null)
                    return 0;
                int releaseKey = (int)ndpKey.GetValue("Release");
                return releaseKey;
            }
        }
        private static void Error(string msg) => MessageBox.Show(msg, "PKHeX Error", MessageBoxButtons.OK, MessageBoxIcon.Stop);

#if !DEBUG
        // Handle the UI exceptions by showing a dialog box, and asking the user whether or not they wish to abort execution.
        private static void UIThreadException(object sender, ThreadExceptionEventArgs t)
        {
            DialogResult result = DialogResult.Cancel;
            try
            {
                result = ErrorWindow.ShowErrorDialog("An unhandled exception has occurred.\nYou can continue running PKHeX, but please report this error.", t.Exception, true);
            }
            catch (Exception reportingException)
            {
                HandleReportingException(t.Exception, reportingException);
            }

            // Exits the program when the user clicks Abort.
            if (result == DialogResult.Abort)
                Application.Exit();
        }

        // Handle the UI exceptions by showing a dialog box, and asking the user whether
        // or not they wish to abort execution.
        // NOTE: This exception cannot be kept from terminating the application - it can only
        // log the event, and inform the user about it.
        private static void CurrentDomain_UnhandledException(object sender, UnhandledExceptionEventArgs e)
        {
            var ex = e.ExceptionObject as Exception;
            try
            {
                if (ex != null)
                {
                    ErrorWindow.ShowErrorDialog("An unhandled exception has occurred.\nPKHeX must now close.", ex, false);
                }
                else
                {
                    Error("A fatal non-UI error has occurred in PKHeX, and the details could not be displayed.  Please report this to the author.");
                }
            }
            catch (Exception reportingException)
            {
                HandleReportingException(ex, reportingException);
            }
        }

        private static void HandleReportingException(Exception ex, Exception reportingException)
        {
            if (reportingException is FileNotFoundException x && x.FileName.StartsWith("PKHeX.Core"))
            {
                Error("Could not locate PKHeX.Core.dll. Make sure you're running PKHeX together with its code library. Usually caused when all files are not extracted.");
                return;
            }
            try
            {
                Error("A fatal non-UI error has occurred in PKHeX, and there was a problem displaying the details.  Please report this to the author.");
                EmergencyErrorLog(ex, reportingException);
            }
            finally
            {
                Application.Exit();
            }
        }

        /// <summary>
        /// Attempt to log exceptions to a file when there's an error displaying exception details.
        /// </summary>
        /// <param name="originalException"></param>
        /// <param name="errorHandlingException"></param>
        private static bool EmergencyErrorLog(Exception originalException, Exception errorHandlingException)
        {
            try
            {
                // Not using a string builder because something's very wrong, and we don't want to make things worse
                var message = (originalException?.ToString() ?? "null first exception") + Environment.NewLine + errorHandlingException;
                File.WriteAllText($"PKHeX_Error_Report {DateTime.Now:yyyyMMddHHmmss}.txt", message);
            }
            catch (Exception)
            {
                // We've failed to save the error details twice now. There's nothing else we can do.
                return false;
            }
            return true;
        }
#endif
    }
}