using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Diagnostics.Eventing.Reader;
using System.Linq;
using System.Runtime.InteropServices;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
using System.Windows.Forms;

namespace arinc_1553_CSharpGUI
{
    using S16BIT = System.Int16;
    using S32BIT = System.Int32;
    using S64BIT = System.Int64;
    using S8BIT = System.SByte;
    using U16BIT = System.UInt16;
    using U32BIT = System.UInt32;
    using U64BIT = System.UInt64;
    using U8BIT = System.Byte;

    public static class ddc1553Constants
    {
        public static class TTResValues
        {
            /* Time tag resolution values */
            public const U16BIT ACE_TT_2US = 0;         /* 2us Time Tag resolution */
            public const U16BIT ACE_TT_4US = 1;         /* 4us Time Tag resolution */
            public const U16BIT ACE_TT_8US = 2;         /* 8us Time Tag resolution */
            public const U16BIT ACE_TT_16US = 3;        /* 16us Time Tag resolution*/
            public const U16BIT ACE_TT_32US = 4;        /* 32us Time Tag resolution*/
            public const U16BIT ACE_TT_64US = 5;        /* 64us Time Tag resolution*/
            public const U16BIT ACE_TT_TEST = 6;        /* Test Time Tag clock */
            public const U16BIT ACE_TT_EXT = 7;         /* Use external TT clock */
            public const U16BIT ACE_TT_1US = 8;         /* 1us Time Tag resolution */
            public const U16BIT ACE_TT_500NS = 9;       /* 500ns Time Tag resolution */
            public const U16BIT ACE_TT_100NS = 10;      /* 100ns Time Tag resolution */
            public const U16BIT ACE_TT_TEST_CLK = 11;   /* Test Time Tag clock */
        }

        public static class ApiErrorCodes
        {
            public const S16BIT ACE_ERR_SUCCESS = 0;
        }
    }

    /* Test result structure */
    public struct TESTRESULT
    {
        public U16BIT wResult;     /* pass or fail code     */
        public U16BIT wExpData;    /* expected data on fail */
        public U16BIT wActData;    /* actual data on fail   */
        public U16BIT wAceAddr;    /* address of failure    */
        public U16BIT wCount;      /* test count index      */
    }

    public class DDC1553Node
    {
        #region Imported API from DDC SDK Dll
        [DllImport("emacepl.dll")]
        static extern S16BIT aceInitialize(
            S16BIT DevNum,
            U16BIT wAccess,
            U16BIT wMode,
            U32BIT dwMemWrdSize,
            U32BIT dwRegAddr,
            U32BIT dwMemAddr
        );

        [DllImport("emacepl.dll")]
        static extern S16BIT aceFree(
            S16BIT DevNum
        );

        [DllImport("emacepl.dll")]
        static extern S16BIT aceBCDataBlkCreate(
            S16BIT DevNum,
            S16BIT nDataBlkID,
            U16BIT wDataBlkType,
            U16BIT[] pBuffer,
            U16BIT wBufferSize
        );

        [DllImport("emacepl.dll")]
        static extern S16BIT aceBCSetMsgRetry(
            S16BIT DevNum,
            U16BIT wNumOfRetries,
            U16BIT wFirstRetryBus,
            U16BIT wSecondRetryBus,
            U16BIT wReserved
        );

        [DllImport("emacepl.dll")]
        static extern S16BIT aceBCMsgCreateBCtoRT(
            S16BIT DevNum,
            S16BIT nMsgBlkID,
            S16BIT nDataBlkID,
            U16BIT wRT,
            U16BIT wSA,
            U16BIT wWC,
            U16BIT wMsgGapTime,
            U32BIT dwMsgOptions
        );

        [DllImport("emacepl.dll")]
        static extern S16BIT aceBCMsgCreateBcst(
            S16BIT DevNum,
            S16BIT nMsgBlkID,
            S16BIT nDataBlkID,
            U16BIT wSA,
            U16BIT wWC,
            U16BIT wMsgGapTime,
            U32BIT dwMsgOptions
        );

        [DllImport("emacepl.dll")]
        static extern S16BIT aceBCMsgCreateBcstRTtoRT(
            S16BIT DevNum,
            S16BIT nMsgBlkID,
            S16BIT nDataBlkID,
            U16BIT wSARx,
            U16BIT wWC,
            U16BIT wRTTx,
            U16BIT wSATx,
            U16BIT wMsgGapTime,
            U32BIT dwMsgOptions
        );

        [DllImport("emacepl.dll")]
        static extern S16BIT aceBCMsgCreateBcstMode(
            S16BIT DevNum,
            S16BIT nMsgBlkID,
            S16BIT nDataBlkID,
            U16BIT wTR,
            U16BIT wModeCmd,
            U16BIT wMsgGapTime,
            U32BIT dwMsgOptions
        );

        [DllImport("emacepl.dll")]
        static extern S16BIT aceBCOpCodeCreate(
            S16BIT DevNum,
            S16BIT nOpCodeID,
            U16BIT wOpCodeType,
            U16BIT wCondition,
            U32BIT dwParameter1,
            U32BIT dwParameter2,
            U32BIT dwReserved
        );

        [DllImport("emacepl.dll")]
        static extern S16BIT aceBCMsgCreateRTtoBC(
            S16BIT DevNum,
            S16BIT nMsgBlkID,
            S16BIT nDataBlkID,
            U16BIT wRT,
            U16BIT wSA,
            U16BIT wWC,
            U16BIT wMsgGapTime,
            U32BIT dwMsgOptions
        );

        [DllImport("emacepl.dll")]
        static extern S16BIT aceBCMsgCreateRTtoRT(
            S16BIT DevNum,
            S16BIT nMsgBlkID,
            S16BIT nDataBlkID,
            U16BIT wRTRx,
            U16BIT wSARx,
            U16BIT wWC,
            U16BIT wRTTx,
            U16BIT wSATx,
            U16BIT wMsgGapTime,
            U32BIT dwMsgOptions
        );

        [DllImport("emacepl.dll")]
        static extern S16BIT aceBCMsgCreateMode(
            S16BIT DevNum,
            S16BIT nMsgBlkID,
            S16BIT nDataBlkID,
            U16BIT wRT,
            U16BIT wTR,
            U16BIT wModeCmd,
            U16BIT wMsgGapTime,
            U32BIT dwMsgOptions
        );

        [DllImport("emacepl.dll")]
        static extern S16BIT aceBCFrameCreate(
            S16BIT DevNum,
            S16BIT nFrameID,
            U16BIT wFrameType,
            S16BIT[] aOpCodeIDs,
            U16BIT wOpCodeCount,
            U16BIT wMnrFrmTime,
            U16BIT wFlags
        );

        [DllImport("emacepl.dll")]
        static extern S16BIT aceBCInstallHBuf(
            S16BIT DevNum,
            U32BIT dwHBufSize
        );

        [DllImport("emacepl.dll")]
        static extern S16BIT aceCmdWordParse(
            U16BIT wCmdWrd,
            out U16BIT pRT,
            out U16BIT pTR,
            out U16BIT pSA,
            out U16BIT pWC
        );

        [DllImport("emacepl.dll")]
        static extern S16BIT aceBCStart(
            S16BIT DevNum,
            S16BIT nMjrFrmID,
            S32BIT lMjrFrmCount
        );

        [DllImport("emacepl.dll")]
        static extern S16BIT aceBCGetHBufMsgDecoded(
            S16BIT DevNum,
            out MSGSTRUCT pMsg,
            out U32BIT pdwMsgCount,
            out U32BIT pdwMsgLostHBuf,
            U16BIT wMsgLoc
        );

        [DllImport("emacepl.dll")]
        static extern S16BIT aceBCStop(
            S16BIT DevNum
        );

        [DllImport("emacepl.dll")]
        static extern S16BIT aceBCDataBlkWrite(
            S16BIT DevNum,
            S16BIT nDataBlkID,
            U16BIT[] pBuffer,
            U16BIT wBufferSize,
            U16BIT wOffset
        );

        [DllImport("emacepl.dll")]
        static extern IntPtr aceGetMsgTypeString(
                U16BIT wMsgType
            );

        [DllImport("emacepl.dll")]
        static extern IntPtr aceGetBSWErrString(
                U16BIT wMode,
                U16BIT wBlkSts
            );

        [DllImport("emacepl.dll")]
        static extern S16BIT aceErrorStr(
                S16BIT nError,
                out string pBuffer,
                U16BIT wBufSize
            );

        [DllImport("emacepl.dll")]
        static extern S16BIT aceTestRegisters(
                S16BIT DevNum,
                out TESTRESULT pTest
            );

        [DllImport("emacepl.dll")]
        static extern S16BIT aceTestMemory(
                S16BIT DevNum,
                out TESTRESULT pTest,
                U16BIT wValue
            );

        [DllImport("emacepl.dll")]
        static extern S16BIT aceTestIrqs(
                S16BIT DevNum,
                out TESTRESULT pTest
            );

        [DllImport("emacepl.dll")]
        static extern S16BIT aceSetTimeTagRes(
            S16BIT DevNum,
            U16BIT wTTRes);
        #endregion

        /* define DDC SDK constants */
        public const U16BIT ACE_ACCESS_CARD = 0;
        public const U16BIT ACE_MODE_TEST = 0x0000; /* self test mode */
        public const U16BIT ACE_MODE_BC = 0x0001;
        public const U16BIT ACE_BCCTRL_CHL_A = 0x0080;
        public const U16BIT ACE_BCCTRL_CHL_B = 0x0000;
        public const U16BIT ACE_BCCTRL_RETRY_ENA = 0x0100;
        public const U16BIT ACE_RETRY_ONCE = 1;
        public const U16BIT ACE_RETRY_TWICE = 2;
        public const U16BIT ACE_RETRY_SAME = 0;
        public const U16BIT ACE_RETRY_ALT = 1;
        public const U16BIT ACE_OPCODE_XEQ = 0x0001;
        public const U16BIT ACE_CNDTST_ALWAYS = 0x000F;
        public const U16BIT ACE_OPCODE_CAL = 0x0003;
        public const U16BIT ACE_FRAME_MINOR = 0x0002;
        public const U16BIT ACE_FRAME_MAJOR = 0x0000;
        public const U16BIT ACE_BC_BSW_CHNL = 0x2000;
        public const U16BIT ACE_BCCTRL_RT_TO_RT = 0x0001;
        public const U16BIT ACE_BC_MSGLOC_NEXT_PURGE = 0;
        public const U16BIT ACE_BC_MSGLOC_LATEST_PURGE = 2;
        public const S16BIT ACE_ERR_SUCCESS = 0;

        public const U8BIT ACE_TEST_PASSED = 0x00;

        /* define App constants */
        public const S16BIT MJR = 2000;

        S16BIT m_DevNum = -1;
        bool isInitialized = false; // True if aceBCInitialize has been called but not aceFree
        bool isBCSetup = false;      // True if BC messages, data tables and frames have been created
        bool isBCRunning = false;   // True if the aceBCStart has been called and BC is running

        public Thread threadMsgGet;
        public readonly object _arrayLock = new object();

        MajorFrame pFrame;
        MIL1553Form pFormMil1553;
        List<CodedType> m_CodedTypes;

        BuiltInTestForm pFormBuiltInTest;

        SemaphoreSlim APILock;

        public DDC1553Node(S16BIT devNum, BuiltInTestForm pFormBuiltInTestHdl, SemaphoreSlim paraAPILock)
        {
            m_DevNum = devNum;
            pFormBuiltInTest = pFormBuiltInTestHdl;
            APILock = paraAPILock;
        }

        public void InitDDC1553Node(ref MajorFrame majFrame, MIL1553Form pFormMil1553Hdl, List<CodedType> paraCodedTypes)
        {
            pFrame = majFrame;
            pFormMil1553 = pFormMil1553Hdl;
            m_CodedTypes = paraCodedTypes;
        }

        /*******************************************************************************
         * Name:    PrintOutError
         *
         * Description:
         *
         *      This function prints out errors returned from library functions.
         *
         * In   result - the error number
         * Out  none
         ******************************************************************************/
        void PrintOutError
        (
            S16BIT nResult
        )
        {
            pFormBuiltInTest.Invoke(pFormBuiltInTest.mMessageDelegate, GetAceErrorStr(nResult));
        }

        /*******************************************************************************
         * Name:    TestRegisters
         *
         * Description:
         *
         *      Tests register operation on an Enhanced Mini-ACE.
         *
         * In   DevNum      ID associated with the hardware being accessed (0-31)
         * In   sTest       TestResult structure
         * Out  none
         ******************************************************************************/
        void TestRegisters
        (
            S16BIT DevNum,
            out TESTRESULT pTest
        )
        {
            string szBigBuffer;
            S16BIT nResult = 0x0000;

            pFormBuiltInTest.Invoke(pFormBuiltInTest.mMessageDelegate, "Testing.....");

            /* Test card registers */
            nResult = aceTestRegisters(DevNum, out pTest);

            /* Capture error */
            if (nResult != 0)
            {
                pFormBuiltInTest.Invoke(pFormBuiltInTest.mMessageDelegate, "Register Test\n");
                PrintOutError(nResult);
                return;
            }

            /* Display Test results */
            if (pTest.wResult == ACE_TEST_PASSED)
            {
                pFormBuiltInTest.Invoke(pFormBuiltInTest.mMessageDelegate, "Registers Passed test.\n");
            }
            else
            {
                szBigBuffer = "Register Failed test, expected=" + pTest.wExpData.ToString("x4") + " read=" + pTest.wActData.ToString("x4") + "!!!\n";

                pFormBuiltInTest.Invoke(pFormBuiltInTest.mMessageDelegate, szBigBuffer);
            }
        }

        /*******************************************************************************
         * Name:    TestMemory
         *
         * Description:
         *
         *      Tests hardware memory on an Enhanced Mini-ACE.
         *
         * In   DevNum      ID associated with the hardware being accessed (0-31)
         * In   sTest       TestResult structure
         * In   wValue      the value to be written and verified
         * Out  none
         ******************************************************************************/
        void TestMemory
        (
            S16BIT DevNum,
            out TESTRESULT pTest,
            U16BIT wValue
        )
        {
            string szBigBuffer;
            S16BIT nResult = 0x0000;

            pFormBuiltInTest.Invoke(pFormBuiltInTest.mMessageDelegate, "Testing.....");

            /* Test card memory */
            nResult = aceTestMemory(DevNum, out pTest, wValue);

            /* Capture error */
            if (nResult != 0)
            {
                pFormBuiltInTest.Invoke(pFormBuiltInTest.mMessageDelegate, "Memory Test\n");
                PrintOutError(nResult);
                return;
            }

            /* Display test results */
            if (pTest.wResult == ACE_TEST_PASSED)
            {
                pFormBuiltInTest.Invoke(pFormBuiltInTest.mMessageDelegate, "Ram Passed " + wValue.ToString("x4") + " test.\n");
            }
            else
            {
                szBigBuffer = "Ram Failed " + wValue.ToString("x4") + " test, data read = " + pTest.wActData.ToString("x4") + " addr = " + pTest.wAceAddr.ToString() + "!!!\n";

                pFormBuiltInTest.Invoke(pFormBuiltInTest.mMessageDelegate, szBigBuffer);
            }
        }

        /*******************************************************************************
         * Name:    TestInterrupts
         *
         * Description:
         *
         *      Tests hardware interrupts on an Enhanced Mini-ACE.
         *
         * In   DevNum      ID associated with the hardware being accessed (0-31)
         * In   sTest       TestResult structure
         * Out  none
         ******************************************************************************/
        void TestInterrupts
        (
            S16BIT DevNum,
            out TESTRESULT pTest
        )
        {
            string szBigBuffer;
            S16BIT nResult = 0x0000;

            pFormBuiltInTest.Invoke(pFormBuiltInTest.mMessageDelegate, "Testing.....");

            /* Test Card Interrupts */
            nResult = aceTestIrqs(DevNum, out pTest);

            /* Capture error */
            if (nResult != 0)
            {
                pFormBuiltInTest.Invoke(pFormBuiltInTest.mMessageDelegate, "IRQ Test\n");
                PrintOutError(nResult);
                return;
            }

            /* Display test results */
            if (pTest.wResult == ACE_TEST_PASSED)
            {
                pFormBuiltInTest.Invoke(pFormBuiltInTest.mMessageDelegate, "Interrupt Occurred, Passed test.\n");
            }
            else
            {
                szBigBuffer = "Interrupt Test Failure, " + (((pTest.wCount & 1) != 0) ? "NO TimeTag Rollover" : "") + " " + (((pTest.wCount & 2) != 0) ? "NO IRQ" : "") + "!!!\n";

                pFormBuiltInTest.Invoke(pFormBuiltInTest.mMessageDelegate, szBigBuffer);
            }
        }


        public int DoSelfTest(S16BIT deviceNumber)
        {
            S16BIT nResult = 0x0000;
            TESTRESULT sTest;

            /* Initialize Device */
            nResult = aceInitialize(deviceNumber, ACE_ACCESS_CARD, ACE_MODE_TEST, 0, 0, 0);
            if (nResult != 0)
            {
                //qDebug() << "Error! aceInitialize returned " + QString::number(wResult) + "(" + GetAceErrorStr(wResult) + ")";
                MessageBox.Show("aceInitialize Failed", "1553 Card SelfTest", MessageBoxButtons.OK, MessageBoxIcon.Error);
                return -1;
            }

            /* Notify GUI thread */
            pFormBuiltInTest.Invoke(pFormBuiltInTest.mMessageDelegate, "----------------Self Test, MIL-1553 LDN" + deviceNumber + "----------------\n");

            /* Run Card Diagnostic Tests */
            TestRegisters(deviceNumber, out sTest);
            TestMemory(deviceNumber, out sTest, 0x1234);
            TestMemory(deviceNumber, out sTest, 0xaaaa);
            TestMemory(deviceNumber, out sTest, 0xaa55);
            TestMemory(deviceNumber, out sTest, 0x55aa);
            TestMemory(deviceNumber, out sTest, 0x5555);
            TestMemory(deviceNumber, out sTest, 0xffff);
            TestMemory(deviceNumber, out sTest, 0x1111);
            TestMemory(deviceNumber, out sTest, 0x8888);
            TestMemory(deviceNumber, out sTest, 0x0000);
            TestInterrupts(deviceNumber, out sTest);
            //    TestProtocol(deviceNumber, &sTest);
            //    TestVectors(deviceNumber, &sTest);

            pFormBuiltInTest.Invoke(pFormBuiltInTest.mMessageDelegate, "-----------------------------------------\n");

            /* Free Device */
            nResult = aceFree(deviceNumber);
            if (nResult != 0)
            {
                MessageBox.Show("aceFree Failed", "1553 Card SelfTest", MessageBoxButtons.OK, MessageBoxIcon.Error);
                return -1;
            }

            return 0;
        }

        /**
         * @brief This function initializes the 1553 device
         * @return 0 if successful, negative otherwise
         */
        public S16BIT Initialize()
        {
            /* Variables */
            S16BIT wResult = 0x0000;

            if (isInitialized)
            {
                //qDebug() << "Attempt to initialise a node that is not free";
                MessageBox.Show("Attempt to initialise a node that is not free", "1553 Card Initialisation", MessageBoxButtons.OK, MessageBoxIcon.Error);
                return -10000;
            }

            /* Initialize Device */
            wResult = aceInitialize(m_DevNum, ACE_ACCESS_CARD, ACE_MODE_BC, 0, 0, 0);

            if (wResult != 0)
            {
                //qDebug() << "Error! aceInitialize returned " + QString::number(wResult) + "(" + GetAceErrorStr(wResult) + ")";
                MessageBox.Show("aceInitialize Failed", "1553 Card Initialisation", MessageBoxButtons.OK, MessageBoxIcon.Error);
            }
            else
            {
                /* All is well */
                isInitialized = true;
            }


            return wResult;
        }

        /**
         * @brief This function sets up the BC. It does the following:
         *          1. Creates data blocks, message blocks for all messages
         *          2. Creates various Opcodes
         *          3. Creates minor frames and major frame.
         *          4. Installs host buffer
         * @param pFrame
         * @return 0 if successful, negative otherwise
         */
        public S16BIT SetupBC()
        {
            /* Variables */
            S16BIT wResult = 0x0000;
            S16BIT[] aOpCodes = new S16BIT[100];

            for (int i = 0; i < 100; i++)
            {
                aOpCodes[i] = 0;
            }

            /* Buffer containing data for initial values of data blocks */
            U16BIT[] wBuffer = new U16BIT[32]
            {
                0xFFFF, 0x1234, 0x3333, 0x4444, 0x1111, 0x2222, 0x3333, 0x4444,
                0x1111, 0x2222, 0x3333, 0x4444, 0x1111, 0x2222, 0x3333, 0x4444,
                0x1111, 0x2222, 0x3333, 0x4444, 0x1111, 0x2222, 0x3333, 0x4444,
                0x1111, 0x2222, 0x3333, 0x4444, 0x1111, 0x2222, 0x3456, 0x4567
            };

            //memcpy_s(wBuffer, sizeof(wBuffer), 0, 1);
            if (!isInitialized)
            {
                //qDebug() << "Attempt to setup BC on an uninitialised node";
                MessageBox.Show("Attempt to setup BC on an uninitialised node", "SetupBC", MessageBoxButtons.OK, MessageBoxIcon.Error);
                return -10000;
            }

            // Set retry type (same channel or alternate channel). This is a channel wide setting and not message specific 
            switch (pFrame.retryType)
            {
                case "Once_on_Same_Bus":
                    wResult = aceBCSetMsgRetry(m_DevNum, ACE_RETRY_ONCE, ACE_RETRY_SAME, ACE_RETRY_ALT, 0);
                    break;
                case "Once_on_Alt_Bus":
                    wResult = aceBCSetMsgRetry(m_DevNum, ACE_RETRY_ONCE, ACE_RETRY_ALT, ACE_RETRY_ALT, 0);
                    break;
                case "1st_2nd_on_Same_Bus":
                    wResult = aceBCSetMsgRetry(m_DevNum, ACE_RETRY_TWICE, ACE_RETRY_SAME, ACE_RETRY_SAME, 0);
                    break;
                case "1st_2nd_on_Alt_Bus":
                    wResult = aceBCSetMsgRetry(m_DevNum, ACE_RETRY_TWICE, ACE_RETRY_ALT, ACE_RETRY_ALT, 0);
                    break;
                case "1st_on_Same_Bus-2nd_on_Alt_Bus":
                    wResult = aceBCSetMsgRetry(m_DevNum, ACE_RETRY_TWICE, ACE_RETRY_SAME, ACE_RETRY_ALT, 0);
                    break;
                case "1st_on_Alt_Bus-2nd_on_Same_Bus":
                    wResult = aceBCSetMsgRetry(m_DevNum, ACE_RETRY_TWICE, ACE_RETRY_ALT, ACE_RETRY_SAME, 0);
                    break;
                default:
                    //qDebug() << "Warning! Invalid value for retry type found! " << pMsg->retryType;
                    // Use default retry type
                    wResult = aceBCSetMsgRetry(m_DevNum, ACE_RETRY_ONCE, ACE_RETRY_ALT, ACE_RETRY_ALT, 0);
                    break;
            }

            if (wResult != 0)
            {
                //qDebug() << "Error! aceBCSetMsgRetry returned " + QString::number(wResult) + "(" + GetAceErrorStr(wResult) + ")";
                MessageBox.Show("Error! aceBCSetMsgRetry returned " + wResult + "(" + GetAceErrorStr(wResult) + ")", "SetupBC", MessageBoxButtons.OK, MessageBoxIcon.Error);
                return wResult;
            }

            /* Create data blocks, message blocks for all messages */
            S16BIT id = 1;
            foreach (Msg1553 pMsg in pFrame.msgs)
            {
                U32BIT msgOptions = ACE_BCCTRL_CHL_A;

                if (pMsg.bus == "BUS_B")
                {
                    msgOptions = ACE_BCCTRL_CHL_B;
                }

                /* Create Datablock */
                wResult = aceBCDataBlkCreate(m_DevNum, id, 32, wBuffer, 32);
                if (wResult != 0)
                {
                    //qDebug() << "Error! aceBCDataBlkCreate returned " + QString::number(wResult) + "(" + GetAceErrorStr(wResult) + ")";
                    MessageBox.Show("Error! aceBCDataBlkCreate returned " + wResult + "(" + GetAceErrorStr(wResult) + ")", "SetupBC", MessageBoxButtons.OK, MessageBoxIcon.Error);
                    return wResult;
                }

                if (pMsg.retryEnabled)
                {
                    msgOptions |= ACE_BCCTRL_RETRY_ENA;
                }

                /* Create message block */
                if (pMsg.type == "BC --> RT")
                {
                    if (pMsg.rtAddr1 == 31)
                    {
                        wResult = aceBCMsgCreateBcst(
                            m_DevNum,            /* Device number                    */
                            id,                /* Message ID to create             */
                            id,               /* Message will use this data block */
                            pMsg.sa1,                   /* RT subaddress                    */
                            pMsg.wordCount_ModeCode,                  /* Word count 0 means 32 data words */
                            0,                   /* Default message timer            */
                            msgOptions);
                    }
                    else
                    {
                        wResult = aceBCMsgCreateBCtoRT(
                            m_DevNum,            /* Device number                    */
                            id,                /* Message ID to create             */
                            id,               /* Message will use this data block */
                            pMsg.rtAddr1,                   /* RT address                       */
                            pMsg.sa1,                   /* RT subaddress                    */
                            pMsg.wordCount_ModeCode,                  /* Word count 0 means 32 data words */
                            0,                   /* Default message timer            */
                            msgOptions);
                    }
                }
                else if (pMsg.type == "RT --> BC")
                {
                    /* Create message block */
                    wResult = aceBCMsgCreateRTtoBC(
                        m_DevNum,            /* Device number                    */
                        id,                /* Message ID to create             */
                        id,               /* Message will use this data block */
                        pMsg.rtAddr1,                   /* RT address                       */
                        pMsg.sa1,                   /* RT subaddress                    */
                        pMsg.wordCount_ModeCode,                  /* Word count 0 means 32 data words */
                        0,                   /* Default message timer            */
                        msgOptions);
                }
                else if (pMsg.type == "RT --> RT")
                {
                    if (pMsg.rtAddr1 == 31)
                    {
                        /* Create message block */
                        wResult = aceBCMsgCreateBcstRTtoRT(
                            m_DevNum,            /* Device number                    */
                            id,                /* Message ID to create             */
                            id,               /* Message will use this data block */
                            pMsg.sa1,                   /* RT subaddress                    */
                            pMsg.wordCount_ModeCode,                  /* Word count 0 means 32 data words */
                            pMsg.rtAddr2,                   /* RT address                       */
                            pMsg.sa2,                   /* RT subaddress */
                            0,                   /* Default message timer            */
                            msgOptions);
                    }
                    else
                    {
                        /* Create message block */
                        wResult = aceBCMsgCreateRTtoRT(
                            m_DevNum,            /* Device number                    */
                            id,                /* Message ID to create             */
                            id,               /* Message will use this data block */
                            pMsg.rtAddr1,                   /* RT address                       */
                            pMsg.sa1,                   /* RT subaddress                    */
                            pMsg.wordCount_ModeCode,                  /* Word count 0 means 32 data words */
                            pMsg.rtAddr2,                   /* RT address                       */
                            pMsg.sa2,                   /* RT subaddress */
                            0,                   /* Default message timer            */
                            msgOptions);
                    }
                }
                else if (pMsg.type == "MODE")
                {
                    U16BIT wTR = 1;
                    if (pMsg.wordCount_ModeCode == 17)
                    {
                        wTR = 0;
                    }

                    if (pMsg.rtAddr1 == 31)
                    {
                        wResult = aceBCMsgCreateBcstMode(
                            m_DevNum,            /* Device number                    */
                            id,                /* Message ID to create             */
                            id,               /* Message will use this data block */
                            wTR,                   /* T/R */
                            pMsg.wordCount_ModeCode,                  /* Word count 0 means 32 data words */
                            0,                   /* Default message timer            */
                            msgOptions);
                    }
                    else
                    {
                        wResult = aceBCMsgCreateMode(
                            m_DevNum,            /* Device number                    */
                            id,                /* Message ID to create             */
                            id,               /* Message will use this data block */
                            pMsg.rtAddr1,                   /* RT address                       */
                            wTR,                   /* T/R */
                            pMsg.wordCount_ModeCode,                  /* Word count 0 means 32 data words */
                            0,                   /* Default message timer            */
                            msgOptions);
                    }
                }
                else
                {
                    //qDebug() << "Unexpected message type found";
                    MessageBox.Show("Unexpected message type found", "SetupBC", MessageBoxButtons.OK, MessageBoxIcon.Error);
                    return -1001;
                }

                if (wResult != 0)
                {
                    //qDebug() << "Error! aceBCMsgCreate returned " + QString::number(wResult) + "(" + GetAceErrorStr(wResult) + ")";
                    MessageBox.Show("Error! aceBCMsgCreate returned " + wResult + "(" + GetAceErrorStr(wResult) + ")", "SetupBC", MessageBoxButtons.OK, MessageBoxIcon.Error);
                    return wResult;
                }

                pMsg.msgID = id;

                /* Create Opcode */
                wResult = aceBCOpCodeCreate(m_DevNum, id, ACE_OPCODE_XEQ, ACE_CNDTST_ALWAYS, (uint)id, 0, 0);

                if (wResult != 0)
                {
                    //qDebug() << "Error! aceBCOpCodeCreate for XEQ returned " + QString::number(wResult) + "(" + GetAceErrorStr(wResult) + ")";
                    MessageBox.Show("Error! aceBCOpCodeCreate for XEQ returned " + wResult + "(" + GetAceErrorStr(wResult) + ")", "SetupBC", MessageBoxButtons.OK, MessageBoxIcon.Error);
                    return wResult;
                }
                id++;
            }

            /* Create minor frames */
            S16BIT mnFrameId = 0;
            S16BIT[] cOpCodes = new S16BIT[10];

            foreach (MinorFrame mnFrame in pFrame.minorFrames)
            {
                U16BIT i = 0;
                foreach (Msg1553 pMsg in mnFrame.msgs)
                {
                    //S16BIT id2 = GetMsgIdFromName(pMsg.name/*, pFrame*/);
                    S16BIT id2 = pMsg.msgID;
                    if (id2 > 0)
                    {
                        aOpCodes[i++] = id2;
                    }
                }

                /* Create Minor Frame */
                wResult = aceBCFrameCreate(m_DevNum, (S16BIT)(mnFrameId + 1), ACE_FRAME_MINOR, aOpCodes, i, 0, 0);

                if (wResult != 0)
                {
                    //qDebug() << "Error! aceBCFrameCreate returned " + QString::number(wResult) + "(" + GetAceErrorStr(wResult) + ")";
                    MessageBox.Show("Error! aceBCFrameCreate returned " + wResult + "(" + GetAceErrorStr(wResult) + ")", "SetupBC", MessageBoxButtons.OK, MessageBoxIcon.Error);
                    return wResult;
                }

                /* Create call Opcode for Mn frame */
                wResult = aceBCOpCodeCreate(m_DevNum, (S16BIT)(1000 + mnFrameId), ACE_OPCODE_CAL, ACE_CNDTST_ALWAYS, (U32BIT)(mnFrameId + 1), 0, 0);
                if (wResult != 0)
                {
                    //qDebug() << "Error! aceBCOpCodeCreate for CAL returned " + QString::number(wResult) + "(" + GetAceErrorStr(wResult) + ")";
                    MessageBox.Show("Error! aceBCOpCodeCreate for CAL returned " + wResult + "(" + GetAceErrorStr(wResult) + ")", "SetupBC", MessageBoxButtons.OK, MessageBoxIcon.Error);
                    return wResult;
                }

                cOpCodes[mnFrameId] = (S16BIT)(mnFrameId + 1000);
                mnFrameId++;
            }

            /* Create Major Frame */
            wResult = aceBCFrameCreate(m_DevNum, MJR, ACE_FRAME_MAJOR, cOpCodes, (U16BIT)mnFrameId, /*200*/(U16BIT)(pFrame.minorFramePeriod * 10), 0);
            if (wResult != 0)
            {
                //qDebug() << "Error! aceBCFrameCreate for MJR returned " + QString::number(wResult) + "(" + GetAceErrorStr(wResult) + ")";
                MessageBox.Show("Error! aceBCFrameCreate for MJR returned " + wResult + "(" + GetAceErrorStr(wResult) + ")", "SetupBC", MessageBoxButtons.OK, MessageBoxIcon.Error);
                return wResult;
            }

            /* Install host buffer */
            wResult = aceBCInstallHBuf(m_DevNum, 1024 * 1024);
            if (wResult != 0)
            {
                //qDebug() << "Error! aceBCInstallHBuf returned " + QString::number(wResult) + "(" + GetAceErrorStr(wResult) + ")";
                MessageBox.Show("Error! aceBCInstallHBuf returned " + wResult + "(" + GetAceErrorStr(wResult) + ")", "SetupBC", MessageBoxButtons.OK, MessageBoxIcon.Error);
                return wResult;
            }

            /* If we reach here, all is well! */
            isBCSetup = true;

            return ACE_ERR_SUCCESS;
        }

        public S16BIT Free()
        {
            S16BIT wResult;
            wResult = aceFree(m_DevNum);
            isInitialized = false;
            isBCSetup = false;
            return wResult;
        }

        /**
         * @brief This function will start the BC.
         * @param repeatCount Number of times that the frame should be executed
         * @return 0 if successful, negative otherwise
         */
        public S16BIT StartBC(S16BIT repeatCount/*, MajorFrame pFrame*/)
        {
            S16BIT wResult = ACE_ERR_SUCCESS;
            wResult = aceBCStart(m_DevNum, MJR, repeatCount);
            if (wResult != 0)
            {
                //qDebug() << "Error! aceBCStart returned " + QString::number(wResult) + "(" + GetAceErrorStr(wResult) + ")";
                MessageBox.Show("Error! aceBCStart returned " + wResult + "(" + GetAceErrorStr(wResult) + ")", "StartBC", MessageBoxButtons.OK, MessageBoxIcon.Error);
                isBCRunning = false;
            }
            else
            {
                isBCRunning = true;
            }

            /* Create a thread to read BC host buffer msgs */
            threadMsgGet = new Thread(ThreadMsgGet);

            // Set the thread's priority to AboveNormal or Highest - Review
            //threadMsgGet.Priority = ThreadPriority.AboveNormal;

            if(m_DevNum == 1)
            {
                threadMsgGet.Priority = ThreadPriority.Highest;
            }

            threadMsgGet.Start();

            return wResult;
        }

        S16BIT WriteMsg2DataBlk(Msg1553 pMsg, U16BIT[] buffer)
        {
            /* Write the populated buffer to the data block associated with this message */
            S16BIT nResult;
            U16BIT bufSize;

            if(pMsg.wordCount_ModeCode == 0)
            {
                bufSize = 32;
            }
            else
            {
                bufSize = pMsg.wordCount_ModeCode;
            }

            nResult = aceBCDataBlkWrite(m_DevNum, pMsg.msgID, buffer, /*pMsg.wordCount_ModeCode*/bufSize, 0);

            if (nResult != 0)
            {
                //qDebug() << "Error! aceBCDataBlkWrite returned " + QString::number(nResult) + "(" + GetAceErrorStr(nResult) + ")";
            }

            return nResult;
        }

        /*******************************************************************************
         * Name:    ThreadMsgGet
         *
         * Description:
         *
         *      A separate thread to read BC host buffer
         *
         * In   pFrame: major frame
         * Out  none
         ******************************************************************************/
        void ThreadMsgGet()
        {
            S16BIT wResult;
            U32BIT dwMsgCount, dwHBufLost;
            MSGSTRUCT sMsg = new MSGSTRUCT();

            int debugCtr = 0;

            // For all BC->RT messages present in the major frame write the default data to 1553 datablocks
            foreach (Msg1553 pMsg in pFrame.msgs)
            {
                if (pMsg.type == "BC --> RT")
                {
                    U16BIT[] buffer = new U16BIT[64];
                    pMsg.LoadDefaultData();    /* this will load default values into the "value" field of each parameter of this message */

                    /* Prepare a buffer containing reverse Egu converted data that can be written to the 1553 controller data block */
                    pMsg.PopulateDataBlkUpdateBuffer(ref buffer, m_CodedTypes);

                    WriteMsg2DataBlk(pMsg, buffer);
                }
            }

            while (true)
            {
                if(debugCtr % 500 == 0)
                {
                   // Debug.WriteLine($"debugCtr for devNum {m_DevNum} = {debugCtr}");
                }
                debugCtr++;

                /* Check host buffer for msgs */
                wResult = GetHBufMsgDecoded(out sMsg, out dwMsgCount, out dwHBufLost);

                if (wResult != 0)
                {
                    //qDebug() << "C1553DataAcqThread: Error reading host buffer";
                }
                else if (dwMsgCount != 0)
                {
                    bool match = false;

                    /* Find the related Msg1553 object and save this MSGSTRUCT object into it */
                    foreach (Msg1553 pMsg in pFrame.msgs)
                    {
                        if (sMsg.wCmdWrd1Flg == 1)
                        {
                            if (pMsg.cw1 == sMsg.wCmdWrd1)
                            {
                                if (sMsg.wCmdWrd2Flg == 1)   /* Is RT-RT? If yes, both CW1 and CW2 must match */
                                {
                                    if (pMsg.cw2 == sMsg.wCmdWrd2)
                                    {
                                        // TODO: Shouldn't wBCCtrlWrd of sMsg be simply compared with that of pMsg instead of compating with pMsg-Bus? pMsg of Bus indicates
                                        // on which bus the message was actually transmitted but not the bus on which it was first supposed to be transmitted
                                        /* The next step in message idendification is to match the Bus. */
                                        if ((((sMsg.wBCCtrlWrd & 0x0080) == 0x0080) && (pMsg.bus == "BUS_A")) ||       /* Bit 7 of BC Control Word tells bus this msg was first scheduled on. 1 = A, 0 = B */
                                            (((sMsg.wBCCtrlWrd & 0x0080) == 0) && (pMsg.bus == "BUS_B")))
                                        {
                                            match = true;
                                            //qDebug()<< "Match found for " + pMsg->name <<" BCCTRL = " <<sMsg.wBCCtrlWrd << " BSW = " << sMsg.wBlkSts;
                                        }
                                        else
                                        {
                                            match = false;
                                            //qDebug()<< "LDN" << m_pDDC1553Node->m_DevNum << " Bus did not match for msg " + pMsg->name <<" BCCTRL = " <<sMsg.wBCCtrlWrd << " BSW = " << sMsg.wBlkSts;
                                        }
                                    }
                                    else
                                    {
                                        match = false;
                                    }
                                }
                                else
                                {
                                    /* The next step in message idendification is to match the Bus. */
                                    if ((((sMsg.wBCCtrlWrd & 0x0080) == 0x0080) && (pMsg.bus == "BUS_A")) ||       /* Bit 7 of BC Control Word tells bus this msg was first scheduled on. 1 = A, 0 = B */
                                       (((sMsg.wBCCtrlWrd & 0x0080) == 0) && (pMsg.bus == "BUS_B")))
                                    {
                                        match = true;
                                        //qDebug()<< "Match found for " + pMsg->name <<" BCCTRL = " <<sMsg.wBCCtrlWrd << " BSW = " << sMsg.wBlkSts;
                                    }
                                    else
                                    {
                                        match = false;
                                        //qDebug()<< "LDN" << m_pDDC1553Node->m_DevNum << " Bus did not match for msg " + pMsg->name <<" BCCTRL = " <<sMsg.wBCCtrlWrd << " BSW = " << sMsg.wBlkSts;
                                    }
                                }
                            }
                        }

                        /* Save message to Msg1553 */
                        if (match)
                        {
                            // Get last updated TT info for this message
                            MSGSTRUCT prevRunTimeData = new MSGSTRUCT();
                            prevRunTimeData = pMsg.getMsgRunTimeData();

                            // Check if prev wTimeTag is greater than current wTimeTag. If yes, a rollover has occured.
                            // So, increment wTimeTag2
                            if (prevRunTimeData.wTimeTag > sMsg.wTimeTag)
                            {
                                prevRunTimeData.wTimeTag2++;

                                // Check if wTimeTag2 rolled over. If so, increment wTimeTag3
                                if (prevRunTimeData.wTimeTag2 == 0)
                                    prevRunTimeData.wTimeTag3++;
                            }

                            sMsg.wTimeTag2 = prevRunTimeData.wTimeTag2;
                            sMsg.wTimeTag3 = prevRunTimeData.wTimeTag3;

                            lock (_arrayLock)
                            {
                                pMsg.setMsgRuntimeData(sMsg);   // doubt

                                /* Engineering unit conversion */
                                pMsg.DoEguConversionForAllParams(m_CodedTypes); // doubt
                            }

                            break;
                        }
                    }

                    if (!match)
                    {
                        //qDebug() << "LDN" << m_pDDC1553Node->m_DevNum << " No match for CW = 0x" << QString("%1").arg(sMsg.wCmdWrd1, 0, 16).toUpper();
                    }

                }

                /* Display msg lost count */
                if (dwHBufLost > 0)
                {
                    //qDebug() << "C1553DataAcqThread: Warning! Some messages lost in Bus" + QString::number(m_lane) + " HBUF";
                }

                Thread.Sleep(0);    // Very imp. to give '0'
            }
        }

        /**
         * @brief This function will stop the BC.
         * @return 0 if successful, negative otherwise
         */
        public S16BIT StopBC()
        {
            S16BIT retVal;

            /* Stop Thread */
            threadMsgGet.Abort();

            /* Wait for a while to allow the thread to exit */
            Thread.Sleep(100);

            retVal = aceBCStop(m_DevNum);
            isBCRunning = false;
            return retVal;
        }

        public S16BIT GetHBufMsgDecoded(out MSGSTRUCT p1553Msg, out U32BIT pdwMsgCount, out U32BIT pdwLostCount)
        {
            /* Check host buffer for msgs */
            S16BIT nResult;

            // Whether to use semaphore or not - Review
            //APILock.Wait();

            nResult = aceBCGetHBufMsgDecoded(m_DevNum, out p1553Msg, out pdwMsgCount, out pdwLostCount, ACE_BC_MSGLOC_NEXT_PURGE);

            //APILock.Release();

            if (nResult != 0)
            {
                //qDebug() << "Error! aceBCGetHBufMsgDecoded returned " + QString::number(nResult) + "(" + GetAceErrorStr(nResult) + ")";
                MessageBox.Show("Error! aceBCGetHBufMsgDecoded returned " + nResult + "(" + GetAceErrorStr(nResult) + ")", "GetHBufMsgDecoded", MessageBoxButtons.OK, MessageBoxIcon.Error);
                return nResult;
            }

            /* Display msg lost count */
            if (pdwLostCount > 0)
            {
                //printf("Warning: aceBCGetHBufMsgDecoded Number of msgs lost %u\n", (int)*pdwLostCount);
            }

            return nResult;
        }

        bool getIsBCRunning()
        {
            return isBCRunning;
        }

        bool getIsInitialized()
        {
            return isInitialized;
        }

        bool getIsBCSetup()
        {
            return isBCSetup;
        }

        string GetAceErrorStr(S16BIT wResult)
        {
            string strBuf;
            //APILock.Wait();
            aceErrorStr(wResult, out strBuf, 250);
            //APILock.Release();
            return strBuf;
        }

        //public static string GetAceBSWExtErrorStr(U16BIT wBlkSts)
        public string GetAceBSWExtErrorStr(U16BIT wBlkSts)
        {
            //APILock.Wait();
            IntPtr ptr = aceGetBSWErrString(ACE_MODE_BC, wBlkSts);
            //APILock.Release();
            string errStr = Marshal.PtrToStringAnsi(ptr);
            string extErrStr = "Unknown error";

            /* Fill BSW extended erorr strings into dictionary */
            if (errStr == "INVWD")
                extErrStr = "Invalid Word";
            else if (errStr == "INSYN ")
                extErrStr = "Invalid Sync in Word";
            else if (errStr == "WDCNT ")
                extErrStr = "Invalid Word Count";
            else if (errStr == "NOGAP ")
                extErrStr = "No Gap in RT Response";
            else if (errStr == "LPTST ")
                extErrStr = "Loop Test Failed";
            else if (errStr == "NORES ")
                extErrStr = "No Response from RT";
            else if (errStr == "FORMT ")
                extErrStr = "Format Error";
            else if (errStr == "ILCMD ")
                extErrStr = "Message Illegalized";
            else if (errStr == "RTRTG ")
                extErrStr = "Format Error in RT-RT Message";
            else if (errStr == "RTRTC ")
                extErrStr = "RTRTC";
            else if (errStr == "CMDER ")
                extErrStr = "Invalid Command";

            return extErrStr;
        }

        public S16BIT SetTimeTagRes(U16BIT wTTRes)
        {
            return aceSetTimeTagRes(m_DevNum, wTTRes);
        }
    }
}
