using CodeMeter;
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace SewerStammGen.Shared
{
    public class DongleNotFoundException : Exception
    {

    }

    public class WWRuntime : IDisposable
    {
        uint FirmCode;
        uint ProductCode;

        Api cmApi;
        CmCredential cmCred;
        CmAccess2 cmAcc;
        HCMSysEntry hcmse;
        CmBoxInfo cmBoxInfo;
        CmBoxEntry2? BoxContent;
                
        uint ExportCount;

        public WWRuntime(uint ProductCode)
        {
            this.ProductCode = ProductCode;
            FirmCode = 103086;

            cmApi = new Api();
            cmCred = new CmCredential();
            cmAcc = new CmAccess2();

            cmAcc.Credential = cmCred;
            cmAcc.Ctrl |= CmAccess.Option.UserLimit;
            cmAcc.FirmCode = FirmCode;
            cmAcc.ProductCode = ProductCode;

            hcmse = cmApi.CmAccess2(CmAccessOption.Local, cmAcc);
            if(hcmse == null)
            {
                ErrorCodes2 code = cmApi.CmGetLastErrorCode2();
                throw new DongleNotFoundException();                
            }

            cmBoxInfo = new CmBoxInfo();

            CmGetBoxContentsOption boxOptions = new CmGetBoxContentsOption();
            boxOptions = CmGetBoxContentsOption.AllEntries;
            CmBoxEntry2[] tmpBoxContent;

            tmpBoxContent = cmApi.CmGetBoxContents2(hcmse, boxOptions, this.FirmCode, cmBoxInfo);

            CmEntryData[] pCmBoxEntry = (CmEntryData[])cmApi.CmGetInfo(hcmse, CmGetInfoOption.EntryData);
            for (int i = 0; i < pCmBoxEntry.Length; i++)
            {
                switch (pCmBoxEntry[i].Ctrl & 0x0ffff)
                {
                    case (uint)CodeMeter.GlobalEntryOption.UserData:
                        {
                            
                            ExportCount = BitConverter.ToUInt32(pCmBoxEntry[i].Data);
                        }
                        break;

                    case (uint)CodeMeter.GlobalEntryOption.ProtectedData:
                        // Transfer to transformed byte
                        uint length = pCmBoxEntry[i].DataLen;
                        byte[] datas = new byte[length];
                        for (uint f = 0; f < length; f++)
                        {
                            datas[f] = pCmBoxEntry[i].Data[f];
                        }


                        //SyncfusionKey = Encoding.ASCII.GetString(datas);
                        break;
                }
            }

            foreach (CmBoxEntry2 boxes in tmpBoxContent)
            {
                if (boxes.ProductCode == this.ProductCode)
                {
                    this.BoxContent = boxes;
                }
            }
        }

        public bool IsLicensed(byte neededMask)
        {
            uint DongleFeature = GetFeatureMap();
            byte DongleFeatureB = (byte)DongleFeature;

            if ((DongleFeatureB & neededMask) == neededMask)
                return true;
            return false;
        }

        private uint GetFeatureMap()
        {
            if (BoxContent == null) return 0;
            return BoxContent.FeatureMap;
        }

        string GetDongleSerial()
        {
            CmBoxInfo res = (CmBoxInfo)cmApi.CmGetInfo(hcmse, CmGetInfoOption.BoxInfo);
            if (null != res)
            {
                return res.SerialNumber.ToString();
            }
            else
            {
                throw new Exception("Fehler beim aufrufen der Seriennummer");
            }
        }

        int GetExportCount()
        {
            return 0;
        }

        public void IncrementExportCounter()
        {
            ExportCount++;
            writeExportCount(ExportCount);
        }

        public bool CheckDongleVorhanden()
        {
            return hcmse != null;
        }

        public void CleanDongle()
        {
            cmApi.CmRelease(hcmse);
        }

        private void writeExportCount(uint count)
        {
            CmCreatePioUserData cmCreatePioUserData = new CmCreatePioUserData();
            CmProgramUpdateProductItem pcmUpdateProductItem = new CmProgramUpdateProductItem();
            CmCreateItem hcmCreateItem = new CmCreateItem();
            object pvPio = new object();

            CmInternalEntryInfo[] cmInternalEntryData;
            cmInternalEntryData = (CmInternalEntryInfo[])cmApi.CmGetInfo(hcmse, CmGetInfoOption.InternalEntryInfo);

            byte[] newCount = BitConverter.GetBytes(count);
            newCount.CopyTo(cmCreatePioUserData.Data, 0);
            cmCreatePioUserData.DataLen = (ushort)newCount.Length;
            pvPio = cmCreatePioUserData;

            CmCreatePioProductCode cmPC = new CmCreatePioProductCode();
            cmPC.TvbCtrl = (CmCreatePioProductCode.Option)0;
            cmPC.ProductCode = ProductCode;
            cmPC.FirmItemReference = cmInternalEntryData[0].FirmItemReference;
            cmPC.ProductItemReference = cmInternalEntryData[0].ProductItemReference;

            string? error;

            bool res = cmApi.CmCreateProductItemOption(hcmse, CmCreateProductItemOptionOption.Update | CmCreateProductItemOptionOption.ProductCode, cmPC);
            if(!res)
            {
                error = cmApi.CmGetLastErrorText();
                return;
            }
 
            res = cmApi.CmCreateProductItemOption(hcmse, CmCreateProductItemOptionOption.UserData | CmCreateProductItemOptionOption.Terminate, pvPio);
            if(!res)
            {
                error = cmApi.CmGetLastErrorText();
                return;
            }
            

            hcmCreateItem.FirmCode = 103086;
            hcmCreateItem.ProductCode = ProductCode;


            byte[] sequence = cmApi.CmCreateSequence(hcmse, GlobalProgrammingOption.UpdateProductItem, ref hcmCreateItem, pcmUpdateProductItem);

            res = cmApi.CmProgram(hcmse, GlobalProgrammingOption.UpdateProductItem, sequence);

            if (!res)
            {
                error = cmApi.CmGetLastErrorText();
                return;
            } 
        }

        public void Dispose()
        {
            CleanDongle();
        }
    }
}
