using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.ComponentModel;
using System.Configuration;
using System.Linq;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
using System.Windows.Input;
using System.Windows.Threading;
using System.Xml;
using static Shafer;

namespace ShaferExample
{
    /// <summary>
    /// Interaction logic for AppWindow.xaml
    /// </summary>
    public partial class AppWindow : Window
    {
        public AppWindow()
        {
            InitializeComponent();
            _timer.Interval = TimeSpan.FromSeconds(1);
            _timer.Tick += Tick;
            DataContext = this;
            AppName.Text = App.AppName;
            AppDesc.Text = App.AppDesc;
        }

        private void WindowLoaded(object sender, RoutedEventArgs e)
        {
            CheckLicense(false);
            _fields = new Dictionary<string, TextBox> {
                { "company", tbCompanyName },
                { "firstname", tbFirstname },
                { "lastname", tbLastName },
                { "telephone", tbTelephone },
                { "email", tbEmail },
            };
            _popupFields = new Dictionary<string, TextBox> {
                { "company", tbCompanyNamePopup },
                { "firstname", tbFirstnamePopup },
                { "lastname", tbLastNamePopup },
                { "telephone", tbTelephonePopup },
                { "email", tbEmailPopup },
            };
            SetTabs();
            ShowVersionInfo();
            if (App.Licensing.PassiveMode)
            {
                LoadFeatureExpiration();
                LoadFeatures();
                LoadAgilities();
                SetActivationsFromAgilityField();
                SetComputerName();

            }
            else
            {
                LoadFeatureExpiration();
                LoadMessages();
                LoadFeatures();
                LoadAgilities();
                LoadElementPool();
                LoadConsumptionToken();
                LoadFloatingFeatures();
                SetComputerName();
                SetActivationsFromAgilityField();
                ReadRegistrationData(_fields);
                App.Licensing.ApplicationStart();
                ShowOverdraftInfo();
                ShowRegPopupIfNeeded();
                WinSparkle.Init();
            }
        }

        private void Window_MouseDown(object sender, MouseButtonEventArgs e)
        {
            if (e.ChangedButton == MouseButton.Left)
            {
                this.DragMove();
            }
        }

        private void Window_Close(object sender, MouseButtonEventArgs e)
        {
            App.Current.Shutdown();
        }

        private void ShowRegPopupIfNeeded()
        {
            if (App.Licensing.ShouldShowRegPopup())
            {
                popRegistrationData.IsOpen = true;
            }
        }

        private void ShowVersionInfo()
        {
            if (App.Licensing.ShowVersionInfo())
            {
                versionOpacity.Visibility = Visibility.Visible;
                var result = MessageBox.Show($"Your license does not permit you to run Version {AppUtils.GetVersion()} of the Elevate application. " +
                    "Please switch to a version you are permitted to run or purchase an upgraded license from your Elevate sales representative.");
                if (result == MessageBoxResult.OK)
                {
                    ReturnLicense(null, null);
                }
            }
        }

        private void ShowOverdraftInfo()
        {
            if (App.Licensing.ShowOverdraftInfo())
            {
                MessageBox.Show("Note that you are using an overdraft license. Please contact your Elevate account manager to ensure you have enough paid licenses.");
            }
        }

        private void SetTabs()
        {
            if (App.Licensing.PassiveMode)
            {
                tabCT.IsEnabled = false;
                tabEP.IsEnabled = false;
                tabFF.IsEnabled = false;
                tabRD.IsEnabled = false;
            }
            else if (App.Licensing.LanDeamonMode)
            {
                tabCT.IsEnabled = false;
            }
        }

        private void CheckLicense(bool refresh = true)
        {
            if (refresh) { App.Licensing.RefreshLicenseStatus(); };
            if (App.Licensing.LicenseStatus != LicenseStatus.PROD_INTRIAL && !App.Licensing.LicenseAcquired)
            {
                RunInUiThread(() => OpenMainWindow());
            }
            else
            {
                ShowLicenseStatus(App.Licensing.LicenseStatus);
            }
        }

        private void OpenMainWindow()
        {
            new RegistrationWindow().Show();
            this.Close();
        }

        private void SetComputerId()
        {
            var computerId = App.Licensing.GetComputerId();
            RunInUiThread(() => tbComputerId.Text = computerId);
        }

        private void SetLicenseStatus()
        {
            RunInUiThread(() => tbLicenseStatus.Text = App.Licensing.LicenseStatus.ToString());
        }

        private void SetLicenseCode(LicenseStatus licenseStatus)
        {
            var code = licenseStatus == LicenseStatus.PROD_INTRIAL ? "Trial License" : App.Licensing.GetLicenseCode();
            RunInUiThread(() => tbLicenseCode.Text = code);
        }

        private void SetExpiryDate()
        {
            _leaseIn = null;
            if (App.Licensing.PassiveMode)
            {
                RunInUiThread(() =>
                {
                    tbLease.Text = NA;
                    tbLeaseExpiredIn.Text = NA;
                });
            }
            else
            {
                string enddate = App.Licensing.GetLeaseExpiryDate();
                int seconds = App.Licensing.GetLeaseExpirySecs();
                RunInUiThread(() => tbLease.Text = enddate);
                _leaseIn = seconds;
            }
        }

        private void ReturnLicense(object sender, RoutedEventArgs e)
        {
            App.Licensing.ReturnLicense();
            CheckLicense();
        }

        private void ReturnPassiveLicense(object sender, RoutedEventArgs e)
        {
            App.Licensing.RemoveFiles();
            CheckLicense();
        }

        private void ReturnLicenseOffline(object sender, RoutedEventArgs e)
        {
            var cert = App.Licensing.GetDeactivationCert();
            Clipboard.SetText(cert);
            MessageBox.Show("Deactivation certificate has been copied to clipboard");
            CheckLicense();
        }

        private string SetLicenseInfo()
        {
            var licenseType = string.Empty;
            var activationType = string.Empty;

            if (App.Licensing.PassiveMode)
            {
                licenseType = "Perpetual";
                activationType = "Passive";
            }
            else
            {
                App.Licensing.GetLicenseInfo(ref licenseType, ref activationType);
            }
            RunInUiThread(() =>
            {
                tbLicenseType.Text = licenseType;
                tbActivationType.Text = activationType;
            });
            return licenseType;
        }

        private void SetActivationsFromAgilityField()
        {
            RunInUiThread(() =>
            {
                tbCurrentActivations.Text = App.Licensing.GetAgility("$CurrentActivations");
                tbActivationsAllowed.Text = App.Licensing.GetAgility("$ActivationsAllowed");
            });
        }

        private void SetActivations()
        {
            var result = App.Licensing.GetNumbAvailProc();
            int MaxActivations = result.Max;
            int AvailActivations = result.Available;
            RunInUiThread(() =>
            {
                tbAvActivations.Text = AvailActivations.ToString();
                tbMaxActivations.Text = MaxActivations.ToString();
            });
        }

        private void RefreshLicense(object sender, RoutedEventArgs e)
        {
            BackgroundWorker worker = new BackgroundWorker();
            worker.DoWork += (object workSender, DoWorkEventArgs workE) =>
            {
                CheckLicense();
                LoadFeatureExpiration();
                LoadMessages();
                LoadAgilities();
                LoadFeatures();
                RefreshFloatingFeatures();
                LoadElementPool();
                LoadConsumptionToken();
            };
            worker.RunWorkerCompleted += (object completeSender, RunWorkerCompletedEventArgs completeE) =>
            {
                Loader.Visibility = Visibility.Hidden;
            };
            Loader.Visibility = Visibility.Visible;
            worker.RunWorkerAsync();
        }

        private void SetMaintenanceExpiryDate()
        {
            _maintenenceIn = null;
            if (App.Licensing.PassiveMode)
            {
                RunInUiThread(() =>
                {
                    tbMaintenenceExpiryDateIn.Text = NA;
                    tbMaintenenceExpiryDate.Text = NA;
                });
            }
            else
            {
                string med = App.Licensing.GetMaintenanceExpiryDate();
                if (med != null)
                {
                    RunInUiThread(() => tbMaintenenceExpiryDate.Text = med);
                    _maintenenceIn = App.Licensing.GetMaintenanceExpirySecs();
                }
                else
                {
                    RunInUiThread(() => tbMaintenenceExpiryDate.Text = "None");
                    _maintenenceIn = null;
                }
            }
        }

        private void ShowLicenseStatus(LicenseStatus licenseStatus)
        {
            _timer.Stop();
            SetComputerId();
            SetLicenseStatus();
            SetActivations();
            SetExpiryDate();
            SetMaintenanceExpiryDate();

            SetLicenseCode(licenseStatus);
            SetActivationsFromAgilityField();
            var licenseType = SetLicenseInfo();

            RunInUiThread(() =>
            {
                btnReturnPassiveLicense.Visibility = Visibility.Collapsed;
                btnRegisterNow.Visibility = Visibility.Collapsed;
                btnReturnLicenseOffline.Visibility = Visibility.Visible;
                btnReturnLicense.Visibility = Visibility.Visible;
                btnLtco.Visibility = Visibility.Visible;

                if (App.Licensing.LanDeamonMode)
                {
                    btnLtco.Visibility = Visibility.Collapsed;
                }

                if (App.Licensing.PassiveMode)
                {
                    btnReturnPassiveLicense.Visibility = Visibility.Visible;
                    btnLtco.Visibility = Visibility.Collapsed;
                    btnReturnLicense.Visibility = Visibility.Collapsed;
                    btnReturnLicenseOffline.Visibility = Visibility.Collapsed;
                }
            });


            //Show info in trial
            if (licenseStatus == Shafer.LicenseStatus.PROD_INTRIAL)
            {
                RunInUiThread(() => tbExpires.Text = App.Licensing.GetTrialExpiryDate());
                _expiresIn = App.Licensing.GetTrialExpirySecs();
                _timer.Start();
                RunInUiThread(() =>
                {
                    btnRegisterNow.Visibility = Visibility.Visible;
                    btnReturnLicenseOffline.Visibility = Visibility.Collapsed;
                    btnReturnLicense.Visibility = Visibility.Collapsed;
                    btnLtco.Visibility = Visibility.Collapsed;
                });
            }
            else if (licenseType.Contains("Subscription"))
            {
                RunInUiThread(() => tbExpires.Text = App.Licensing.GetSubscriptionExpiryDate());
                _expiresIn = App.Licensing.GetSubscriptionExpirySecs();
            }
            else
            {
                RunInUiThread(() => tbExpires.Text = "Perpetual");
                RunInUiThread(() => tbExpiresIn.Text = NA);
                _expiresIn = null;
            }
            _timer.Start();

        }

        private void Tick(object sender, EventArgs e)
        {
            if (_expiresIn.HasValue)
            {
                _expiresIn = _expiresIn - 1;
                tbExpiresIn.Text = AppUtils.ExpiresIn(_expiresIn.Value);
            }
            else
            {
                tbExpiresIn.Text = null;
            }

            if (_leaseIn.HasValue)
            {
                _leaseIn = _leaseIn - 1;
                tbLeaseExpiredIn.Text = AppUtils.ExpiresIn(_leaseIn.Value);
            }
            else
            {
                tbLeaseExpiredIn.Text = null;
            }

            if (_maintenenceIn.HasValue)
            {
                _maintenenceIn = _maintenenceIn - 1;
                WinSparkle.Setup(_maintenenceIn);
                tbMaintenenceExpiryDateIn.Text = AppUtils.ExpiresIn(_maintenenceIn.Value);
            }
            else
            {
                tbMaintenenceExpiryDateIn.Text = null;
            }
        }

        private void EditRegistrationData(object sender, RoutedEventArgs e)
        {
            OpenRegistrationDataPopup();
        }

        private void OpenRegistrationDataPopup()
        {
            ReadRegistrationData(_popupFields);
            popRegistrationData.IsOpen = true;
        }

        private void CloseRegistrationDataPopup(object sender, RoutedEventArgs e)
        {
            popRegistrationData.IsOpen = false;
        }

        private void SaveRegistrationData(object sender, RoutedEventArgs e)
        {
            XmlDocument registrationFile = new XmlDocument(); // Create XML document to save form data to 
            var form = new RegistrationForm();

            form.CompanyName = tbCompanyNamePopup.Text;
            form.Email = tbEmailPopup.Text;
            form.FirstName = tbFirstnamePopup.Text;
            form.LastName = tbLastNamePopup.Text;
            form.Telephone = tbTelephonePopup.Text;

            BackgroundWorker worker = new BackgroundWorker();
            worker.DoWork += (object workSender, DoWorkEventArgs workE) =>
            {
                registrationFile = App.Licensing.CreateRegistrationFile(form);
                registrationFile.Save("RegFile.xml");

                CheckLicense();
            };
            worker.RunWorkerCompleted += (object completeSender, RunWorkerCompletedEventArgs completeE) =>
            {
                ReadRegistrationData(_fields);
                MessageBox.Show("Registration data saved successful.");
                Loader.Visibility = Visibility.Hidden;
            };
            popRegistrationData.IsOpen = false;
            Loader.Visibility = Visibility.Visible;
            worker.RunWorkerAsync();
        }

        private bool ReadRegistrationData(Dictionary<string, TextBox> fieldsList)
        {
            var result = false;
            XmlDocument registrationFile = new XmlDocument();
            var regData = App.Licensing.GetRegData();
            if (regData == string.Empty) { return false; }
            registrationFile.LoadXml(regData);
            var xmlnode = registrationFile.GetElementsByTagName("clientinfo");
            if (xmlnode.Count == 1)
            {
                result = true;
                var clientInfo = xmlnode[0];
                foreach (XmlNode node in clientInfo.ChildNodes)
                {
                    if (fieldsList.ContainsKey(node.Name))
                    {
                        fieldsList[node.Name].Text = AppUtils.GetNodeValue(node);
                    }
                }
            }
            return result;
        }

        private void LoadFeatureExpiration()
        {
            var expirations = App.Licensing.GetAgility("FeatureExpirations");
            ExpirationInfo = new FeatureExpirationInfo(expirations);
        }

        private void LoadMessages()
        {
            var messages = App.Licensing.GetAgility("Messages");
            var newMessages = new ObservableCollection<Message>(new MessagesInfo(messages).CurrentMessages);
            RunInUiThread(() =>
            {
                Messages.Clear();
                foreach (var newMsg in newMessages)
                {
                    Messages.Add(newMsg);
                }
                CollectionViewSource.GetDefaultView(Messages).Refresh();
            });
        }

        private void LoadAgilities()
        {
            RunInUiThread(() =>
            {
                Agilities.Clear();
                var agilities = ConfigurationManager.AppSettings["agilities"];
                foreach (var agilityName in agilities.Split(new[] { ";" }, StringSplitOptions.RemoveEmptyEntries))
                {
                    Agilities.Add(new Agility() { Name = agilityName, Value = App.Licensing.GetAgility(agilityName) });
                }
            });
        }

        private void LoadFeatures()
        {
            RunInUiThread(() =>
            {
                Features.Clear();
                var features = ConfigurationManager.AppSettings["features"];
                foreach (var featureName in features.Split(new[] { ";" }, StringSplitOptions.RemoveEmptyEntries))
                {
                    Features.Add(new Feature()
                    {
                        Name = featureName,
                        Value = App.Licensing.GetFeature(featureName),
                        Expiration = ExpirationInfo.GetFeatureExpirationDate(featureName)
                    });
                }
                CollectionViewSource.GetDefaultView(Features).Refresh();
            });
        }

        private void LoadFloatingFeatures()
        {
            RunInUiThread(() =>
            {
                var elements = ConfigurationManager.AppSettings["floatingFeature"];
                foreach (var featureName in elements.Split(new[] { ";" }, StringSplitOptions.RemoveEmptyEntries))
                {
                    FloatingFeatures.Add(new Feature()
                    {
                        Name = featureName,
                        Expiration = ExpirationInfo.GetFloatingFeatureExpirationDate(featureName)
                    });
                }
                CollectionViewSource.GetDefaultView(FloatingFeatures).Refresh();
            });
        }

        private void RefreshFloatingFeatures()
        {
            RunInUiThread(() =>
            {
                foreach (var feature in FloatingFeatures)
                {
                    feature.Expiration = ExpirationInfo.GetFloatingFeatureExpirationDate(feature.Name);
                }
                CollectionViewSource.GetDefaultView(FloatingFeatures).Refresh();
            });
        }

        private void LoadElementPool()
        {
            RunInUiThread(() =>
            {
                var elements = ConfigurationManager.AppSettings["elementPool"];
                foreach (var element in elements.Split(new[] { ";" }, StringSplitOptions.RemoveEmptyEntries))
                {
                    if (ElementsPool.Where(x => x.Name == element).Any())
                    {
                        ElementsPool.Where(x => x.Name == element).First().Copy(App.Licensing.GetPoolInfo(element));
                    }
                    else
                    {
                        ElementsPool.Add(App.Licensing.GetPoolInfo(element));
                    }
                }
                CollectionViewSource.GetDefaultView(ElementsPool).Refresh();
            });
        }

        private void LoadConsumptionToken()
        {
            RunInUiThread(() =>
            {
                var elements = ConfigurationManager.AppSettings["consumptionToken"];
                foreach (var element in elements.Split(new[] { ";" }, StringSplitOptions.RemoveEmptyEntries))
                {
                    if (ConsumptionTokens.Where(x => x.Name == element).Any())
                    {
                        ConsumptionTokens.Where(x => x.Name == element).First().Copy(App.Licensing.GetTokenInfo(element));
                    }
                    else
                    {
                        ConsumptionTokens.Add(App.Licensing.GetTokenInfo(element));
                    }
                }
                CollectionViewSource.GetDefaultView(ConsumptionTokens).Refresh();
            });
        }

        private void RegisterNow(object sender, RoutedEventArgs e)
        {
            OpenMainWindow();
        }

        private void CheckoutFloatingFeature(object sender, RoutedEventArgs e)
        {
            FloatingFeatureAction(true, App.Licensing.CheckoutFloatingFeature);
        }

        private void CheckInFloatingFeature(object sender, RoutedEventArgs e)
        {
            FloatingFeatureAction(false, App.Licensing.CheckinFloatingFeature);
        }

        private void FloatingFeatureAction(bool value, Func<string, bool> func)
        {
            var name = ((Feature)cbFloatingFeatureName.SelectedValue).Name;
            var status = func(name);
            if (status)
            {
                var feature = FloatingFeatures.Where(x => x.Name == name).FirstOrDefault();
                feature.Value = value;
            }
            CollectionViewSource.GetDefaultView(FloatingFeatures).Refresh();
            lblFloatingFeatureValue.Content = "Status: " + (status ? "OK" : "Error");
        }

        private void CheckoutPoolElement(object sender, RoutedEventArgs e)
        {
            PoolElement(App.Licensing.CheckoutPoolElement);
        }

        private void CheckInPoolElement(object sender, RoutedEventArgs e)
        {
            PoolElement(App.Licensing.CheckinPoolElement);
        }

        private void PoolElement(Func<string, uint, bool> action)
        {
            uint value = 0;
            if (uint.TryParse(tbElementPoolValue.Text, out value))
            {
                var status = action(((ElementPool)cbElementPoolName.SelectedValue).Name, value);
                lblElementPoolStatus.Content = "Status: " + (status ? "OK" : "Error");
                LoadElementPool();
            }
            else
            {
                MessageBox.Show("Invalid element pool value");
            }
        }

        private void CheckoutConsumptionToken(object sender, RoutedEventArgs e)
        {
            ConsumptionToken(App.Licensing.CheckoutTokens, false);
        }

        private void ConsumeConsumptionToken(object sender, RoutedEventArgs e)
        {
            ConsumptionToken(App.Licensing.ConsumeTokens, true);
        }

        private void ConsumptionToken(Func<string, uint, bool> action, bool consume)
        {
            uint value = 0;
            if (uint.TryParse(tbConsumptionTokenValue.Text, out value))
            {
                var name = ((ConsumptionToken)cbConsumptionTokenName.SelectedValue).Name;
                var status = action(name, value);
                lblConsumptionTokenStatus.Content = "Status: " + (status ? "OK" : "Error");
                if (consume) { ConsumptionTokens.Where(x => x.Name == name).Single().Consumed += value; };
                LoadConsumptionToken();
            }
            else
            {
                MessageBox.Show("Invalid element pool value");
            }
        }

        private void LtcoCheckout(object sender, RoutedEventArgs e)
        {
            var success = false;
            BackgroundWorker worker = new BackgroundWorker();
            worker.DoWork += (object workSender, DoWorkEventArgs workE) =>
            {
                DateTime utc = DateTime.UtcNow;
                success = App.Licensing.LTCO(utc.AddDays(LtcoDays));
                if (success) { CheckLicense(); };
            };
            worker.RunWorkerCompleted += (object completeSender, RunWorkerCompletedEventArgs completeE) =>
            {
                if (success) { MessageBox.Show("LTCO successful"); };
                Loader.Visibility = Visibility.Hidden;
            };
            popLtcoCheckout.IsOpen = false;
            Loader.Visibility = Visibility.Visible;
            worker.RunWorkerAsync();
        }

        private void ShowLtcoPopup(object sender, RoutedEventArgs e)
        {
            popLtcoCheckout.IsOpen = true;
        }

        private void CloseLtcoCheckoutPopup(object sender, RoutedEventArgs e)
        {
            popLtcoCheckout.IsOpen = false;
        }

        private void LtcoDaysUp_Click(object sender, RoutedEventArgs e)
        {
            LtcoDays++;
        }

        private void LtcoDaysDown_Click(object sender, RoutedEventArgs e)
        {
            LtcoDays--;
        }

        private void LtcoDays_TextChanged(object sender, TextChangedEventArgs e)
        {
            if (tbLtcoDays == null)
            {
                return;
            }

            if (!int.TryParse(tbLtcoDays.Text, out _ltcoValue))
                tbLtcoDays.Text = _ltcoValue.ToString();
        }

        public int LtcoDays
        {
            get { return _ltcoValue; }
            set
            {
                _ltcoValue = value;
                tbLtcoDays.Text = value.ToString();
            }
        }

        private void FeatureSelected(object sender, SelectionChangedEventArgs e)
        {
            Feature feature = (Feature)e.AddedItems[0];
            UInt32 transId = 0;
            //Call start and end for analytics
            App.Licensing.FeatureStart(feature.Name, ref transId);
            App.Licensing.FeatureEnd(feature.Name, transId);
        }

        private void SetComputerName()
        {
            tbHostName.Text = Environment.MachineName;
        }

        private void RunInUiThread(Action job)
        {
            Application.Current.Dispatcher.Invoke(job);
        }

        private void CheckForUpdates(object sender, RoutedEventArgs e)
        {
            WinSparkle.Update(_maintenenceIn);
        }

        public FeatureExpirationInfo ExpirationInfo { get; set; }
        public ObservableCollection<Message> Messages { get; set; } = new ObservableCollection<Message>();
        public ObservableCollection<Feature> FloatingFeatures { get; set; } = new ObservableCollection<Feature>();
        public ObservableCollection<ElementPool> ElementsPool { get; set; } = new ObservableCollection<ElementPool>();
        public ObservableCollection<ConsumptionToken> ConsumptionTokens { get; set; } = new ObservableCollection<ConsumptionToken>();
        public ObservableCollection<Feature> Features { get; set; } = new ObservableCollection<Feature>();
        public ObservableCollection<Agility> Agilities { get; set; } = new ObservableCollection<Agility>();
        private Dictionary<string, TextBox> _fields;
        private Dictionary<string, TextBox> _popupFields;
        private double? _expiresIn;
        private double? _leaseIn;
        private double? _maintenenceIn;
        private DispatcherTimer _timer = new DispatcherTimer();
        private int _ltcoValue = 0;
        private const string NA = "N/A";
    }
}
