//************************************************************************************
//*** Author : Richard Allen
//*** Created : 01/11/2012
//*** 
//*** Change History :
//*** V1_0_0 - Richard Allen (01 November 2013) - Initial
//*** V1_0_1 - Richard Allen (23 November 2013) - Added Client Side Privacy options
//*** V1_0_2 - Richard Allen (08 January 2014) - Add Security Constants functionailty
//************************************************************************************bye
using ShaferExample;
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Runtime.InteropServices;
using System.Text;
using static ShaferExample.AppUtils;

public class Shafer
{
    //NALP

    [DllImport("ShaferFilechck.dll", CallingConvention = CallingConvention.Cdecl)]
    public static extern int NalpLibOpen(byte[] xmlParms);

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

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

    //NSL


    [DllImport("ShaferFilechck.dll", CallingConvention = CallingConvention.Cdecl)]
    public static extern int NSLGetHostName(out IntPtr hostname);

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

    [DllImport("ShaferFilechck.dll", CallingConvention = CallingConvention.Cdecl)]
    public static extern int NSLGetLicense(byte[] licenseCode, ref int licenseStatus, byte[] regInfo);

    [DllImport("ShaferFilechck.dll", CallingConvention = CallingConvention.Cdecl)]
    public static extern int NSLSetCredentials(byte[] ursername, byte[] password, UserAuth userAuthenticator, int dataSize, IntPtr data);

    [DllImport("ShaferFilechck.dll", CallingConvention = CallingConvention.Cdecl)]
    public static extern int NSLGetCredentials(out int lastReturn, out IntPtr username, out IntPtr inData, out IntPtr outData);

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

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

    //Create Delegate for UserAuth Callback
    public delegate int UserAuth(string username, string password, IntPtr data, ref string authToken, int outDataSize, ref IntPtr outData);

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

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

    [DllImport("ShaferFilechck.dll", CallingConvention = CallingConvention.Cdecl)]
    public static extern int NSLGetSubExpSec(ref int expirySecs, ref int epochSecs);

    [DllImport("ShaferFilechck.dll", CallingConvention = CallingConvention.Cdecl)]
    public static extern int NSLGetSubExpDate(out IntPtr expiryDateTime);

    [DllImport("ShaferFilechck.dll", CallingConvention = CallingConvention.Cdecl)]
    public static extern int NSLGetTrialExpSec(ref int expirySecs, ref int epochSecs);

    [DllImport("ShaferFilechck.dll", CallingConvention = CallingConvention.Cdecl)]
    public static extern int NSLGetTrialExpDate(out IntPtr expiryDateTime);
    [DllImport("ShaferFilechck.dll", CallingConvention = CallingConvention.Cdecl)]
    public static extern int NSLGetLeaseExpSec(ref int expirySecs, ref int epochSecs);
    [DllImport("ShaferFilechck.dll", CallingConvention = CallingConvention.Cdecl)]
    public static extern int NSLGetLeaseExpDate(out IntPtr expiryDateTime);
    [DllImport("ShaferFilechck.dll", CallingConvention = CallingConvention.Cdecl)]
    public static extern int NSLGetMaintExpSec(ref int expirySecs, ref int epochSecs);
    [DllImport("ShaferFilechck.dll", CallingConvention = CallingConvention.Cdecl)]
    public static extern int NSLGetMaintExpDate(out IntPtr expiryDateTime);
    [DllImport("ShaferFilechck.dll", CallingConvention = CallingConvention.Cdecl)]
    public static extern int NSLHelloWorld();
    [DllImport("ShaferFilechck.dll", CallingConvention = CallingConvention.Cdecl)]
    public static extern int NSLTestConnection2(UInt32 connTimeOut, UInt32 transTimeOut);

    [DllImport("ShaferFilechck.dll", CallingConvention = CallingConvention.Cdecl)]
    public static extern int NSLGetTimeStamp(ref long epochSecs);

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

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

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

    [DllImport("ShaferFilechck.dll", CallingConvention = CallingConvention.Cdecl)]
    public static extern int NSLGetVMInfo(ref UInt32 isVM, out IntPtr vmType);

    //new or changed
    [DllImport("ShaferFilechck.dll", CallingConvention = CallingConvention.Cdecl)]
    public static extern int NSLPutUDFValue(byte[] fieldname, byte[] fieldValue);

    [DllImport("ShaferFilechck.dll", CallingConvention = CallingConvention.Cdecl)]
    public static extern int NSLImportCertificate(byte[] licenseNumber, ref int licenseStatus, byte[] certificate);

    [DllImport("ShaferFilechck.dll", CallingConvention = CallingConvention.Cdecl)]
    public static extern int NSLGetActivationCertReq(byte[] licenseNumber, byte[] regInfo, out IntPtr certificate);

    [DllImport("ShaferFilechck.dll", CallingConvention = CallingConvention.Cdecl)]
    public static extern int NSLGetDeactivationCertReq(byte[] licenseNumber, out IntPtr certificate);

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

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

    [DllImport("ShaferFilechck.dll", CallingConvention = CallingConvention.Cdecl)]
    public static extern int NSLFree(IntPtr myVar);

    [DllImport("ShaferFilechck.dll", CallingConvention = CallingConvention.Cdecl)]
    public static extern int NSLGetLicenseInfo(out IntPtr licenseType, out IntPtr actType);

    // features

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

    [DllImport("ShaferFilechck.dll", CallingConvention = CallingConvention.Cdecl)]
    public static extern int NSLCheckoutFeature(byte[] featureName, ref int featureStat, byte[] licenseNo);

    [DllImport("ShaferFilechck.dll", CallingConvention = CallingConvention.Cdecl)]
    public static extern int NSLReturnFeature(byte[] featureName, byte[] licenseNo);

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

    [DllImport("ShaferFilechck.dll", CallingConvention = CallingConvention.Cdecl)]
    public static extern int NSLGetCounter(byte[] counterName, ref ulong count);

    [DllImport("ShaferFilechck.dll", CallingConvention = CallingConvention.Cdecl)]
    public static extern int NSLIncrementUsage(byte[] counterName, ref ulong count);

    [DllImport("ShaferFilechck.dll", CallingConvention = CallingConvention.Cdecl)]
    public static extern int NSLCheckoutPool(byte[] poolName, UInt32 numToCheckOut, ref int poolStatus, byte[] licenseNo);

    [DllImport("ShaferFilechck.dll", CallingConvention = CallingConvention.Cdecl)]
    public static extern int NSLReturnPool(byte[] poolName, UInt32 numToCheckIn, byte[] licenseNo);

    [DllImport("ShaferFilechck.dll", CallingConvention = CallingConvention.Cdecl)]
    public static extern int NSLGetPoolStatus(byte[] poolName, ref UInt32 numCheckedOut, ref int poolStatus);

    [DllImport("ShaferFilechck.dll", CallingConvention = CallingConvention.Cdecl)]
    public static extern int NSLGetPoolInfo(byte[] poolName, ref UInt32 poolMax, ref UInt32 poolAmt, ref Int32 poolStatus);

    //NSA

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

    [DllImport("ShaferFilechck.dll", CallingConvention = CallingConvention.Cdecl)]
    public static extern int NSALogin(byte[] username, byte[] nsaClientData, ref UInt32 txnID);

    [DllImport("ShaferFilechck.dll", CallingConvention = CallingConvention.Cdecl)]
    public static extern int NSALogout(byte[] username, byte[] nsaClientData, ref UInt32 txnID);

    [DllImport("ShaferFilechck.dll", CallingConvention = CallingConvention.Cdecl)]
    public static extern int NSAFeatureStart(byte[] username, byte[] featureCode, byte[] nsaClientData, ref UInt32 txnID);

    [DllImport("ShaferFilechck.dll", CallingConvention = CallingConvention.Cdecl)]
    public static extern int NSAFeatureStop(byte[] username, byte[] featureCode, byte[] nsaClientData, ref UInt32 txnID);

    [DllImport("ShaferFilechck.dll", CallingConvention = CallingConvention.Cdecl)]
    public static extern int NSAException(byte[] username, byte[] exceptionCode, byte[] description, byte[] nsaClientData, ref UInt32 txnID);

    [DllImport("ShaferFilechck.dll", CallingConvention = CallingConvention.Cdecl)]
    public static extern int NSASysInfo(byte[] username, byte[] appLanguage, byte[] version, byte[] edition, byte[] build, byte[] licenseStatus, byte[] nsaClientData, ref UInt32 txnId);

    [DllImport("ShaferFilechck.dll", CallingConvention = CallingConvention.Cdecl)]
    public static extern int NSAAppStart(byte[] username, byte[] nsaClientData, ref UInt32 txnID);

    [DllImport("ShaferFilechck.dll", CallingConvention = CallingConvention.Cdecl)]
    public static extern int NSAAppStop(byte[] username, byte[] nsaClientData, ref UInt32 txnID);

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

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

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

    [DllImport("ShaferFilechck.dll", CallingConvention = CallingConvention.Cdecl)]
    public static extern int NSASetPrivacy(short privSetting);

    [DllImport("ShaferFilechck.dll", CallingConvention = CallingConvention.Cdecl)]
    public static extern int NSASendCache(byte[] username, ref UInt32 txnID);

    [DllImport("ShaferFilechck.dll", CallingConvention = CallingConvention.Cdecl)]
    public static extern int NSAGetStats(out IntPtr returnStats);

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

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

    [DllImport("ShaferFilechck.dll", CallingConvention = CallingConvention.Cdecl)]
    public static extern int NSAGetHostName(out IntPtr hostname);

    [DllImport("ShaferFilechck.dll", CallingConvention = CallingConvention.Cdecl)]
    public static extern int NSAFree(IntPtr myVar);

    [DllImport("ShaferFilechck.dll", CallingConvention = CallingConvention.Cdecl)]
    public static extern int NSLCheckoutTokens(byte[] tokenName, UInt32 tokenAmt, ref int tokenStatus, byte[] licenseNo);

    [DllImport("ShaferFilechck.dll", CallingConvention = CallingConvention.Cdecl)]
    public static extern int NSLConsumeTokens(byte[] tokenName, UInt32 tokenAmt, byte[] licenseNo);

    [DllImport("ShaferFilechck.dll", CallingConvention = CallingConvention.Cdecl)]
    public static extern int NSLGetTokenInfo(byte[] tokenName, ref UInt32 tokenMax, ref UInt32 tokenAmt, ref Int32 tokenStatus);

    [DllImport("ShaferFilechck.dll", CallingConvention = CallingConvention.Cdecl)]
    public static extern int NSLRemoteCallV(byte[] rpcName, out IntPtr ReturnVal, IntPtr rpcVarNames, IntPtr rpcVarVals);

    #region LICENSESTATUS
    public enum LicenseStatus : int
    {
        [Description("Normal LTCO")]
        PROD_LTCO_AUTHORIZED = 51,
        [Description("Online concurrency LTCO")]
        PROD_LTCO_CONCURRENT = 53,
        [Description("NetworkMaster")]
        PROD_LTCO_ACCOUNT = 54,
        [Description("ABL concurrent LTCO")]
        PROD_LTCO_ACCOUNT_CONCURRENT = 55,
        [Description("NetworkMaster")]
        PROD_NETWORK_MASTER = 16,
        [Description("ActivatedNetworkLTCO")]
        PROD_NETWORK_LTCO = 15,
        [Description("ActivatedNetwork")]
        PROD_NETWORK = 14,
        [Description("ActivatedABL")]
        PROD_ABL = 4,
        [Description("ActivatedABLConcurrent")]
        PROD_ABL_CONCURRENT = 5,
        [Description("ActivatedConcurrent")]
        PROD_CONCURRENT = 3,
        [Description("InTrial")]
        PROD_INTRIAL = 2,
        [Description("Authorized")]
        PROD_AUTHORIZED = 1,
        [Description("Undetermined")]
        PROD_UNDETERMINED = 0,
        [Description("Product has expired")]
        PROD_PRODEXPIRED = -1,
        [Description("Backtime Counter Tripped")]
        PROD_BTCOUNTER = -2,
        [Description("Feature not Authorised")]
        PROD_FAETURESWICHEDOFF = -3,
        [Description("Feature/Product not Found")]
        PROD_PRODFEATNOTFOUND = -4,
        [Description("License doesn't verify")]
        PROD_LICENSE_DOESNT_VERIFY = -5,
        [Description("Returned license to server")]
        PROD_RETURNED_LICENSE = -6,
        [Description("Date set back too far")]
        PROD_DATE_SET_BACK_TOO_FAR = -7,
        [Description("Product in Invalid State")]
        PROD_INVALID_STATE = -8,
        [Description("License is a activation receipt")]
        PROD_OFFLINELIC_RECEIPT = -9,
        [Description("No Available Licenses")]
        PROD_NO_LICENSES = -50,
        [Description("Daemon Failed to Verify")]
        PROD_DAEMON_VERIFY_FAILURE = -51,
        [Description("Daemon System ID Failure")]
        PROD_DAEMON_SYSTEMID_FAILURE = -52,
        [Description("Daemon didn't find metadata")]
        PROD_DAEMON_MISSING_META = -53,
        [Description("Shafer marked Simon invalid")]
        PROD_SHAFER_INVALID = -60,
        [Description("ABL cached credentials need validattion")]
        PROD_SHAFER_CREDCHECK = -70,
        [Description("Error start")]
        PROD_SERVER_ERROR_START = 100,
        [Description("Product is InActive")]
        PROD_PRODINACTIVE = -110,
        [Description("Invalid Trial Period")]
        PROD_INVALIDTRIAL = -111,
        [Description("ComputerID has already been activated")]
        PROD_COMPUTERIDALREADYACTIVE = -112,
        [Description("Trial Expired")]
        PROD_EXPIRED = -113,
        [Description("LicenseCode is inactive")]
        PROD_LCINACTIVE = -114,
        [Description("Number of Allowed Activations Exceeded")]
        PROD_NOT_AUTHORIZED = -115,
        [Description("Subscription Expired")]
        PROD_SUBSCRIPTION_EXPIRED = -116,
        [Description("Duplicate Device ID")]
        PROD_DUPLICATE_DEVICE = -117,
        [Description("Too Many Heartbeats Missed (Network)")]
        PROD_MISSED_HEARTBEATS = -200,
        [Description("Seat Revoked By Daemon")]
        PROD_SEAT_REVOKED = -201
    }
    #endregion

    #region FEATSTATUS
    public enum FEATSTATUS : int
    {
        FEATSTATUS_EXPIRED = -5,  //Feature request but license expired
        FEATSTATUS_UNAUTH = -4,      //Feature not authorized for use
        FEATSTATUS_DENIED = -3,      //Feature request denied
        FEATSTATUS_UNKNOWN = -2,     //Unknown Feature requested
        FEATSTATUS_ERROR = -1,       //Error
        FEATSTATUS_UNSET = 0,     //Unset define to 0 explicitly just in case
        FEATSTATUS_AUTH = 1,        //Feature authorized for use
    }
    #endregion

    #region POOLSTATUS
    public enum POOLSTATUS : int
    {
        POOLSTATUS_EXPIRED = -5,  //Resource request but license expired
        POOLSTATUS_UNAUTH = -4,      //Resource not authorized for use
        POOLSTATUS_DENIED = -3,      //Resource request denied
        POOLSTATUS_UNKNOWN = -2,     //Unknown Resource requested
        POOLSTATUS_ERROR = -1,       //Error
        POOLSTATUS_UNSET = 0,     //Unset define to 0 explicitly just in case
        POOLSTATUS_AUTH = 1,        //Resource authorized for use
    }
    #endregion


    public enum PrivacyValues : short
    {
        OFF = 0,
        ON = 1,
        NOT_SET = 2
    }


    public static string GetLicenseType(UInt32 code)
    {
        string type = "";
        switch (code)
        {
            case 0:
                {
                    type = "Unknown";
                    break;
                }
            case 2:
                {
                    type = "Trial";
                    break;
                }
            case 3:
                {
                    type = "Permanent (node-locked)";
                    break;
                }
            case 4:
                {
                    type = "Permanent (concurrent)";
                    break;
                }
            case 5:
                {
                    type = "Subscription (node-locked)";
                    break;
                }
            case 6:
                {
                    type = "Subscription (concurrent)";
                    break;
                }
            case 7:
                {
                    type = "Network License";
                    break;
                }
            case 8:
                {
                    type = "Subscription Network License";
                    break;
                }
            case 9:
                {
                    type = "Network License LTCO";
                    break;
                }
            case 10:
                {
                    type = "Network License (OEM)";
                    break;
                }
            case 11:
                {
                    type = "Account based";
                    break;
                }
            case 12:
                {
                    type = "Subscription (Account based)";
                    break;
                }
            case 13:
                {
                    type = "Concurrent (Account based)";
                    break;
                }
            case 14:
                {
                    type = "Subscription Concurrent (Account based)";
                    break;
                }
            case 15:
                {
                    type = "LTCO";
                    break;
                }
            case 16:
                {
                    type = "Concurrent LTCO";
                    break;
                }
            case 17:
                {
                    type = "Account based LTCO";
                    break;
                }
            case 18:
                {
                    type = "Account based Concurrent LTCO";
                    break;
                }
        }
        return type;
    }

    public static string GetActivationType(UInt32 code)
    {
        string type = "";
        switch (code)
        {
            case 0:
                {
                    type = "Unknown";
                    break;
                }
            case 1:
                {
                    type = "Online";
                    break;
                }
            case 2:
                {
                    type = "Offline";
                    break;
                }
            case 3:
                {
                    type = "Daemon";
                    break;
                }
            case 4:
                {
                    type = "Reserved";
                    break;
                }
        }
        return type;
    }

    public int Init(ref bool badImage)
    {
        var config = new NalpLibConfiguration();
        int shaferError = 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 separate folder for shafer files
        _workDir = Environment.GetFolderPath(Environment.SpecialFolder.ApplicationData) + $"\\Nalpeiron\\{App.AppCode}Shafer";

        //Create parameters key/value pair structure
        Dictionary<string, string> xParms = new Dictionary<string, string>
        {
            //Advnaced settings, for normal operation leave as defaults
            { "LogLevel", "4" }, //set log level, please see documentation
            { "OfflineMode", "0" }, //Select Offline mode, please see documentation
            { "LogDir", _workDir }, //Default Directory, change to specify different location
            { "LogQLen", "300" }, //please see documentation
            { "CacheQLen", "25" }, //please see documentation
            { "SoapThreadsMin", "10" }, //please see documentation
            { "SoapThreadsMax", "10" }, //please see documentation
            { "ProxyIP", "" }, //InternetConnection Proxy IP address, if required
            { "ProxyPort", "" }, //InternetConnection Proxy Port, if required
            { "ProxyUsername", "" }, //InternetConnection Proxy Username, if required
            { "ProxyPassword", "" }, //InternetConnection Proxy Password, if required
            { "NSAEnabled", "1" }, //Enable Analytics
            { "NSLEnabled", "1" }, //Enable Licensing
            { "SecurityValue", securityRandom.ToString() }, //Security Offset if enabled, or 0
            { "WorkDir", _workDir }, //Work Directory
            { "Encrypt", "0" }, //Enable Licensing
            { "DaemonIP", config.DaemonIP ?? string.Empty }, //Daemon IP
            { "DaemonPort", config.DaemonPort ?? string.Empty }, //Daemon Port
            { "DaemonUser", config.DaemonUser ?? string.Empty }, //Daemon User
            { "DaemonPass", config.DaemonPass ?? string.Empty } //Daemon Password
        };

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

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

        return shaferError;
    }

    public int ApplicationStart(string username, string appLanguage, string version, string edition, string build, string licenseStatus)
    {
        int returnValue = 0;
        UInt32 txnID = 0;
        string nsaClientData = "";
        var utfNSAClientData = StringToBytes(nsaClientData);
        var utfUsername = StringToBytes(username);
        returnValue = NSAAppStart(username == "" ? null : utfUsername, nsaClientData == "" ? null : utfNSAClientData, ref _applicationTxnID);
        if (returnValue != 0)
            return returnValue;

        //Call NPSystemInfo to Record System Infomation
        // Convert strings to byte arrays for Unicode compatibility
        var utfAppLanguage = StringToBytes(appLanguage);
        var utfVersion = StringToBytes(version);
        var utfBuild = StringToBytes(build);
        var utfEdition = StringToBytes(edition);
        var utfLicenseStatus = StringToBytes(licenseStatus);
        returnValue = NSASysInfo(username == "" ? null : utfUsername, appLanguage == "" ? null : utfAppLanguage, version == "" ? null : utfVersion, edition == "" ? null : utfEdition, build == "" ? null : utfBuild, licenseStatus == "" ? null : utfLicenseStatus, nsaClientData == "" ? null : utfNSAClientData, ref txnID);
        if (returnValue != 0)
            return returnValue;
        // TxnID = 0;
        //If any records left in cache then send them now
        returnValue = NSASendCache(utfUsername, ref txnID);

        return returnValue;
    }

    public int GetPrivacy()
    {
        return NSAGetPrivacy();
    }

    public int SetPrivacy(PrivacyValues privacy)
    {
        return NSASetPrivacy((short)privacy);
    }

    public int ApplicationEnd(string username)
    {
        //Close NSA, and record Application End
        int returnValue = 0;
        UInt32 txnID = 0;
        string nsaClientData = "";
        var utfNSAClientData = StringToBytes(nsaClientData);
        var utfUsername = StringToBytes(username);
        returnValue = NSASendCache(utfUsername, ref txnID);
        returnValue = NSAAppStop(username == "" ? null : utfUsername, nsaClientData == "" ? null : utfNSAClientData, ref _applicationTxnID);
        if (returnValue != 0)
            return returnValue;

        returnValue = NalpLibClose();
        return returnValue;
    }

    public int ApplicationEndNSL()
    {
        //Close NSL, Don't record Application End
        int returnValue = 0;

        returnValue = NalpLibClose();
        return returnValue;
    }


    public int LogUserOn(string username, ref UInt32 txnID)
    {
        int returnValue = 0;
        // Convert strings to byte arrays for Unicode compatibility
        string nsaClientData = "";
        var utfNSAClientData = StringToBytes(nsaClientData);
        var utfUsername = StringToBytes(username);
        returnValue = NSALogin(username == "" ? null : utfUsername, nsaClientData == "" ? null : utfNSAClientData, ref txnID);
        return returnValue;
    }

    public int LogUserOff(string username, UInt32 txnID)
    {
        int returnValue = 0;
        // Convert strings to byte arrays for Unicode compatibility
        string nsaClientData = "";
        var utfNSAClientData = StringToBytes(nsaClientData);
        var utfUsername = StringToBytes(username);
        returnValue = NSALogout(username == "" ? null : utfUsername, nsaClientData == "" ? null : utfNSAClientData, ref txnID);
        return returnValue;
    }

    public int FeatureStart(string username, string featureCode, ref UInt32 txnID)
    {
        int returnValue = 0;
        // Convert strings to byte arrays for Unicode compatibility
        string nsaClientData = "";
        var utfNSAClientData = StringToBytes(nsaClientData);
        var utfUsername = StringToBytes(username);
        var utfFeatureCode = StringToBytes(featureCode);
        returnValue = NSAFeatureStart(utfUsername, utfFeatureCode, nsaClientData == "" ? null : utfNSAClientData, ref txnID);
        return returnValue;
    }

    public int FeatureEnd(string username, string featureCode, UInt32 txnID)
    {
        int returnValue = 0;
        // Convert strings to byte arrays for Unicode compatibility
        string nsaClientData = "";
        var utfNSAClientData = StringToBytes(nsaClientData);
        var utfUsername = StringToBytes(username);
        var utfFeatureCode = StringToBytes(featureCode);
        returnValue = NSAFeatureStop(utfUsername, utfFeatureCode, nsaClientData == "" ? null : utfNSAClientData, ref txnID);
        return returnValue;
    }

    public int Exception(string username, string exceptionCode, string description)
    {
        int returnValue = 0;
        UInt32 txnID = 0;
        // Convert strings to byte arrays for Unicode compatibility
        string nsaClientData = "";
        var utfNSAClientData = StringToBytes(nsaClientData);
        var utfUsername = StringToBytes(username);
        var utfExceptionCode = StringToBytes(exceptionCode);
        var utfDescription = StringToBytes(description);
        returnValue = NSAException(utfUsername, utfExceptionCode, utfDescription, nsaClientData == "" ? null : utfNSAClientData, ref txnID);
        return returnValue;
    }

    public int Stats(out string stats)
    {
        int returnValue = 0;

        IntPtr i = new IntPtr();

        returnValue = NSAGetStats(out i);
        //Return the stats if call ok
        stats = Marshal.PtrToStringAnsi(i);
        NSAFree(i);
        return returnValue;
    }

    public int GetLicenseInfo(ref UInt32 licenseType, ref UInt32 activationType)
    {
        IntPtr i = new IntPtr();
        IntPtr j = new IntPtr();

        int returnValue = 0;
        returnValue = NSLGetLicenseInfo(out i, out j);
        licenseType = Convert.ToUInt32(i.ToInt32());
        activationType = Convert.ToUInt32(j.ToInt32());
        returnValue -= (int)_securityOffset;
        return returnValue;
    }

    public int GetFilePath(string extension, ref string path)
    {
        int returnValue = 0;
        IntPtr i = new IntPtr();
        var utfExtension = StringToBytes(extension);
        returnValue = NalpGetFilepath(utfExtension, out i);
        returnValue -= (int)_securityOffset;
        path = Marshal.PtrToStringAnsi(i);
        NSLFree(i);
        return returnValue;
    }

    public int NSLGetHostName(ref string hostName)
    {
        int returnValue = 0;

        IntPtr i = new IntPtr();

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

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

    public int NSLGetVMInfo(ref UInt32 isVM, ref string vmType)
    {
        int returnValue = 0;

        IntPtr i = new IntPtr();

        returnValue = NSLGetVMInfo(ref isVM, out i);
        returnValue -= (int)_securityOffset;

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

    public int NSAGetHostName(ref string hostName)
    {
        int returnValue = 0;

        IntPtr i = new IntPtr();

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

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


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

        IntPtr i = new IntPtr();

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

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

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

        IntPtr i = new IntPtr();

        returnValue = NSAGetComputerID(out i);

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

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

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

    public int GetLicense(string licenseCode, string registrationData)
    {
        int returnValue = 0;
        int licenseStatus = 0;
        var utfLicenseCode = StringToBytes(licenseCode);
        var utfRegdata = StringToBytes(registrationData);
        returnValue = NSLGetLicense(licenseCode == "" ? null : utfLicenseCode, ref licenseStatus, utfRegdata);
        returnValue -= (int)_securityOffset;
        //Return the stats if call ok

        return returnValue;
    }

    public int ReturnLicense(string licenseCode)
    {
        int returnValue = 0;
        int licenseStatus = 0;
        // Convert strings to byte arrays for Unicode compatibility
        var utfLicenseCode = StringToBytes(licenseCode);
        returnValue = NSLReturnLicense(utfLicenseCode, ref licenseStatus);
        returnValue -= (int)_securityOffset;
        //Return the stats if call ok

        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 = NSLGetFeatureStatus(utfFeatureName, ref featureStatus);
        returnValue -= (int)_securityOffset;
        //Return the stats if call ok

        return returnValue;
    }

    public int GetTrialExpirySecs(ref int seconds)
    {
        int returnValue = 0;
        int epochSeconds = 0; //Actual Expiry Date in Epoch Seconds

        returnValue = NSLGetTrialExpSec(ref seconds, ref epochSeconds);
        returnValue -= (int)_securityOffset;

        return returnValue;
    }

    public int GetTrialExpiryDate(ref string trialExpiryDate)
    {
        int returnValue = 0;

        IntPtr i = new IntPtr();

        returnValue = NSLGetTrialExpDate(out i);
        returnValue -= (int)_securityOffset;
        trialExpiryDate = Marshal.PtrToStringAnsi(i);
        NSLFree(i);
        return returnValue;
    }

    public int GetSubscriptionExpirySecs(ref int seconds)
    {
        int returnValue = 0;
        int epochSeconds = 0; //Actual Expiry Date in Epoch Seconds

        returnValue = NSLGetSubExpSec(ref seconds, ref epochSeconds);
        returnValue -= (int)_securityOffset;

        return returnValue;
    }

    public int GetSubscriptionExpiryDate(ref string subscriptionExpiryDate)
    {
        int returnValue = 0;

        IntPtr i = new IntPtr();
        returnValue = NSLGetSubExpDate(out i);
        returnValue -= (int)_securityOffset;

        subscriptionExpiryDate = Marshal.PtrToStringAnsi(i);
        NSLFree(i);
        return returnValue;
    }

    public int GetLeaseExpirySecs(ref int seconds)
    {
        int returnValue = 0;
        int epochSeconds = 0; //Actual Expiry Date in Epoch Seconds

        returnValue = NSLGetLeaseExpSec(ref seconds, ref epochSeconds);
        returnValue -= (int)_securityOffset;

        return returnValue;
    }

    public int GetLeaseExpiryDate(ref string leaseExpiryDate)
    {
        int returnValue = 0;

        IntPtr i = new IntPtr();
        returnValue = NSLGetLeaseExpDate(out i);
        returnValue -= (int)_securityOffset;
        leaseExpiryDate = Marshal.PtrToStringAnsi(i);
        NSLFree(i);
        return returnValue;
    }

    public int GetMaintenanceExpirySecs(ref int seconds)
    {
        int returnValue = 0;
        int epochSeconds = 0; //Actual Expiry Date in Epoch Seconds

        returnValue = NSLGetMaintExpSec(ref seconds, ref epochSeconds);
        returnValue -= (int)_securityOffset;

        return returnValue;
    }

    public int GetMaintenanceExpiryDate(ref string maintenanceExpiryDate)
    {
        int returnValue = 0;

        IntPtr i = new IntPtr();
        returnValue = NSLGetMaintExpDate(out i);
        returnValue -= (int)_securityOffset;
        maintenanceExpiryDate = Marshal.PtrToStringAnsi(i);
        NSLFree(i);
        return returnValue;
    }

    public int GetTimeStamp(ref long timestamp)
    {
        int returnValue = 0;
        returnValue = NSLGetTimeStamp(ref timestamp);
        returnValue -= (int)_securityOffset;
        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 = NSLGetUDFValue(utfFieldname, out i);
        returnValue -= (int)_securityOffset;

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

    public int NSLTestInternetConnection()
    {
        int returnValue = 0;
        returnValue = NSLTestConnection2(0,0);
        returnValue -= (int)_securityOffset;
        return returnValue;
    }

    public int NSATestInternetConnection()
    {
        int returnValue = 0;
        returnValue = NSATestConnection();
        returnValue -= (int)_securityOffset;
        return returnValue;
    }

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

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

        return returnValue;
    }

    public int CheckinFloatingFeature(string licenseCode, string featureName)
    {
        int returnValue = 0;
        // Convert strings to byte arrays for Unicode compatibility
        var utfLicenseCode = StringToBytes(licenseCode);
        var utfFeatureName = StringToBytes(featureName);
        returnValue = NSLReturnFeature(utfFeatureName, utfLicenseCode);
        returnValue -= (int)_securityOffset;
        return returnValue;
    }


    public int CheckoutFloatingFeature(string licenseCode, string featureName, ref int featureStatus)
    {
        int returnValue = 0;
        // Convert strings to byte arrays for Unicode compatibility
        var utfLicenseCode = StringToBytes(licenseCode);
        var utfFeatureName = StringToBytes(featureName);
        returnValue = NSLCheckoutFeature(utfFeatureName, ref featureStatus, utfLicenseCode);
        returnValue -= (int)_securityOffset;
        return returnValue;
    }

    public int GetDeactivationCert(string licenseCode, ref string certificate)
    {
        int returnValue = 0;

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

        certificate = Marshal.PtrToStringAnsi(i);
        NSLFree(i);
        return returnValue;
    }

    public int GetActivationCert(string licenseCode, ref string certificate, string regdata)
    {
        int returnValue = 0;

        IntPtr i = new IntPtr();
        // Convert strings to byte arrays for Unicode compatibility
        var utfLicenseCode = StringToBytes(licenseCode);
        var utfRegdata = StringToBytes(regdata);
        returnValue = NSLGetActivationCertReq(utfLicenseCode, utfRegdata, out i);
        returnValue -= (int)_securityOffset;

        certificate = Marshal.PtrToStringAnsi(i);
        NSLFree(i);
        return returnValue;
    }

    public static int GetErrorMessage(int errorCode, ref string errorMessage)
    {
        int returnValue = 0;

        IntPtr i = new IntPtr();
        returnValue = NalpGetErrorMsg(errorCode, out i);

        errorMessage = Marshal.PtrToStringAnsi(i);
        NSLFree(i);
        return returnValue;
    }

    public static int NSLGetLibVersion(ref string version)
    {
        int returnValue = 0;
        IntPtr i = new IntPtr();
        returnValue = NSLGetVersion(out i);
        version = Marshal.PtrToStringAnsi(i);
        NSLFree(i);
        return returnValue;
    }

    public static int NSAGetLibVersion(ref string version)
    {
        int returnValue = 0;
        IntPtr i = new IntPtr();
        returnValue = NSAGetVersion(out i);
        version = Marshal.PtrToStringAnsi(i);
        NSAFree(i);
        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 = NSLImportCertificate(licenseCode == "" ? null : utfLicenseCode, ref licenseStatus, utfCertificate);
        returnValue -= (int)_securityOffset;
        certificate = Marshal.PtrToStringAnsi(i);
        NSLFree(i);
        return returnValue;
    }

    public int CheckinElements(string poolname, UInt32 numToCheckIn, string licenseCode)
    {
        int returnValue = 0;
        // Convert strings to byte arrays for Unicode compatibility
        var utfLicenseCode = StringToBytes(licenseCode);
        var utfPoolname = StringToBytes(poolname);
        returnValue = NSLReturnPool(utfPoolname, numToCheckIn, utfLicenseCode);
        returnValue -= (int)_securityOffset;
        return returnValue;
    }

    public int CheckoutElements(string poolname, UInt32 numToCheckOut, ref int status, string licenseCode)
    {
        int returnValue = 0;
        // Convert strings to byte arrays for Unicode compatibility
        var utfLicenseCode = StringToBytes(licenseCode);
        var utfPoolname = StringToBytes(poolname);
        returnValue = NSLCheckoutPool(utfPoolname, numToCheckOut, ref status, utfLicenseCode);
        returnValue -= (int)_securityOffset;
        return returnValue;
    }

    // Deprecated.  Use GetPoolInfo instead
    public int GetPoolStatus(string poolname, ref UInt32 numCheckedOut, ref int status)
    {
        int returnValue = 0;
        // Convert strings to byte arrays for Unicode compatibility
        var utfPoolname = StringToBytes(poolname);
        returnValue = NSLGetPoolStatus(utfPoolname, ref numCheckedOut, ref status);
        returnValue -= (int)_securityOffset;
        return returnValue;
    }

    public int GetPoolInfo(string poolname, ref UInt32 poolMax, ref UInt32 poolAmt, ref int poolStatus)
    {
        int returnValue = 0;
        // Convert strings to byte arrays for Unicode compatibility
        var utfPoolname = StringToBytes(poolname);
        returnValue = NSLGetPoolInfo(utfPoolname, ref poolMax, ref poolAmt, ref poolStatus);
        returnValue -= (int)_securityOffset;
        return returnValue;
    }

    public int CheckoutTokens(string tokenName, UInt32 numToCheckOut, ref int status, string licenseCode)
    {
        int returnValue = 0;
        // Convert strings to byte arrays for Unicode compatibility
        var utfLicenseCode = StringToBytes(licenseCode);
        var utfTokenname = StringToBytes(tokenName);
        returnValue = NSLCheckoutTokens(utfTokenname, numToCheckOut, ref status, licenseCode == "" ? null : utfLicenseCode);
        returnValue -= (int)_securityOffset;
        return returnValue;
    }

    public int ConsumeTokens(string tokenName, UInt32 numToCheckOut, ref int status, string licenseCode)
    {
        int returnValue = 0;
        // Convert strings to byte arrays for Unicode compatibility
        var utfLicenseCode = StringToBytes(licenseCode);
        var utfTokenname = StringToBytes(tokenName);
        returnValue = NSLConsumeTokens(utfTokenname, numToCheckOut, licenseCode == "" ? null : utfLicenseCode);
        returnValue -= (int)_securityOffset;
        return returnValue;
    }

    public int GetTokenInfo(string tokenName, ref UInt32 tokenMax, ref UInt32 tokenAmt, ref int tokenStatus)
    {
        int returnValue = 0;
        // Convert strings to byte arrays for Unicode compatibility
        var utfTokenname = StringToBytes(tokenName);
        returnValue = NSLGetTokenInfo(utfTokenname, ref tokenMax, ref tokenAmt, ref tokenStatus);
        returnValue -= (int)_securityOffset;
        return returnValue;
    }

    public int GetCredentials(out int lastReturn, out string username, out IntPtr inData, out IntPtr outData)
    {
        int returnValue = 0;

        IntPtr i = new IntPtr();

        returnValue = NSLGetCredentials(out lastReturn, out i, out inData, out outData);
        returnValue -= (int)_securityOffset;

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

    public int SetCredentials(string username, string password)
    {
        return StdCall(() => NSLSetCredentials(StringToBytes(username), StringToBytes(password), null, 0, IntPtr.Zero));
    }

    public int ClearCredentials()
    {
        return StdCall(NSLClearCredentials);
    }

    public int CheckCredentials()
    {
        return StdCall(CheckCredentials);
    }

    private int StdCall(Func<int> func)
    {
        int returnValue = 0;
        returnValue = func();
        returnValue -= (int)_securityOffset;
        return returnValue;
    }

    public int NSLRemoteCallV(string functionName, string[] varNames, string[] varVals, ref string[] rpcReturn)
    {
        int returnValue = 0;
        var utfFunctionName = StringToBytes(functionName);

        IntPtr in1 = new IntPtr(); //Return Value

        IntPtr[] i1 = new IntPtr[varNames.Length + 1];

        for (int i = 0; i < varNames.Length; i++)
        {
            byte[] chars = Encoding.UTF8.GetBytes(varNames[i] + '\0');
            IntPtr ptr = Marshal.AllocHGlobal(chars.Length);
            Marshal.Copy(chars, 0, ptr, chars.Length);
            i1[i] = ptr;
        }
        i1[varNames.Length] = IntPtr.Zero;

        IntPtr[] i2 = new IntPtr[varVals.Length + 1];

        for (int i = 0; i < varVals.Length; i++)
        {
            byte[] chars = System.Text.Encoding.UTF8.GetBytes(varVals[i] + '\0');
            IntPtr ptr = Marshal.AllocHGlobal(chars.Length);
            Marshal.Copy(chars, 0, ptr, chars.Length);
            i2[i] = ptr;
        }
        i2[varVals.Length] = IntPtr.Zero;

        // In order to obtain the address of the IntPtr array, 
        // we must fix it in memory. We do this using GCHandle.
        GCHandle gch = GCHandle.Alloc(i1, GCHandleType.Pinned);
        // pimage_files will point to the head of the IntPtr array.
        IntPtr ia1 = gch.AddrOfPinnedObject();

        GCHandle gch1 = GCHandle.Alloc(i2, GCHandleType.Pinned);
        // pimage_files will point to the head of the IntPtr array.
        IntPtr ia2 = gch1.AddrOfPinnedObject();

        returnValue = NSLRemoteCallV(utfFunctionName, out in1, ia1, ia2);
        returnValue -= (int)_securityOffset;

        // After the API is called, free the GCHandle.
        gch.Free();
        gch1.Free();

        // Free each string contained in the IntPtr array.
        for (int i = 0; i < varNames.Length; i++)
        {
            Marshal.FreeCoTaskMem(i1[i]);
        }

        // Free each string contained in the IntPtr array.
        for (int i = 0; i < varVals.Length; i++)
        {
            Marshal.FreeCoTaskMem(i2[i]);
        }

        return returnValue;
    }

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