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

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

    /* Test result structure */
    public struct TESTRESULT_429
    {
        public U16BIT wResult;     /* pass or fail code     */
        public U32BIT dwExpData;   /* expected data on fail */
        public U32BIT dwActData;   /* actual data on fail   */
        public U32BIT dwAceAddr;   /* address of failure    */
        public U32BIT dwCount;     /* test count index      */
    }

    /* Channel Count struct */
    public struct CHANCOUNT_t
    {
        public U8BIT bTx;
        public U8BIT bRx;
        public U8BIT bGroup;
        public U8BIT bDiscrete;
        public U8BIT bAvionic;
        public U8BIT bBoardModel;
        public U8BIT b1553;
        public U8BIT a429Prog;
        public U8BIT bUart;
        public U8BIT RS232;
        public U8BIT RS485;
        public U8BIT CanBus;
        public U8BIT a717Rx;
        public U8BIT a717Tx;
        public U8BIT a717Prog;
        public U8BIT bDioTt;
    }

    public struct RX_HBUF_MESSAGE
    {
        public U32BIT u32Data;
        public U32BIT u32StampHigh;
        public U32BIT u32StampLow;
    }

    public enum Speed { DD429_LOW_SPEED = 0, DD429_HIGH_SPEED, DD429_ARINC575 };
    public enum Parity { DD429_NO_PARITY = 0, DD429_ODD_PARITY, DD429_EVEN_PARITY };

    public enum TimeStampFormat
    {
        TT48 = 0,
        IRIG_A,
        IRIG_B,
        IRIG_C,
        IRIG_D,
        IRIG_B_ENHANCED,
        MAX_TTFORMAT
    }

    public enum TimeStampRollover
    {
        TTRO_16 = 0,
        TTRO_17,
        TTRO_18,
        TTRO_19,
        TTRO_20,
        TTRO_21,
        TTRO_22,
        TTRO_48,
        MAX_TTRO
    }

    public enum TimeStampResolution
    {
        TTRES_64 = 0,
        TTRES_32,
        TTRES_16,
        TTRES_8,
        TTRES_4,
        TTRES_2,
        TTRES_1,
        TTRES_500,
        TTRES_100,
        MAX_TTRES
    };

    public class DDCARINC429
    {
        #region Imported API from DDC SDK Dll
        [DllImport("DD42992.dll")]
        static extern S16BIT InitCard(
            S16BIT s16Card);

        [DllImport("DD42992.dll")]
        static extern S16BIT GetChannelCount(
            S16BIT s16Card,
            ref CHANCOUNT_t refChanCount);

        [DllImport("DD42992.dll")]
        static extern S16BIT FreeCard(
            S16BIT s16Card);

        [DllImport("DD42992.dll")]
        static extern S16BIT EnableTx(
            S16BIT s16Card,
            S16BIT s16Transmitter,
            S16BIT s16Enable
            );

        [DllImport("DD42992.dll")]
        static extern S16BIT GetTxStatus(
            S16BIT s16Card,
            S16BIT s16Transmitter
            );

        [DllImport("DD42992.dll")]
        static extern S16BIT SetTxSpeed(
            S16BIT s16Card,
            S16BIT s16Transmitter,
            S16BIT s16Speed
            );

        [DllImport("DD42992.dll")]
        static extern S16BIT LoadTxQueueOne(
            S16BIT s16Card,
            S16BIT s16Transmitter,
            U32BIT u32Data
            );

        [DllImport("DD42992.dll")]
        static extern S16BIT InstallFifoRxHostBuffer(
            S16BIT s16Card,
            U32BIT u32Reserved
            );

        [DllImport("DD42992.dll")]
        static extern S16BIT SetRxChannelSpeed(
            S16BIT s16Card,
            S16BIT s16Receiver,
            S16BIT s16Speed
            );

        [DllImport("DD42992.dll")]
        static extern S16BIT SetRxChannelParity(
            S16BIT s16Card,
            S16BIT s16Receiver,
            S16BIT s16Parity
            );

        [DllImport("DD42992.dll")]
        static extern S16BIT SetRxChannelMode(
            S16BIT s16Card,
            S16BIT s16Receiver,
            S16BIT s16Mode
            );

        [DllImport("DD42992.dll")]
        static extern S16BIT EnableRxHostBuffer(
            S16BIT s16Card,
            S16BIT s16Receiver,
            S16BIT u32Reserved
            );

        [DllImport("DD42992.dll")]
        static extern S16BIT EnableFilter(
            S16BIT s16Card,
            S16BIT s16Receiver,
            S16BIT s16Enable
            );

        [DllImport("DD42992.dll")]
        static extern S16BIT EnableRx(
            S16BIT s16Card,
            S16BIT s16Receiver,
            S16BIT s16Enable
            );

        [DllImport("DD42992.dll")]
        static extern S16BIT GetRxStatus(
            S16BIT s16Card,
            S16BIT s16Receiver
            );

        [DllImport("DD42992.dll")]
        static extern S16BIT DisableRxHostBuffer(
            S16BIT s16Card,
            S16BIT s16Receiver
            );

        [DllImport("DD42992.dll")]
        static extern S16BIT ResetRxChannel(
            S16BIT s16Card,
            S16BIT s16Receiver
            );

        [DllImport("DD42992.dll")]
        static extern S16BIT UninstallFifoRxHostBuffer(
            S16BIT s16Card
            );

        [DllImport("DD42992.dll")]
        static extern S16BIT ReadRxHostBuffer(
            S16BIT s16Card,
            S16BIT s16Receiver,
            U16BIT u16NumMsgsToRead,
            ref U16BIT u16NumMsgsRead,
            [Out] RX_HBUF_MESSAGE[] sMessages
            );

        [DllImport("DD42992.dll")]
        static extern S16BIT ConfigTimeStamp(
            S16BIT s16Card,
            U8BIT u8Format,
            U8BIT u8Rollover,
            U8BIT u8Resolution
            );

        [DllImport("DD42992.dll")]
        static extern S16BIT ResetTimeStamp(
            S16BIT s16Card
            );

        [DllImport("DD42992.dll")]
        static extern S16BIT EnableTimeStamp(
            S16BIT s16Card,
            S16BIT s16Receiver,
            S16BIT s16Enable
        );

        [DllImport("DD42992.dll")]
        static extern S16BIT dd429X_TestRegisters(
            S16BIT s16Card,
            out TESTRESULT_429 pTest
        );

                /*[DllImport("DD42992.dll")]
                static extern S16BIT GetErrorMsg(
                    S16BIT s16ErrorNumber,
                    out string szErrorMessage
                );*/

        [DllImport("DD42992.dll")]
        static extern void PrintErrorMsg(
            S16BIT s16ErrorNumber
        );

        [DllImport("DD42992.dll")]
        static extern S16BIT dd429X_TestMemory(
            S16BIT s16Card,
            out TESTRESULT_429 pTest,
            U32BIT u32Value
        );

        [DllImport("DD42992.dll")]
        static extern S16BIT AddRepeated(
            S16BIT s16Card,
            S16BIT s16Transmitter,
            U32BIT u32Data,
            S16BIT s16Frequency,
            S16BIT s16Offset
            );

        [DllImport("DD42992.dll")]
        static extern S16BIT DelRepeated(
            S16BIT s16Card,
            S16BIT s16Transmitter,
            S16BIT s16LabelSdi
            );

        #endregion

        /* define DDC SDK constants */
        public const S16BIT ERR_SUCCESS = 0;
        public const S16BIT DD429_DISABLE = 0;
        public const S16BIT DD429_ENABLE = 1;
        public const S16BIT FIFO_MODE = 1;
        public const S16BIT ERR_OVERFLOW = -200;
        public const S16BIT ERR_RX_HBUF_OVERFLOW = -222;
        public const U16BIT MAX_NUM_MESSAGES_TO_READ = 3069;

        CHANCOUNT_t sChanCount = new CHANCOUNT_t();
        S16BIT s16Result, s16Card;
        U8BIT u8Chan;
        public U8BIT u8NumTx, u8NumRx;
        RX_HBUF_MESSAGE[] sRxHbufMessages = new RX_HBUF_MESSAGE[MAX_NUM_MESSAGES_TO_READ];

        public Thread threadRx;

        ARINC429Form pForm1;

        List<MsgArinc429> pArincMsgs;
        List<CodedType> m_CodedTypes;

        BuiltInTestForm pFormBuiltInTest;

        ArincConfiguration m_arincConfig;

        U8BIT timeTagResolution = (U8BIT)TimeStampResolution.TTRES_100;
        public readonly object _arrayLock = new object();

        public DDCARINC429(S16BIT devNum, BuiltInTestForm pFormBuiltInTestHdl)
        {
            s16Card = devNum;
            pFormBuiltInTest = pFormBuiltInTestHdl;
        }

        public void InitDDCARINC429(ref List<MsgArinc429> arincMsgs, ARINC429Form pFormArinc429Hdl, List<CodedType> paraCodedTypes, S32BIT arincConfig)
        {
            pArincMsgs = arincMsgs;
            pForm1 = pFormArinc429Hdl;
            m_CodedTypes = paraCodedTypes;

            m_arincConfig = (ArincConfiguration) arincConfig;
        }

        /*******************************************************************************
        * Name:    TestRegisters
        *
        * Description:
        *
        *      Tests register operation.
        *
        * In   DevNum      ID associated with the hardware being accessed (0-31)
        * In   pTest       TestResult structure
        * Out  return      error value
        ******************************************************************************/
        S16BIT TestRegisters(S16BIT DevNum, out TESTRESULT_429 pTest)
        {
            S16BIT nResult = 0x0000;

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

            nResult = dd429X_TestRegisters(DevNum, out pTest);
            if (ERR_SUCCESS != nResult)
            {
                PrintErrorMsg(nResult);
                pFormBuiltInTest.Invoke(pFormBuiltInTest.mMessageDelegate, "\n");
                return nResult;
            }

            if (0 == pTest.wResult)
            {   /*Pass*/
                pFormBuiltInTest.Invoke(pFormBuiltInTest.mMessageDelegate, "Registers Passed test.\n");
                return nResult;
            }
            else
            {   /*Fail*/
                pFormBuiltInTest.Invoke(pFormBuiltInTest.mMessageDelegate, "Register Failed test, expected=" + pTest.dwExpData.ToString("X8") + " read=" + pTest.dwActData.ToString("X8") + "\n");
                return (S16BIT)pTest.wResult;
            }
        }

        /*******************************************************************************
        * Name:    TestMemory
        *
        * Description:
        *
        *      Tests hardware memory.
        *
        * In   DevNum      ID associated with the hardware being accessed (0-31)
        * In   pTest       TestResult structure
        * In   dwValue     the value to written and verified
        * Out  return      error value
        ******************************************************************************/
        S16BIT TestMemory(S16BIT DevNum, out TESTRESULT_429 pTest, U32BIT dwValue)
        {
            S16BIT nResult = 0x0000;

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

            nResult = dd429X_TestMemory(DevNum, out pTest, dwValue);
            if (ERR_SUCCESS != nResult)
            {
                PrintErrorMsg(nResult);
                pFormBuiltInTest.Invoke(pFormBuiltInTest.mMessageDelegate, "\n");
                return nResult;
            }

            if (0 == pTest.wResult)
            {   /*Pass*/
                pFormBuiltInTest.Invoke(pFormBuiltInTest.mMessageDelegate, "Ram Passed 0x" + dwValue.ToString("X8") + " test.\n");
                return nResult;
            }
            else
            {   /*Fail*/
                pFormBuiltInTest.Invoke(pFormBuiltInTest.mMessageDelegate, "Ram Failed 0x" + dwValue.ToString("X8") + " test, data read = 0x" + pTest.dwActData.ToString("X8") + " addr = " + pTest.dwAceAddr.ToString() + "\n");
                return (S16BIT)pTest.wResult;
            }
        }

        public int DoSelfTest(S16BIT deviceNumber)
        {
            U32BIT u32Loop;
            U32BIT [] u32Data = { 0x12345678, 0xFFFFFFFF, 0x11111111, 0x00000000, 0x55555555, 0xAA55AA55, 0x88888888, 0x55AA55AA, 0xAAAAAAAA, 0xA1ECB01A };
            S16BIT wStatus;
            S16BIT nResult;
            TESTRESULT_429 sTest = new TESTRESULT_429();

            nResult = 0;

            /* Initialize device */
            if ((wStatus = InitCard(deviceNumber)) != 0)
            {
                MessageBox.Show("InitCard Failed", "ARINC Card SelfTest", MessageBoxButtons.OK, MessageBoxIcon.Error);
                PrintErrorMsg(wStatus);
                return -1;
            }

            pFormBuiltInTest.Invoke(pFormBuiltInTest.mMessageDelegate, "----------------Self Test, ARINC-429 LDN" + deviceNumber + "----------------\n");

            /*Test Registers*/
            nResult = TestRegisters(deviceNumber, out sTest);

            /*Test Memory*/
            for (u32Loop = 0; u32Loop < /*(sizeof(u32Data) / sizeof(u32Data[0]))*/10; u32Loop++)
            {
                nResult = TestMemory(deviceNumber, out sTest, u32Data[u32Loop]);
            }

            /*Test interrupt*/
            //nResult = TestInterrupts(Card);

            pFormBuiltInTest.Invoke(pFormBuiltInTest.mMessageDelegate, "\n");
            pFormBuiltInTest.Invoke(pFormBuiltInTest.mMessageDelegate, "=================================================================\n");
            pFormBuiltInTest.Invoke(pFormBuiltInTest.mMessageDelegate, " Tests Complete\n");
            if (nResult != 0)
                pFormBuiltInTest.Invoke(pFormBuiltInTest.mMessageDelegate, " Test(s) FAILED, see above for further details\n");
            else
                pFormBuiltInTest.Invoke(pFormBuiltInTest.mMessageDelegate, " All tests PASSED.\n");
            pFormBuiltInTest.Invoke(pFormBuiltInTest.mMessageDelegate, "=================================================================\n");

            /* Shutdown device */
            FreeCard(deviceNumber);

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

            return 0;
        }

        public S16BIT FreeCard()
        {
            // Disable transmit on all Tx channels
            for (u8Chan = 1; u8Chan <= u8NumTx; u8Chan++)
            {
                s16Result = DisableTx(u8Chan);
                if (s16Result != ERR_SUCCESS)
                {
                    MessageBox.Show("Disable Tx Failed for channel " + u8Chan.ToString(), "Free Card", MessageBoxButtons.OK, MessageBoxIcon.Error);
                    return s16Result;
                }
            }

            // Disable receive on all Rx channels
            for (u8Chan = 1; u8Chan <= u8NumRx; u8Chan++)
            {
                s16Result = DisableRx(u8Chan);
                if (s16Result != ERR_SUCCESS)
                {
                    MessageBox.Show("Disable Rx Failed for channel " + u8Chan.ToString(), "Free Card", MessageBoxButtons.OK, MessageBoxIcon.Error);
                    return s16Result;
                }
            }

            s16Result = UninstallFifoRxHostBuffer(s16Card);
            if (s16Result != ERR_SUCCESS)
            {
                MessageBox.Show("UninstallFifoRxHostBuffer Failed");
                return s16Result;
            }
            else
            {
                // MessageBox.Show("UninstallFifoRxHostBuffer success!");
            }

            /* Free device */
            s16Result = FreeCard(s16Card);
            if (s16Result != ERR_SUCCESS)
            {
                MessageBox.Show("FreeCard Failed");
                return s16Result;
            }

            return ERR_SUCCESS;
        }

        public S16BIT InitCard()
        {
            /* Initialize device */
            s16Result = InitCard(s16Card);
            if (s16Result != ERR_SUCCESS)
            {
                MessageBox.Show("InitCard Failed", "ARINC 429 Card Initialisation", MessageBoxButtons.OK, MessageBoxIcon.Error);
                return s16Result;
            }
            else
            {
                // MessageBox.Show("InitCard success!");
            }

            /* Configure time stamp feature to 48 bit, roll over at max. value, and 100ns resolution */
            s16Result = ConfigTimeStamp(s16Card, (U8BIT)TimeStampFormat.TT48, (U8BIT)TimeStampRollover.TTRO_48, timeTagResolution);
            if (s16Result != ERR_SUCCESS)
            {
                MessageBox.Show("ConfigTimeStamp Failed", "ARINC 429 Card Initialisation", MessageBoxButtons.OK, MessageBoxIcon.Error);
                return s16Result;
            }
            else
            {
                // MessageBox.Show("ConfigTimeStamp success!");
            }

            /* Clear timer */
            s16Result = ResetTimeStamp(s16Card);
            if (s16Result != ERR_SUCCESS)
            {
                MessageBox.Show("ResetTimeStamp Failed", "ARINC 429 Card Initialisation", MessageBoxButtons.OK, MessageBoxIcon.Error);
                return s16Result;
            }
            else
            {
                // MessageBox.Show("ResetTimeStamp success!");
            }

            /* Determine device channel count */
            s16Result = GetChannelCount(s16Card, ref sChanCount);
            if (s16Result != ERR_SUCCESS)
            {
                MessageBox.Show("GetChannelCount Failed", "ARINC 429 Card Initialisation", MessageBoxButtons.OK, MessageBoxIcon.Error);
                return s16Result;
            }
            else
            {
                // MessageBox.Show("GetChannelCount success!");
                u8NumTx = sChanCount.bTx;
                u8NumRx = sChanCount.bRx;

                /* If no fixed tx channels, check for existance of programmable */
                if (u8NumTx == 0)
                {
                    MessageBox.Show("ARINC 429 card has no fixed tx channels!");

                    if (sChanCount.a429Prog > 0)
                        u8NumTx = sChanCount.a429Prog;
                }

                /* If no fixed rx channels, check for existance of programmable */
                if (u8NumRx == 0)
                {
                    MessageBox.Show("ARINC 429 card has no fixed rx channels!");

                    if (sChanCount.a429Prog > 0)
                        u8NumRx = sChanCount.a429Prog;
                }

                s16Result = InstallFifoRxHostBuffer(s16Card, 0);
                if (s16Result != ERR_SUCCESS)
                {
                    MessageBox.Show("InstallFifoRxHostBuffer Failed", "ARINC 429 Card Initialisation", MessageBoxButtons.OK, MessageBoxIcon.Error);
                    return s16Result;
                }
                else
                {
                    // MessageBox.Show("InstallFifoRxHostBuffer success!");
                }

                // Enable transmit on all Tx channels
                for (u8Chan = 1; u8Chan <= u8NumTx; u8Chan++)
                {
                    s16Result = EnableTx(u8Chan);
                    if (s16Result != ERR_SUCCESS)
                    {
                        MessageBox.Show("Enable Tx Failed for channel " + u8Chan.ToString(), "ARINC 429 Card Initialisation", MessageBoxButtons.OK, MessageBoxIcon.Error);
                        return s16Result;
                    }
                }

                // Enable receive on all Rx channels
                for (u8Chan = 1; u8Chan <= u8NumRx; u8Chan++)
                {
                    s16Result = EnableRx(u8Chan);
                    if (s16Result != ERR_SUCCESS)
                    {
                        MessageBox.Show("Enable Rx Failed for channel " + u8Chan.ToString(), "ARINC 429 Card Initialisation", MessageBoxButtons.OK, MessageBoxIcon.Error);
                        return s16Result;
                    }
                }

                // MessageBox.Show("ARINC 429 card initialisation completed successfully", "ARINC 429 Card Initialisation", MessageBoxButtons.OK, MessageBoxIcon.Information);

                return ERR_SUCCESS;
            }
        }

        public S16BIT EnableTx(U8BIT chanNo)
        {
            /* Initialize transmitters on the selected Tx channel */
            s16Result = EnableTx(s16Card, chanNo, DD429_ENABLE);
            if(m_arincConfig == ArincConfiguration.LowSpeed_Channel1 || m_arincConfig == ArincConfiguration.LowSpeed_Channel2 || m_arincConfig == ArincConfiguration.LowSpeed_Channel1_2)
            {
                s16Result += SetTxSpeed(s16Card, chanNo, (S16BIT)Speed.DD429_LOW_SPEED);
            }
            else
            {
                s16Result += SetTxSpeed(s16Card, chanNo, (S16BIT)Speed.DD429_HIGH_SPEED);
            }

            if (s16Result != ERR_SUCCESS)
            {
                MessageBox.Show("Enable Tx Failed", "ARINC 429 Tx Channel Enable", MessageBoxButtons.OK, MessageBoxIcon.Error);
                return s16Result;
            }

            return ERR_SUCCESS;
        }

        public S16BIT DisableTx(U8BIT chanNo)
        {
            /* Disable transmitters on the selected Tx channel */
            s16Result = EnableTx(s16Card, chanNo, DD429_DISABLE);

            if (s16Result != ERR_SUCCESS)
            {
                MessageBox.Show("Disable Tx Failed", "ARINC 429 Tx Channel Disable", MessageBoxButtons.OK, MessageBoxIcon.Error);
                return s16Result;
            }

            return ERR_SUCCESS;
        }

        public S16BIT EnableRx(U8BIT chanNo)
        {
            /* Initialize the selected Rx channel */

            if (m_arincConfig == ArincConfiguration.LowSpeed_Channel1 || m_arincConfig == ArincConfiguration.LowSpeed_Channel2 || m_arincConfig == ArincConfiguration.LowSpeed_Channel1_2)
            {
                s16Result = SetRxChannelSpeed(s16Card, chanNo, (S16BIT)Speed.DD429_LOW_SPEED);
            }
            else
            {
                s16Result = SetRxChannelSpeed(s16Card, chanNo, (S16BIT)Speed.DD429_HIGH_SPEED);
            }
            s16Result += SetRxChannelParity(s16Card, chanNo, (S16BIT)Parity.DD429_NO_PARITY);
            s16Result += SetRxChannelMode(s16Card, chanNo, FIFO_MODE);
            s16Result += EnableRxHostBuffer(s16Card, chanNo, 0);
            s16Result += EnableTimeStamp(s16Card, chanNo, DD429_ENABLE);
            s16Result += EnableFilter(s16Card, chanNo, DD429_DISABLE);
            s16Result += EnableRx(s16Card, chanNo, DD429_ENABLE);

            if (s16Result != ERR_SUCCESS)
            {
                MessageBox.Show("Enable Rx Failed", "ARINC 429 Rx Channel Enable", MessageBoxButtons.OK, MessageBoxIcon.Error);
                return s16Result;
            }

            return ERR_SUCCESS;
        }

        public S16BIT DisableRx(U8BIT chanNo)
        {
            /* Disable receivers on the selected Rx channel */

            s16Result = GetRxStatus(s16Card, chanNo);

            if (s16Result == DD429_ENABLE)
            {
                s16Result = DisableRxHostBuffer(s16Card, chanNo);
                s16Result += ResetRxChannel(s16Card, chanNo);

                if (s16Result != ERR_SUCCESS)
                {
                    MessageBox.Show("Disable Rx Failed: " + chanNo.ToString(), "ARINC 429 Rx Channel Disable", MessageBoxButtons.OK, MessageBoxIcon.Error);
                    return s16Result;
                }
            }

            return ERR_SUCCESS;
        }

        public S16BIT TxData(U8BIT chanNo, uint num)
        {
            s16Result = LoadTxQueueOne(s16Card, chanNo, num);
            if (s16Result != 1)
            {
                MessageBox.Show("LoadTxQueueOne Failed", "ARINC 429 Transmit Message", MessageBoxButtons.OK, MessageBoxIcon.Error);
                return s16Result;
            }

            return s16Result;
        }

        public S16BIT ScheduleTx(U8BIT chanNo, uint num)
        {
            s16Result = AddRepeated(s16Card, chanNo, num, 1000, 0);
            if (s16Result != ERR_SUCCESS)
            {
                MessageBox.Show("AddRepeated Failed", "ARINC 429 Schedule Transmit", MessageBoxButtons.OK, MessageBoxIcon.Error);
                return s16Result;
            }

            return s16Result;
        }

        public S16BIT StopTx(U8BIT chanNo, uint num)
        {
            s16Result = DelRepeated(s16Card, chanNo, (S16BIT) (num & 0x0FFF));
            if (s16Result != ERR_SUCCESS)
            {
                MessageBox.Show("DelRepeated Failed", "ARINC 429 Stop Transmit", MessageBoxButtons.OK, MessageBoxIcon.Error);
                return s16Result;
            }

            return s16Result;
        }

        public void StartReception()
        {
            /* Create a thread to read Rx host buffer */
            threadRx = new Thread(ThreadRx);
            threadRx.Start();
        }

        byte ReverseBits(byte b)
        {
            // swap halves (4-bit groups)
            b = (byte)((b >> 4) | (b << 4));
        
            // swap 2-bit pairs
            b = (byte)(((b & 0b11001100) >> 2) | ((b & 0b00110011) << 2));
            
            // swap adjacent bits
            b = (byte)(((b & 0b10101010) >> 1) | ((b & 0b01010101) << 1));
            
            return b;
        }

        /*******************************************************************************
         * Name:    ThreadRx
         *
         * Description:
         *
         *      A separate thread to read Rx host buffer
         *
         ******************************************************************************/
        void ThreadRx()
        {
            U64BIT timeStamp;
            U8BIT labelValue;
            U8BIT startRxChan, endRxChan;

            if(m_arincConfig == ArincConfiguration.LowSpeed_Channel1 || m_arincConfig == ArincConfiguration.HighSpeed_Channel1)
            {
                startRxChan = endRxChan = 1;
            }
            else if (m_arincConfig == ArincConfiguration.LowSpeed_Channel2 || m_arincConfig == ArincConfiguration.HighSpeed_Channel2)
            {
                startRxChan = endRxChan = 2;
            }
            else
            {
                startRxChan = 1;
                endRxChan = 2;
            }

            while (true)
            {
                /* Check Rx host buffer for msgs */

                //for (u8Chan = 1; u8Chan <= u8NumRx; u8Chan++)
                for (u8Chan = startRxChan; u8Chan <= endRxChan; u8Chan++)
                {
                    U16BIT u16NumMessagesRead = 0;
                    string dataString = "";

                    s16Result = ReadRxHostBuffer(s16Card, u8Chan, MAX_NUM_MESSAGES_TO_READ, ref u16NumMessagesRead, sRxHbufMessages);

                    if (s16Result == ERR_OVERFLOW)
                    {
                        //MessageBox.Show("Overflow error", "ARINC 429 Receive Message", MessageBoxButtons.OK, MessageBoxIcon.Error);
                        dataString = "Channel = " + u8Chan.ToString() + ", Overflow error" + Environment.NewLine;
                    }
                    else if (s16Result == ERR_RX_HBUF_OVERFLOW)
                    {
                        //MessageBox.Show("host buffer overflow error", "ARINC 429 Receive Message", MessageBoxButtons.OK, MessageBoxIcon.Error);
                        dataString = "Channel = " + u8Chan.ToString() + ", host buffer overflow error" + Environment.NewLine;
                    }
                    else if (s16Result == ERR_SUCCESS)
                    {
                        if (u16NumMessagesRead > 0)
                        {
                            for (int ctr = 0; ctr < u16NumMessagesRead; ctr++)
                            {
                                dataString = "Channel = " + u8Chan.ToString() + ", Data = 0x" + sRxHbufMessages[ctr].u32Data.ToString("X8") +
                                                            ", TT High = 0x" + sRxHbufMessages[ctr].u32StampHigh.ToString("X8") +
                                                            ", TT Low = 0x" + sRxHbufMessages[ctr].u32StampLow.ToString("X8") + Environment.NewLine;

                                bool match = false;
                                labelValue = (U8BIT) (sRxHbufMessages[ctr].u32Data & 0xFF);

                                // 429 labels are always reversed before transmission. Undo this.
                                labelValue = ReverseBits(labelValue);

                                /* Find the related MsgArinc429 object and save this object into it */
                                foreach (MsgArinc429 pMsg in pArincMsgs)
                                {
                                    if (pMsg.labelValue == labelValue)
                                    {
                                        match = true;
                                        //qDebug()<< "Match found for " + pMsg->name <<" BCCTRL = " <<sMsg.wBCCtrlWrd << " BSW = " << sMsg.wBlkSts;
                                    }

                                    /* Save message to MsgArinc429 */
                                    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;*/

                                        timeStamp = ((U64BIT)(sRxHbufMessages[ctr].u32StampHigh & 0xFFFF) << 32) | sRxHbufMessages[ctr].u32StampLow;

                                        lock (_arrayLock)
                                        {
                                            pMsg.setMsgRuntimeData(sRxHbufMessages[ctr].u32Data, timeStamp, u8Chan);   // doubt

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

                                        /* Notify GUI thread */
                                        //if (pForm1.IsHandleCreated)
                                        //{
                                        //    pForm1.Invoke(pForm1.mRxDelegate, dataString, /*timeStamp.ToString()*/Convert100nsToDHMSM(timeStamp), pMsg.labelName, u8Chan);
                                        //}

                                        break;
                                    }
                                }

                                if (!match)
                                {
                                    //qDebug() << "LDN" << m_pDDC1553Node->m_DevNum << " No match for CW = 0x" << QString("%1").arg(sMsg.wCmdWrd1, 0, 16).toUpper();
                                }
                            }
                        }
                        else
                        {
                            //MessageBox.Show("No message to read!", "ARINC 429 Receive Message", MessageBoxButtons.OK, MessageBoxIcon.Warning);
                            //dataString = "Channel = " + u8Chan.ToString() + ", No message to read!" + Environment.NewLine;
                        }
                    }

                    /* This invoke is to handle conditions where there were errors when attempting to read messages. This invoke 
                     * will cause an error string to be displayed instead of a value */
                    /*if (!(u16NumMessagesRead > 0))
                    {
                        if (pForm1.IsHandleCreated)
                        {
                            pForm1.Invoke(pForm1.mRxDelegate, dataString, "", "", (U8BIT) 0);
                        }
                    }*/
                }

                Thread.Sleep(0);
            }
        }

/*        public string Convert100nsToDHMSM(U64BIT timeStamp)
        {
            TimeSpan ts = TimeSpan.FromTicks((long)timeStamp);

            return string.Format("{0}:{1:D2}:{2:D2}:{3:D2}.{4:D3}", ts.Days, ts.Hours,
                ts.Minutes, ts.Seconds, ts.Milliseconds);
        }*/

        public void StopReception()
        {
            /* Stop Thread */
            threadRx.Abort();

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

    }
}
