using System;
using System.Data;
using System.Linq;
using System.Windows;
using System.Windows.Controls;
using System.IO;
using Elwig.Helpers;
using Elwig.Helpers.Weighing;
using System.Collections.Generic;
using System.Windows.Threading;
using System.Globalization;
using System.Threading;
using System.Windows.Markup;
using System.Reflection;

namespace Elwig {
    public partial class App : Application {

        public static readonly string DataPath = @"C:\ProgramData\Elwig\";
        public static readonly string ExePath = @"C:\Program Files\Elwig\";
        public static readonly string TempPath = Path.Combine(Path.GetTempPath(), "Elwig");
        public static readonly Config Config = new(DataPath + "config.ini");

        public static int VersionMajor { get; private set; }
        public static int VersionMinor { get; private set; }
        public static int VersionPatch { get; private set; }
        public static string Version {
            get => $"{VersionMajor}.{VersionMinor}.{VersionPatch}";
            private set {
                var p = value.Split(".").Select(p => int.Parse(p.Trim())).ToArray();
                VersionMajor = p.ElementAtOrDefault(0);
                VersionMinor = p.ElementAtOrDefault(1);
                VersionPatch = p.ElementAtOrDefault(2);
            }
        }

        public static string ZwstId { get; private set; }
        public static string BranchName { get; private set; }
        public static int? BranchPlz { get; private set; }
        public static string? BranchLocation { get; private set; }
        public static string? BranchAddress { get; private set; }
        public static string? BranchPhoneNr { get; private set; }
        public static string? BranchFaxNr { get; private set; }
        public static string? BranchMobileNr { get; private set; }
        public static IList<IScale> Scales { get; private set; }
        public static ClientParameters Client { get; private set; }

        public static bool IsPrintingReady => Documents.Html.IsReady && Documents.Pdf.IsReady;
        public static Dispatcher MainDispatcher { get; private set; }

        public App() : base() {
            System.Text.Encoding.RegisterProvider(System.Text.CodePagesEncodingProvider.Instance);
            Directory.CreateDirectory(TempPath);
            Directory.CreateDirectory(DataPath);
            MainDispatcher = Dispatcher;
            Scales = Array.Empty<IScale>();
        }

        protected override void OnStartup(StartupEventArgs evt) {
            var locale = new CultureInfo("de-AT");
            locale.NumberFormat.CurrencyGroupSeparator = "\u202f";
            locale.NumberFormat.NumberGroupSeparator = "\u202f";
            locale.NumberFormat.PercentGroupSeparator = "\u202f";
            Thread.CurrentThread.CurrentCulture = locale;
            Thread.CurrentThread.CurrentUICulture = locale;
            CultureInfo.DefaultThreadCurrentCulture = locale;
            CultureInfo.DefaultThreadCurrentUICulture = locale;
            FrameworkElement.LanguageProperty.OverrideMetadata(
                typeof(FrameworkElement),
                new FrameworkPropertyMetadata(XmlLanguage.GetLanguage(CultureInfo.CurrentCulture.IetfLanguageTag))
            );

            Version = typeof(App).GetTypeInfo().Assembly.GetCustomAttribute<AssemblyInformationalVersionAttribute>()?.InformationalVersion ?? "";

            try {
                AppDbUpdater.CheckDb();
            } catch (Exception e) {
                MessageBox.Show($"Invalid Database:\n\n{e.Message}", "Invalid Database", MessageBoxButton.OK, MessageBoxImage.Error);
                Shutdown();
                return;
            }

            Dictionary<string, (string, string, int?, string?, string?, string?, string?, string?)> branches = new();
            using (var ctx = new AppDbContext()) {
                branches = ctx.Branches.ToDictionary(b => b.Name.ToLower(), b => (b.ZwstId, b.Name, b.PostalDest?.AtPlz?.Plz, b.PostalDest?.AtPlz?.Ort.Name, b.Address, b.PhoneNr, b.FaxNr, b.MobileNr));
                try {
                    Client = new(ctx);
                } catch (Exception e) {
                    MessageBox.Show($"Fehler beim Laden der Mandantendaten:\n\n{e.Message}", "Fehler", MessageBoxButton.OK, MessageBoxImage.Error);
                    Shutdown();
                    return;
                }
            }

            Utils.RunBackground("HTML Initialization", () => Documents.Html.Init(PrintingReadyChanged));
            Utils.RunBackground("PDF Initialization", () => Documents.Pdf.Init(PrintingReadyChanged));

            var list = new List<IScale>();
            foreach (var s in Config.Scales) {
                var id = s[0];
                try {
                    var type = s[1]?.ToLower();
                    var model = s[2];
                    var cnx = s[3];
                    var empty = s[4];
                    var filling = s[5];
                    int? limit = s[6] == null ? null : int.Parse(s[6]);
                    var log = s[7];
                    if (type == "systec") {
                        list.Add(new SystecScale(id, model, cnx, empty, filling, limit, log));
                    } else {
                        throw new ArgumentException($"Invalid scale type: \"{type}\"");
                    }
                } catch (Exception e) {
                    list.Add(new InvalidScale(id));
                    MessageBox.Show($"Unable to create scale {s[0]}:\n\n{e.Message}", "Scale Error", MessageBoxButton.OK, MessageBoxImage.Error);
                }
            }
            Scales = list;

            if (Config.Branch != null) {
                if (!branches.ContainsKey(Config.Branch.ToLower())) {
                    MessageBox.Show("Invalid branch name in config!", "Invalid Branch Config", MessageBoxButton.OK, MessageBoxImage.Error);
                    Shutdown();
                } else {
                    var entry = branches[Config.Branch.ToLower()];
                    ZwstId = entry.Item1;
                    BranchName = entry.Item2;
                    BranchPlz = entry.Item3;
                    BranchLocation = entry.Item4;
                    BranchAddress = entry.Item5;
                    BranchPhoneNr = entry.Item6;
                    BranchFaxNr = entry.Item7;
                    BranchMobileNr = entry.Item8;
                }
            } else if (branches.Count == 1) {
                var entry = branches.First().Value;
                ZwstId = entry.Item1;
                BranchName = entry.Item2;
                BranchPlz = entry.Item3;
                BranchLocation = entry.Item4;
                BranchAddress = entry.Item5;
                BranchPhoneNr = entry.Item6;
                BranchFaxNr = entry.Item7;
                BranchMobileNr = entry.Item8;
            } else {
                MessageBox.Show("Unable to determine local branch!", "Invalid Branch Config", MessageBoxButton.OK, MessageBoxImage.Error);
                Shutdown();
            }

            base.OnStartup(evt);
        }

        private void PrintingReadyChanged() {
            Dispatcher.BeginInvoke(OnPrintingReadyChanged, new EventArgs());
        }

        protected void OnPrintingReadyChanged(EventArgs evt) {
            foreach (Window w in Windows) {
                foreach (var b in ControlUtils.FindAllChildren<Button>(w).Where(b => b.Tag?.ToString() == "Print")) {
                    b.IsEnabled = IsPrintingReady;
                }
                foreach (var i in ControlUtils.FindAllChildren<MenuItem>(w).Where(i => i.Tag?.ToString() == "Print")) {
                    i.IsEnabled = IsPrintingReady;
                }
            }
        }
    }
}