using System;
using System.Collections.Generic;
using System.Linq;
using System.Reflection;
using Edge.Core.Processor;
using Edge.Core.Database;
using System.Diagnostics;
using System.Threading;
using System.Xml;
using System.Security.Cryptography;
using System.Text;
using System.Runtime.InteropServices;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Logging;
using LiteFccCoreMain.Service;
using System.IO.Ports;
using System.Runtime.Loader;
using System.Threading.Tasks;
using System.IO;
using MQTTnet.Server;
using MQTTnet.Protocol;
using MQTTnet.Diagnostics;
using Microsoft.EntityFrameworkCore;
using Edge.Core.Configuration;
using System.Text.RegularExpressions;
using Edge.Core.Processor.Dispatcher;
using Edge.Core.UniversalApi;

namespace LiteFccCoreMain
{
    class Program
    {
        static ILogger mainLogger;//= NLog.LogManager.LoadConfiguration("nlog.config").GetLogger("Main");
        static DefaultDispatcher processorsDispatcher = null;
        public static async Task Main(string[] args)
        {
            Console.WriteLine("LiteFccCore is starting...");
            var services = ServiceBuilder.Build();
            var loggerFactory = services.GetRequiredService<ILoggerFactory>();
            mainLogger = loggerFactory.CreateLogger("Main");

            string coreVersionStr = $"{Assembly.GetAssembly(typeof(IProcessor)).GetName().Version.ToString()}(core), " +
                $"{Assembly.GetAssembly(typeof(Program)).GetName().Version.ToString()}(entry)";

            IEnumerable<IProcessor> processorInstances = null;


            #region pre-Setup

            AppDomain.CurrentDomain.UnhandledException += (sender, arg) =>
            {
                mainLogger.LogError(string.Format("Unhandled exception from AppDomain.UnhandledException event:\r\n {0}", arg.ExceptionObject));
                NLog.LogManager.Shutdown();
            };
            AppDomain.CurrentDomain.ProcessExit += (s, a) =>
            {
                mainLogger.LogCritical("!!!!!!            Process is exiting...           !!!!!!");
                Console.WriteLine("Stopping processors...");
                mainLogger.LogInformation("Stopping processors...");
                var stopResults = processorsDispatcher?.StopProcessorsAsync(processorInstances, "Process is Exiting...")?.Result;
                Console.WriteLine("Stopped processors.");
                mainLogger.LogInformation("Stopped processors." + Environment.NewLine + Environment.NewLine + Environment.NewLine);
                NLog.LogManager.Shutdown();
            };
            Console.CancelKeyPress += (sender, arg) =>
            {
                Console.WriteLine("Cancel key: " + arg.SpecialKey + " pressed, will exit whole application...");
                mainLogger.LogInformation("Cancel key: " + arg.SpecialKey + " pressed, will exit whole application...");
                //shutdown the while LiteFccCore process
                Environment.Exit(-1);
            };
            Console.WriteLine($"Version: {coreVersionStr}");
            mainLogger.LogInformation($"Version: {coreVersionStr}");

            PerformanceMonitor.Logger = loggerFactory.CreateLogger("Performance");
            PerformanceMonitor.Start();

            #endregion

            var configurator = services.GetService<Configurator>();

            #region Migrating database

            mainLogger.LogInformation("Migrating database...");
            Console.WriteLine("Migrating database...");
            var migrateDbContext = services.GetRequiredService<SqliteDbContext>();
            try
            {
                migrateDbContext.Database.Migrate();
            }
            catch (Exception exx)
            {
                string migrationsStr = "";
                string pendingMigrationsStr = "";
                string appliedMigrationsStr = "";
                try
                {
                    migrationsStr = migrateDbContext.Database?.GetMigrations()?.Aggregate((acc, n) => acc + ", " + n) ?? "";
                    pendingMigrationsStr = migrateDbContext.Database?.GetPendingMigrations()?.Aggregate((acc, n) => acc + ", " + n) ?? "";
                    appliedMigrationsStr = migrateDbContext.Database?.GetAppliedMigrations()?.Aggregate((acc, n) => acc + ", " + n) ?? "";
                }
                catch
                {
                    mainLogger.LogError("Starting LiteFccCore Exceptioned when Migrating the database, detail: " + exx + System.Environment.NewLine +
                    "migrations are: " + migrationsStr + System.Environment.NewLine +
                    ", pendingMigrations are: " + pendingMigrationsStr + System.Environment.NewLine +
                    ", appliedMigrations are: " + appliedMigrationsStr);
                }

                throw new InvalidOperationException("Starting LiteFccCore Exceptioned when Migrating the database");
            }

            mainLogger.LogInformation("     Migrate finished.");
            Console.WriteLine($"     Migrate finished.{Environment.NewLine}");

            #endregion

            try
            {
                processorsDispatcher = new DefaultDispatcher(services);
                IEnumerable<ProcessorInstanceOperatingResult> instantiateResults = null;
                if (args?.Any(g => g.Equals("--enable-safeboot", StringComparison.OrdinalIgnoreCase)) ?? false)
                {
                    mainLogger.LogInformation("Enabling Safe Boot, Only Loading Core Moduels...");
                    Console.BackgroundColor = ConsoleColor.Yellow;
                    Console.ForegroundColor = ConsoleColor.Black;
                    Console.WriteLine("Enabling Safe Boot, Only Loading Core Moduels...");
                    Console.ResetColor();
                    instantiateResults = await processorsDispatcher.CreateProcessorsAsync("Main starting with enabling SafeBoot", true);
                }
                else
                    instantiateResults = await processorsDispatcher.CreateProcessorsAsync("Main starting", false);
                var startResults = await processorsDispatcher.StartProcessorsAsync(
                    instantiateResults.Where(r => r.Succeed).Select(r => r.ProcessorInstance),
                    "Main starting");
                processorInstances = startResults.Select(r => r.ProcessorInstance);
                processorsDispatcher.OnProcessorInstantiated += (__, arg) =>
                {
                    processorInstances = arg.OperationResults.Select(r => r.ProcessorInstance);
                    instantiateResults = null;
                    startResults = null;
                };
                mainLogger.LogInformation("LiteFccCore is running...");
                Console.BackgroundColor = ConsoleColor.Green;
                Console.ForegroundColor = ConsoleColor.Black;
                Console.WriteLine($"LiteFccCore with version: {coreVersionStr} is running...");
                Console.ResetColor();
            }
            catch (Exception exx)
            {
                mainLogger.LogError("******Start LiteFccCore internal error!\r\n" + exx.ToString());
                Console.BackgroundColor = ConsoleColor.Red;
                Console.ForegroundColor = ConsoleColor.Black;
                Console.WriteLine("******Start LiteFccCore internal error: " + exx.ToString().Substring(0, 70) + "...");
                Console.ResetColor();
            }

            while (true)
            {
                // in ARM Linux deamon mode, it always can read an empty line, here have to sleep to avoid quit.
                Thread.Sleep(1000);
                var read = Console.ReadLine();
            }
        }
    }
}