using System;
using System.Collections.Generic;
using System.Linq;
using System.Runtime.InteropServices;
using System.IO;
using static ShaferExample.AppUtils;

namespace ShaferExample
{
    public class Passive
    {
        //Passive
        [DllImport("PassiveFilechck.dll", CallingConvention = CallingConvention.Cdecl)]
        public static extern int PSLLibOpen(byte[] xmlParams);

        [DllImport("PassiveFilechck.dll", CallingConvention = CallingConvention.Cdecl)]
        public static extern int PSLLibClose();

        [DllImport("PassiveFilechck.dll", CallingConvention = CallingConvention.Cdecl)]
        public static extern int PSLGetErrorMsg(int errorNumber, out IntPtr errorMessage);

        [DllImport("PassiveFilechck.dll", CallingConvention = CallingConvention.Cdecl)]
        public static extern int PSLGetFilepath(byte[] extension, out IntPtr filepath);

        [DllImport("PassiveFilechck.dll", CallingConvention = CallingConvention.Cdecl)]
        public static extern int PSLValidateLibrary(int customerID, int productID);

        [DllImport("PassiveFilechck.dll", CallingConvention = CallingConvention.Cdecl)]
        public static extern int PSLGetVersion(out IntPtr version);

        [DllImport("PassiveFilechck.dll", CallingConvention = CallingConvention.Cdecl)]
        public static extern int PSLGetComputerID(out IntPtr computerID);

        [DllImport("PassiveFilechck.dll", CallingConvention = CallingConvention.Cdecl)]
        public static extern int PSLGetLicenseStatus(out int licenseStatus);

        [DllImport("PassiveFilechck.dll", CallingConvention = CallingConvention.Cdecl)]
        public static extern int PSLGetLicenseCode(out IntPtr licenseCode);

        [DllImport("PassiveFilechck.dll", CallingConvention = CallingConvention.Cdecl)]
        public static extern int PSLImportLicense(byte[] licenseCode, ref int licenseStatus, byte[] certificate);

        [DllImport("PassiveFilechck.dll", CallingConvention = CallingConvention.Cdecl)]
        public static extern int PSLGetFeatureStatus(byte[] featureName, ref int featureStatus);

        [DllImport("PassiveFilechck.dll", CallingConvention = CallingConvention.Cdecl)]
        public static extern int PSLGetUDFValue(byte[] fieldname, out IntPtr fieldValue);

        [DllImport("PassiveFilechck.dll", CallingConvention = CallingConvention.Cdecl)]
        public static extern int PSLGetNumbAvailProc(ref int maxProc, ref int availProc);

        [DllImport("PassiveFilechck.dll", CallingConvention = CallingConvention.Cdecl)]
        public static extern int PSLFree(IntPtr myvar);

        public int Init(ref bool badImage)
        {
            //Create separate folder for passive files
            _workDir = Environment.GetFolderPath(Environment.SpecialFolder.ApplicationData) + $"\\Nalpeiron\\{App.AppCode}Passive";
            var config = new NalpLibConfiguration();
            int pslError = 0;
            //Create a random number between 1 and 500 for the Security Constants
            Random rnd = new Random();
            UInt32 securityRandom = (UInt32)rnd.Next(1, 500); // creates a number between 1 and 500

            //If using security constants then Calculate Offset and pass to Init
            if (config.X != 0 && config.Y != 0 && config.Z != 0)
            {
                //Calculate Offset
                _securityOffset = CalculateSecurityOffSet(securityRandom, config.X, config.Y, config.Z);
            }

            //Create parameters key/value pair structure
            var xParms = new Dictionary<string, string>
            {
                //Advnaced settings, for normal operation leave as defaults
                { "SecurityValue", securityRandom.ToString() }, //Security Offset if enabled, or 0
                { "LicenseCode", config.ClientID },
                { "LogLevel", "4" }, //set log level, please see documentation
                { "WorkGroup", "" },
                { "MaxLogSize", "" },
                { "WorkDir", _workDir }, //Work Directory
                { "LogQLen", "300" } //please see documentation
            };

            //Create the XML doc to pass parameters
            string xmlParms = ConstructXMLVariableTag(xParms, "PSLXMLParams");

            //Initialise NSA and record Application Start
            // Convert string to byte array for Unicode compatibvility
            var utfXmlParms = StringToBytes(xmlParms);
            try
            {
                pslError = PSLLibOpen(utfXmlParms);
                if (pslError < 0)
                {
                    return pslError;
                }
            }
            catch (BadImageFormatException)
            {
                badImage = true;
                return pslError;
            }
            //Validate the DLL (Check to see if correct DLL for This Customer and Product)
            pslError = PSLValidateLibrary(config.CustomerID, config.ProductID);
            if (pslError < 0)
            {
                return pslError;
            }

            return pslError;
        }

        public int GetNumbAvailProc(ref int maximum, ref int available)
        {
            int returnValue = 0;

            returnValue = PSLGetNumbAvailProc(ref maximum, ref available);
            returnValue -= (int)_securityOffset;

            return returnValue;
        }

        public int PSLGetComputerID(out string computerID)
        {
            int returnValue = 0;

            IntPtr i = new IntPtr();

            returnValue = PSLGetComputerID(out i);
            returnValue -= (int)_securityOffset;

            //Return the stats if call ok
            computerID = Marshal.PtrToStringAnsi(i);
            PSLFree(i);
            return returnValue;
        }

        public int GetAAFieldValue(string fieldname, ref string fieldValue)
        {
            int returnValue = 0;

            IntPtr i = new IntPtr();
            // Convert strings to byte arrays for Unicode compatibility
            var utfFieldname = StringToBytes(fieldname);
            returnValue = PSLGetUDFValue(utfFieldname, out i);
            returnValue -= (int)_securityOffset;

            fieldValue = Marshal.PtrToStringAnsi(i);
            PSLFree(i);
            return returnValue;
        }

        public int GetFeatureStatus(string featureName, ref int featureStatus)
        {
            int returnValue = 0;
            // Convert strings to byte arrays for Unicode compatibility
            var utfFeatureName = StringToBytes(featureName);
            returnValue = PSLGetFeatureStatus(utfFeatureName, ref featureStatus);
            returnValue -= (int)_securityOffset;
            //Return the stats if call ok

            return returnValue;
        }

        public int GetLicenseStatus(ref int licenseStatus)
        {
            int returnValue = 0;

            returnValue = PSLGetLicenseStatus(out licenseStatus);
            returnValue -= (int)_securityOffset;
            //Return the stats if call ok
            return returnValue;
        }

        public int ImportActivationCert(string licenseCode, string certificate)
        {
            int returnValue = 0;
            int LicenseStatus = 0;
            IntPtr i = new IntPtr();
            // Convert strings to byte arrays for Unicode compatibility
            var utfLicenseCode = StringToBytes(licenseCode);
            var utfCertificate = StringToBytes(certificate);
            returnValue = PSLImportLicense(licenseCode == "" ? null : utfLicenseCode, ref LicenseStatus, utfCertificate);
            returnValue -= (int)_securityOffset;
            certificate = Marshal.PtrToStringAnsi(i);
            PSLFree(i);
            return returnValue;
        }

        internal void PSLClose(bool removeFiles = false)
        {
            PSLLibClose();
            if (removeFiles)
            {
                var files = Directory.GetFiles(_workDir, "*.*", SearchOption.TopDirectoryOnly);
                files.ToList().ForEach(x => File.Delete(x));
            }
        }

        public string WorkDir { get { return _workDir; } }
        private string _workDir;
        private UInt32 _securityOffset = 0;
    }
}
