using Newtonsoft.Json;
using System;
using System.IO;
using System.Linq;
using System.Windows;
using System.Xml;
using static Shafer;

namespace ShaferExample
{
    //class cover passive and online mode
    public class LicensingLogic : IDisposable
    {
        public LicensingLogic()
        {
            _shaferLogic = new Shafer();
            _passiveLogic = new Passive();
            Config = new NalpLibConfiguration();
            Initialize();
            RefreshLicenseStatus();
        }

        private void Initialize()
        {
            bool badImage = false;
            var error = PassiveMode ? _passiveLogic.Init(ref badImage) : _shaferLogic.Init(ref badImage);
            if (error < 0)
            {
                if (error == -1122)
                {
                    throw new Exception("Library validation failed");
                }
                else
                {
                    ShowError(error, true);
                }
            }
            else
            {
                CheckBuildPlatform(badImage);
            }
        }

        internal void NewLicense()
        {
            //New license activation
            _newLicense = true;
        }

        internal bool ShowOverdraftInfo()
        {
            var result = false;
            if (_newLicense)
            {
                //Check overdraft only at activation
                var overdraft = 0;
                if (int.TryParse(GetAgility("Overdraft"), out overdraft))
                {
                    int current = 0;
                    int max = 0;
                    if (int.TryParse(App.Licensing.GetAgility("$ActivationsAllowed"), out max)
                        && int.TryParse(App.Licensing.GetAgility("$CurrentActivations"), out current))
                    {

                        result = current > (max - overdraft);
                    }
                }
            }
            return result;
        }

        internal bool ShowVersionInfo()
        {
            var result = false;
            var version = GetAgility("MinMaxVersion");
            if (!string.IsNullOrWhiteSpace(version))
            {
                //The version should have the following format: '4-6', not well-formed or empty will be ignored
                var versions = version.Split(new string[] { "-" }, StringSplitOptions.RemoveEmptyEntries);
                if (versions.Length == 2)
                {
                    if (int.TryParse(versions[0], out int majorMin) && int.TryParse(versions[1], out int majorMax) && int.TryParse(AppUtils.GetVersion(), out int major))
                    {
                        return !(majorMin <= major && major <= majorMax);
                    }
                }
            }
            return result;
        }

        internal bool ShouldShowRegPopup()
        {
            var result = _newLicense && Config.ForceReg;
            _newLicense = false;
            return result;
        }

        public void ApplicationStart()
        {
            //Check the privacy settings if required
            var shaferError = _shaferLogic.GetPrivacy();
            if (shaferError < 0)
            {
                ShowError(shaferError, true);
            }
            else if ((Shafer.PrivacyValues)shaferError == Shafer.PrivacyValues.NOT_SET || (Shafer.PrivacyValues)shaferError == Shafer.PrivacyValues.ON)
            {
                //If privacy not set or set to on then prompt and set
                var dialogResult = MessageBox.Show("You are not currently participating in our product improvement program, participate in the direction of future versions of our products by clicking Yes, below"
                       , "Privacy", MessageBoxButton.YesNo);
                if (dialogResult == MessageBoxResult.Yes)
                {
                    //Set to allow data transmission
                    shaferError = _shaferLogic.SetPrivacy(Shafer.PrivacyValues.OFF);
                }
                else
                {
                    //Set to disallow data transmission
                    shaferError = _shaferLogic.SetPrivacy(Shafer.PrivacyValues.ON);
                }
                if (shaferError < 0)
                {
                    ShowError(shaferError, true);
                }
            }
            //If privacy already set then don't ask again

            //Record the application Start
            shaferError = _shaferLogic.ApplicationStart(GetUsername(), AppUtils.GetLanguage(), AppUtils.GetVersion(), AppUtils.GetEdition(), AppUtils.GetBuild(), LicenseStatus == Shafer.LicenseStatus.PROD_INTRIAL ? "Trial" : "Activated");
            if (shaferError < 0 && shaferError != -1079 && shaferError != -1080) // Forbidden by privacy setting
            {
                ShowError(shaferError, true);
            }
        }

        private void CheckBuildPlatform(bool badImage)
        {
            if (badImage)
            {
                string buildplatform = AppUtils.GetBuildPlatform();
                string libraryPlatform = buildplatform == "x86" ? "x64" : "x86";
                string badimagemsg = $"Failure.  You seem to be running this application in {buildplatform} " +
                    $"mode but your downloaded DLL (ShaferFileChck.dll) has been configured for {libraryPlatform}.  " +
                    $"Please re-download your DLL for {buildplatform} or change your application target platform to {libraryPlatform}.";
                throw new Exception(badimagemsg);
            }
        }

        //Takes the error message id and retrieves the apropriate error message to present to the user
        public static void ShowError(int shaferError, bool exitApplication)
        {
            string errMess = string.Empty;
            GetErrorMessage(shaferError, ref errMess);
            if (exitApplication)
            {
                throw new Exception(errMess);
            }
            else
            {
                MessageBox.Show(errMess);
            }
        }

        public void GetLicenseInfo(ref string licensetype, ref string activationtype)
        {
            UInt32 ltype = 0;
            UInt32 atype = 0;
            var shaferError = _shaferLogic.GetLicenseInfo(ref ltype, ref atype);
            if (shaferError == 0)
            {
                licensetype = Shafer.GetLicenseType(ltype);
                activationtype = Shafer.GetActivationType(atype);
            }
            else
            {
                ShowError(shaferError, true);
            }
        }

        internal void RemoveFiles(string workDir)
        {
            var files = Directory.GetFiles(workDir, "*.*", SearchOption.TopDirectoryOnly);
            files.ToList().ForEach(x => File.Delete(x));
        }

        internal void RemoveFiles()
        {
            //Only for passive
            if (PassiveMode)
            {
                _passiveLogic.PSLClose(true);
                RemoveFiles(_passiveLogic.WorkDir);
                Initialize();
                RefreshLicenseStatus();
            }
            else
            {
                throw new NotImplementedException();
            }
        }

        public string GetTrialExpiryDate()
        {
            string expiryDate = "";
            //Get the Actual Expiry Date
            var shaferError = _shaferLogic.GetTrialExpiryDate(ref expiryDate);
            if (shaferError < 0)
            {
                ShowError(shaferError, true);
            }
            return expiryDate;
        }

        public int GetTrialExpirySecs()
        {
            int expiresinSecs = 0;
            //Get the length left in trial in Seconds
            var shaferError = _shaferLogic.GetTrialExpirySecs(ref expiresinSecs);
            if (shaferError < 0)
            {
                ShowError(shaferError, true);
            }
            return expiresinSecs;
        }

        public string GetSubscriptionExpiryDate()
        {
            string expiryDate = "";
            //Get the Actual Expiry Date
            var shaferError = _shaferLogic.GetSubscriptionExpiryDate(ref expiryDate);
            if (shaferError < 0)
            {
                ShowError(shaferError, true);
            }
            return expiryDate;
        }

        public int GetSubscriptionExpirySecs()
        {
            int expiresinSecs = 0;
            //Get the length left in trial in Seconds
            var shaferError = _shaferLogic.GetSubscriptionExpirySecs(ref expiresinSecs);
            if (shaferError < 0)
            {
                ShowError(shaferError, true);
            }
            return expiresinSecs;
        }

        public string GetActivationCert(string licenseCode)
        {
            licenseCode = licenseCode.Trim();
            string certificate = string.Empty;
            var regdata = GetRegData();
            var shaferError = _shaferLogic.GetActivationCert(licenseCode, ref certificate, regdata);
            if (shaferError < 0)
            {
                ShowError(shaferError, false);
            }
            //Save license code for future use
            SaveLicenseCode(licenseCode);
            return certificate;
        }

        public void ImportActivationCert(string activationCode)
        {
            string LicenseCode = GetLicenseCode();

            var shaferError = _shaferLogic.ImportActivationCert(LicenseCode, activationCode);
            if (shaferError < 0)
            {
                ShowError(shaferError, false);
            }
        }

        private void GetLicenseStatus(ref int licenseStatus)
        {
            var error = PassiveMode ? _passiveLogic.GetLicenseStatus(ref licenseStatus) : _shaferLogic.GetLicenseStatus(ref licenseStatus);
            if (error < 0)
            {
                ShowError(error, true);
            }
        }

        public int RefreshLicenseStatus()
        {
            int licenseStatus = 0;
            int error = 0;
            GetLicenseStatus(ref licenseStatus);
            //Credentials needed
            if (licenseStatus == -70)
            {
                var auth = GetAuthentication();
                if (auth != null)
                {
                    SetCredentials(auth.Username, auth.Password);
                    NSLCheckCredentials();
                    GetLicenseStatus(ref licenseStatus);
                }
            }

            //SECURITY: If computer has been BACK Dated to get more time for the trial or Subscription then Exit Application
            if (licenseStatus == (int)LicenseStatus.PROD_BTCOUNTER)
            {
                ShowError(error, true);
            }

            //If License Status is negative then try to get license from server, if not then prompt for manual activation
            if (licenseStatus <= 0)
            {
                //LicenseStatus = GetLicenseFromServer(false);
            }
            else if (!PassiveMode && _shaferLogic.NSLTestInternetConnection() == 0)
            {
                // Extra security procedure: if license table overwritten on an online system, the license table will refresh itself.
                // Don't try License Refresh if Network LTCO - daemon doesn't allow LTCO Refreshes for security reasons
                if (licenseStatus != (int)LicenseStatus.PROD_NETWORK_LTCO)
                {
                    // To enable the license to be refreshed (update local entitlements from the server) on application start, we need to attempt a license refresh.  If offline then continue running with the local entitements.  If online then refresh from the server. 
                    licenseStatus = GetLicenseFromServer();
                }
            }

            if (licenseStatus == 0)
            {
                MessageBox.Show("Server can not be contacted");
            }
            else
            {
                _licenseStatus = (LicenseStatus)licenseStatus;
            }

            return licenseStatus;
        }

        public void ReturnLicense()
        {
            CleanRegData();
            var licenseCode = GetLicenseCode();
            _shaferLogic.ReturnLicense(licenseCode);
            CleanLicenseData();
            ClearCredentials();
        }

        public string GetDeactivationCert()
        {
            CleanRegData();
            string licenseCode = GetLicenseCode();
            string certificate = "";

            var shaferError = _shaferLogic.GetDeactivationCert(licenseCode, ref certificate);
            if (shaferError < 0)
            {
                ShowError(shaferError, false);
            }

            return certificate;
        }

        public int GetLicenseFromServer(string licenseCode = null)
        {
            // Define empty string to populate wih  registration data
            string regData = GetRegData();

            var newLicenseCode = licenseCode != null;
            //Try to get License Contained in License.TXT, if no License.TXT AND TRIAL EXPIRED OR NO TRIAL prompt for Licnese Code 
            licenseCode = newLicenseCode ? licenseCode : GetLicenseCode();
            licenseCode = licenseCode.Trim();
            int licenseStatus = 0;

            //First Check internet connection
            var shaferError = _shaferLogic.NSLTestInternetConnection();
            if (shaferError == 0)
            {
                // Attempt to activate
                // If RegData is non-empty, pass to GetLicense.  Otherwise pass null.
                if (!string.IsNullOrWhiteSpace(regData))
                {
                    shaferError = _shaferLogic.GetLicense(licenseCode, regData);
                }
                else
                {
                    shaferError = _shaferLogic.GetLicense(licenseCode, null);
                }

                //Custom messages or default
                if (shaferError == -4216)
                {
                    MessageBox.Show("User not authorized");
                }
                else if (shaferError == -4217)
                {
                    MessageBox.Show("Authentication Failed");
                }
                else if (shaferError < 0)
                {
                    ShowError(shaferError, false);
                }
                shaferError = _shaferLogic.GetLicenseStatus(ref licenseStatus);
                if (shaferError < 0)
                {
                    ShowError(shaferError, false);
                }

                //Save License Code in file for future use
                if (newLicenseCode) { SaveLicenseCode(licenseCode); };
            }
            else
            {
                ShowError(shaferError, false);
            }
            return licenseStatus;
        }

        public void RunActivationCert(string licenseCode, string activationCode)
        {
            licenseCode = licenseCode.Trim();
            var shaferError = PassiveMode ? _passiveLogic.ImportActivationCert(licenseCode, activationCode) : _shaferLogic.ImportActivationCert(licenseCode, activationCode);
            if (shaferError < 0)
            {
                ShowError(shaferError, false);
            }
            SaveLicenseCode(licenseCode);
        }


        private void SaveLicenseCode(string LicenseCode)
        {
            //Save License Code to File
            File.WriteAllText(licenseFileName, LicenseCode);
        }

        private void SaveAuthentication(AuthData auth)
        {
            File.WriteAllText(authenticationFileName, JsonConvert.SerializeObject(auth));
        }

        private AuthData GetAuthentication()
        {
            string authDataJson = "";
            //Attempt to read from file
            if (File.Exists(authenticationFileName))
            {
                authDataJson = File.ReadAllText(authenticationFileName);
            }
            return authDataJson.Length > 0 ? JsonConvert.DeserializeObject<AuthData>(authDataJson) : null;
        }

        public String GetLicenseCode()
        {
            string LicenseCode = "";
            //Attempt to read from file
            if (File.Exists(licenseFileName))
            {
                LicenseCode = File.ReadAllText(licenseFileName);
            }
            return LicenseCode;
        }

        public string GetRegData()
        {
            string rData = string.Empty;
            if (File.Exists(regFileName))
            {
                // Remove carriage returns, line feeds and spaces from registration data.  Append to to RegData.
                rData = File.ReadAllText(regFileName).Replace("\r\n", "");
            }
            return rData;
        }

        private RegistrationForm ReadRegistrationData()
        {
            var result = new RegistrationForm();
            XmlDocument registrationFile = new XmlDocument();
            var regData = GetRegData();
            if (regData == string.Empty) { return result; }
            registrationFile.LoadXml(regData);
            var xmlnode = registrationFile.GetElementsByTagName("clientinfo");
            if (xmlnode.Count == 1)
            {
                var clientInfo = xmlnode[0];
                foreach (XmlNode node in clientInfo.ChildNodes)
                {
                    switch (node.Name)
                    {
                        case "companyname":
                            result.CompanyName = AppUtils.GetNodeValue(node);
                            break;
                        case "firstname":
                            result.CompanyName = AppUtils.GetNodeValue(node);
                            break;
                        case "lastname":
                            result.CompanyName = AppUtils.GetNodeValue(node);
                            break;
                        case "telephone":
                            result.CompanyName = AppUtils.GetNodeValue(node);
                            break;
                        case "email":
                            result.CompanyName = AppUtils.GetNodeValue(node);
                            break;
                    }
                }
            }

            return result;
        }

        public void RemoveFile(string fileName)
        {
            if (File.Exists(fileName))
            {
                File.Delete(fileName);
            }
        }

        public void CleanRegData()
        {
            RemoveFile(regFileName);
        }

        public void CleanLicenseData()
        {
            RemoveFile(licenseFileName);
            RemoveFile(authenticationFileName);
        }

        public XmlDocument CreateRegistrationFile(RegistrationForm form)
        {
            // Create the xml document. 
            XmlDocument doc = new XmlDocument();

            // Create root element 'data'.

            XmlElement root = doc.CreateElement("clientinfo");
            doc.AppendChild(root);

            //Create other elements from form data.  Read element value from corresponding text box and append to child elements of root element 'data'.
            XmlElement firstname = doc.CreateElement("firstname");
            firstname.InnerText = form.FirstName;
            root.AppendChild(firstname);

            XmlElement lastname = doc.CreateElement("lastname");
            lastname.InnerText = form.LastName;
            root.AppendChild(lastname);

            XmlElement company = doc.CreateElement("company");
            company.InnerText = form.CompanyName;
            root.AppendChild(company);

            XmlElement email = doc.CreateElement("email");
            email.InnerText = form.Email;
            root.AppendChild(email);

            XmlElement telephone = doc.CreateElement("telephone");
            telephone.InnerText = form.Telephone;
            root.AppendChild(telephone);
            return doc;
        }

        public bool GetFeature(string name)
        {
            int result = 0;
            var shaferError = PassiveMode ? _passiveLogic.GetFeatureStatus(name, ref result) : _shaferLogic.GetFeatureStatus(name, ref result);
            return result == 1;
        }

        public bool FeatureStart(string featureName, ref UInt32 TxnId)
        {
            var result = true;
            var shaferError = _shaferLogic.FeatureStart(GetUsername(), featureName, ref TxnId);
            if (shaferError < 0)
            {
                ShowError(shaferError, false);
                result = false;
            }
            return result;
        }

        public bool FeatureEnd(string featureName, UInt32 TxnId)
        {
            var result = true;
            var shaferError = _shaferLogic.FeatureEnd(GetUsername(), featureName, TxnId);
            if (shaferError < 0)
            {
                ShowError(shaferError, false);
                result = false;
            }
            return result;
        }

        public string GetAgility(string name)
        {
            string result = string.Empty;
            var shaferError = PassiveMode ? _passiveLogic.GetAAFieldValue(name, ref result) : _shaferLogic.GetAAFieldValue(name, ref result);
            return result;
        }

        public ElementPool GetPoolInfo(string name)
        {
            var returnValue = new ElementPool() { Name = name };
            uint poolMax = 0, poolAmt = 0;
            int poolStatus = 0;
            var shaferError = _shaferLogic.GetPoolInfo(name, ref poolMax, ref poolAmt, ref poolStatus);
            if (shaferError == 0)
            {
                returnValue = new ElementPool() { Name = name, Value = poolStatus == 1, ValueMax = poolMax, ValueAmt = poolAmt };
            }
            return returnValue;
        }

        public bool CheckinFloatingFeature(string name)
        {
            var shaferError = _shaferLogic.CheckinFloatingFeature(GetLicenseCode(), name);
            var result = true;
            if (shaferError < 0)
            {
                ShowError(shaferError, false);
                result = false;
            }
            return result;
        }

        public bool CheckoutFloatingFeature(string name)
        {
            int status = 0;
            var result = true;
            var shaferError = _shaferLogic.CheckoutFloatingFeature(GetLicenseCode(), name, ref status);
            if (shaferError < 0)
            {
                ShowError(shaferError, false);
                result = false;
            }
            return result;
        }

        public bool CheckinPoolElement(string name, uint value)
        {
            var shaferError = _shaferLogic.CheckinElements(name, value, GetLicenseCode());
            var result = true;
            if (shaferError < 0)
            {
                ShowError(shaferError, false);
                result = false;
            }
            return result;
        }

        public bool CheckoutPoolElement(string name, uint value)
        {
            int status = 0;
            var result = true;
            var shaferError = _shaferLogic.CheckoutElements(name, value, ref status, GetLicenseCode());
            if (shaferError < 0)
            {
                ShowError(shaferError, false);
                result = false;
            }
            return result;
        }

        public bool ConsumeTokens(string name, uint value)
        {
            int status = 0;
            var shaferError = _shaferLogic.ConsumeTokens(name, value, ref status, GetLicenseCode());
            var result = true;
            if (shaferError < 0)
            {
                ShowError(shaferError, false);
                result = false;
            }
            return result;
        }

        public bool CheckoutTokens(string name, uint value)
        {
            int status = 0;
            var shaferError = _shaferLogic.CheckoutTokens(name, value, ref status, GetLicenseCode());
            var result = true;
            if (shaferError < 0)
            {
                ShowError(shaferError, false);
                result = false;
            }
            return result;
        }

        public ConsumptionToken GetTokenInfo(string name)
        {
            var returnValue = new ConsumptionToken() { Name = name };
            uint poolMax = 0, poolAmt = 0;
            int poolStatus = 0;
            var shaferError = _shaferLogic.GetTokenInfo(name, ref poolMax, ref poolAmt, ref poolStatus);
            if (shaferError == 0)
            {
                returnValue = new ConsumptionToken() { Name = name, Value = poolStatus == 1, ValueMax = poolMax, ValueAmt = poolAmt };
            }
            return returnValue;
        }

        public string GetHostName()
        {
            string host = string.Empty;
            var shaferError = _shaferLogic.NSLGetHostName(ref host);
            if (shaferError < 0)
            {
                ShowError(shaferError, false);
            }
            return host;
        }

        public bool LTCO(DateTime utcDate)
        {
            var rpcVarVals = new string[] { AppUtils.ToUnixTime(utcDate).ToString() };
            var rpcReturn = new string[] { string.Empty };
            var shaferError = _shaferLogic.NSLRemoteCallV("LTCO", new string[] { "rpcLTCOEndDate" }, rpcVarVals, ref rpcReturn);
            if (shaferError < 0)
            {
                ShowError(shaferError, false);
            }
            return shaferError == 0;
        }

        public string GetComputerId()
        {
            var computerId = string.Empty;
            if (PassiveMode)
            {
                _passiveLogic.PSLGetComputerID(out computerId);
            }
            else
            {
                _shaferLogic.NSLGetComputerID(out computerId);
            }
            return computerId;
        }

        public string GetMaintenanceExpiryDate()
        {
            var date = string.Empty;
            _shaferLogic.GetMaintenanceExpiryDate(ref date);
            return date;
        }

        public int GetMaintenanceExpirySecs()
        {
            var sec = 0;
            _shaferLogic.GetMaintenanceExpirySecs(ref sec);
            return sec;
        }

        public string GetLeaseExpiryDate()
        {
            var date = string.Empty;
            _shaferLogic.GetLeaseExpiryDate(ref date);
            return date;
        }

        public int GetLeaseExpirySecs()
        {
            var sec = 0;
            _shaferLogic.GetLeaseExpirySecs(ref sec);
            return sec;
        }

        public Processes GetNumbAvailProc()
        {
            var max = 0;
            var available = 0;
            if (PassiveMode)
            {
                _passiveLogic.GetNumbAvailProc(ref max, ref available);
            }
            else
            {
                _shaferLogic.GetNumbAvailProc(ref max, ref available);
            }
            return new Processes() { Max = max, Available = available };
        }

        public bool LanDeamonMode
        {
            get
            {
                return Config.LicenseMode == NalpLibConfiguration.LicenseModes.LanDaemon.ToString();
            }
        }

        public bool PassiveMode
        {
            get
            {
                return Config.LicenseMode == NalpLibConfiguration.LicenseModes.Passive.ToString();
            }
        }

        public bool LicenseAcquired
        {
            get
            {
                return AppUtils.LicenseAcquired(LicenseStatus);
            }
        }

        private void ClearCredentials()
        {
            var error = _shaferLogic.ClearCredentials();
            if (error < 0)
            {
                ShowError(error, false);
            }
        }

        public void SetCredentials(string username, string password, bool onlineActivation = true)
        {
            username = username.Trim();
            var error = _shaferLogic.SetCredentials(username, password);
            if (error < 0)
            {
                ShowError(error, false);
            }
            if (onlineActivation)
            {
                var licenseStatus = (LicenseStatus)GetLicenseFromServer(username);
                if (licenseStatus == LicenseStatus.PROD_ABL || licenseStatus == LicenseStatus.PROD_ABL_CONCURRENT)
                {
                    SaveAuthentication(new AuthData { Password = password, Username = username });
                }
            }
        }

        public void Dispose()
        {
            if (PassiveMode)
            {
                _passiveLogic.PSLClose();
            }
            else
            {
                if ((PrivacyValues)_shaferLogic.GetPrivacy() == PrivacyValues.OFF)
                {
                    _shaferLogic.ApplicationEnd(GetUsername());
                }
                else
                {
                    _shaferLogic.ApplicationEndNSL();
                }
            }
        }

        private string GetUsername()
        {
            var username = string.Empty;
            var auth = GetAuthentication();
            if (auth != null)
            {
                username = auth.Username;
            }
            else
            {
                var regData = ReadRegistrationData();
                username = $"{regData.FirstName} {regData.LastName}".Trim();
            }
            if (string.IsNullOrWhiteSpace(username))
            {
                username = "Username";
            }
            return username;
        }

        public void ActivationFailedMessage()
        {
            MessageBox.Show($"Activation failed Status: {AppUtils.GetEnumDescription(LicenseStatus)}");
        }

        public LicenseStatus LicenseStatus { get { return _licenseStatus; } }
        public NalpLibConfiguration Config { get; }

        private LicenseStatus _licenseStatus;
        private Shafer _shaferLogic;
        private Passive _passiveLogic;
        private bool _newLicense = false;

        private static string authenticationFileName = "Authentication.xml";
        private static string licenseFileName = "License.xml";
        private static string regFileName = "RegFile.xml";
    }
}
