using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Windows.Forms;

using pisocanfd_net;

namespace SendRecv
{
    public partial class Form1 : Form
    {
        enum CANPORT {CAN1 = 1, CAN2};

        public bool SelectedCardOpen = false;
        public byte SelectedBoardNo = 0;
        public byte SelectedCardPortNo = 0;

        //Data Length Code to Number of CAN data bytes 
        byte[] CANFD_DLC2DBN = { 0, 1, 2, 3, 4, 5, 6, 7, 8, 12, 16, 20, 24, 32, 48, 64 };


        public Form1()
        {
            InitializeComponent();
        }

        private void btnScan_Click(object sender, EventArgs e)
        {
            UInt16 u16Ret;
            byte i, BoardID;

            cboModuleName.Items.Clear();
            for (i = 0; i < 16; i++)
            {
                string str;

                u16Ret = PISOCANFD.CANFD_GetCardBoardID(i, out BoardID);
                if (u16Ret == 0)
                {
                    switch (BoardID)
                    {
                        case 1: //PISO-CAN200U-FD
                            str = String.Format("BoardNo: {0:D2}, {1}", i, "PISO-CAN200U-FD");
                            break;
                        case 2: //PISO-CAN400U-FD
                            str = String.Format("BoardNo: {0:D2}, {1}", i, "PISO-CAN400U-FD");
                            break;
                        case 3: //PEX-CAN200i-FD
                            str = String.Format("BoardNo: {0:D2}, {1}", i, "PEX-CAN200i-FD");
                            break;
                        case 4: //PCM-CAN200-FD
                            str = String.Format("BoardNo: {0:D2}, {1}", i, "PCM-CAN200-FD");
                            break;
                        default:
                            str = String.Format("BoardNo: {0:D2}, Not Support(0x{1:X})", i, BoardID);
                            break;
                    }
                }
                else
                {
                    str = String.Format("BoardNo: {0:D2}, {1}", i, "NONE");
                }

                cboModuleName.Items.Add(str);
            }

            cboModuleName.SelectedIndex = 0;
        }

        private void cboModuleName_SelectedIndexChanged(object sender, EventArgs e)
        {
            UInt16 u16Ret;
            byte PortNum = 0;

            SelectedBoardNo = (byte)cboModuleName.SelectedIndex;

            if (cboModuleName.Items[SelectedBoardNo].ToString().Length > 20)
            {
                u16Ret = PISOCANFD.CANFD_GetCardPortNum((byte)SelectedBoardNo, out PortNum);
                if (u16Ret == 0)
                {
                    switch (PortNum)
                    {
                        case 2:
                            SelectedCardPortNo = PortNum;
                            break;
                        case 4:
                            SelectedCardPortNo = PortNum;
                            break;
                        default:
                            SelectedCardPortNo = 0;
                            break;
                    }
                }

                btnActive.Enabled = true;
            }
            else
            {
                SelectedCardPortNo = 0;
                btnActive.Enabled = false;
            }
        }

        private void btnOpen_Click(object sender, EventArgs e)
        {
            int iRet = 0;
            byte BoardNo = (byte)SelectedBoardNo;

            if (SelectedCardOpen == true)
            {
                tmrRxMsg.Enabled = false;

                //Close Device
                iRet = PISOCANFD.CANFD_CloseBoard(SelectedBoardNo);
                if (iRet != 0)
                {
                    string message = String.Format("Fail to close board.[CANFD_CloseBoard, iRet=0x{0:X}]", iRet);
                    string caption = "Error";
                    MessageBox.Show(message, caption, MessageBoxButtons.OK, MessageBoxIcon.Error);
                }
                else
                {
                    SelectedCardOpen = false;

                    btnScan.Enabled = true;
                    cboModuleName.Enabled = true;
                    btnActive.Text = "Active";

                    grpSendMessage.Enabled = false;
                    grpReceiveMessage.Enabled = false;

                }
            }
            else
            {
                bool SetDeviceParam = true;

                //Active Board
                iRet = PISOCANFD.CANFD_ActiveBoard(BoardNo);
                if (iRet == 0)
                {
                    byte OPMode, ISOMode;
                    UInt32 BR_Arbit, BR_DataPhase;
                    UInt16 BR_ArbitSP, BR_DataPhaseSP;

                    //====== Set CAN P1 parameters ======
                    //Initialize CAN P1
                    if (SetDeviceParam == true)
                    {
                        iRet = PISOCANFD.CANFD_Init(BoardNo, (byte)CANPORT.CAN1);
                        if (iRet != 0)
                        {
                            string message = String.Format("Initialize CAN Port1 error. [CANFD_Init, iRet=0x{0:X}].", iRet);
                            string caption = "Error";
                            MessageBox.Show(message, caption, MessageBoxButtons.OK, MessageBoxIcon.Error);
                            SetDeviceParam = false;
                        }
                    }

                    //Set CAN P1 baudrate and sample point                    
                    if (SetDeviceParam == true)
                    {
                        BR_Arbit = Convert.ToUInt32(Convert.ToSingle(txtABaudrate.Text) * 1000.0);
                        BR_DataPhase = Convert.ToUInt32(Convert.ToSingle(txtDBaudrate.Text) * 1000.0);
                        BR_ArbitSP = Convert.ToUInt16(Convert.ToSingle(txtASamplePoint.Text) * 100.0);
                        BR_DataPhaseSP = Convert.ToUInt16(Convert.ToSingle(txtDSamplePoint.Text) * 100.0);
                        if ((BR_Arbit > 0 && BR_Arbit <= 2000000) && (BR_DataPhase > 100000 && BR_DataPhase <= 5000000)
                            && (BR_ArbitSP > 0 && BR_ArbitSP <= 10000) && (BR_DataPhaseSP > 0 && BR_DataPhaseSP <= 10000))
                        {
                            iRet = PISOCANFD.CANFD_SetBitRateWithSP(BoardNo, (byte)CANPORT.CAN1, BR_Arbit, BR_DataPhase, BR_ArbitSP, BR_DataPhaseSP);
                            if (iRet != 0)
                            {
                                string message = String.Format("Set CAN Port1 baudrate and sample point error. [CANFD_SetBitRateWithSP, iRet=0x{0:X}].", iRet);
                                string caption = "Error";
                                MessageBox.Show(message, caption, MessageBoxButtons.OK, MessageBoxIcon.Error);
                                SetDeviceParam = false;
                            }
                        }
                        else
                        {
                            string message = String.Format("Port1 baudrate or sample point parameters error.");
                            string caption = "Error";
                            MessageBox.Show(message, caption, MessageBoxButtons.OK, MessageBoxIcon.Error);
                            SetDeviceParam = false;
                        }
                    }

                    //Set CAN P1 Port ISO mode
                    if (SetDeviceParam == true)
                    {
                        //Set CAN Port to use ISO-11898 CAN FD specification
                        ISOMode = 1; //1: ISO-11898, 0:  Bosch v1.0

                        iRet = PISOCANFD.CANFD_SetISOCRCEn(BoardNo, (byte)CANPORT.CAN1, ISOMode);
                        if (iRet != 0)
                        {
                            string message = String.Format("Set CAN Port1 ISO mode error. [CANFD_SetISOCRCEn, iRet=0x{0:X}].", iRet);
                            string caption = "Error";
                            MessageBox.Show(message, caption, MessageBoxButtons.OK, MessageBoxIcon.Error);
                            SetDeviceParam = false;
                        }
                    }

                    //Set CAN P1 Port Operation mode
                    if (SetDeviceParam == true)
                    {
                        //Set CAN Port to CAN FD Mode
                        OPMode = 0; //0: CAN FD, 1: CAN 2.0, 2: Listen Only

                        iRet = PISOCANFD.CANFD_SetOPMode(BoardNo, (byte)CANPORT.CAN1, OPMode);
                        if (iRet != 0)
                        {
                            string message = String.Format("Set CAN Port1 operation mode error. [CANFD_SetOPMode, iRet=0x{0:X}].", iRet);
                            string caption = "Error";
                            MessageBox.Show(message, caption, MessageBoxButtons.OK, MessageBoxIcon.Error);
                            SetDeviceParam = false;
                        }
                    }

                    //====== Set CAN P2 parameters ======
                    //Initialize CAN P2
                    if (SetDeviceParam == true)
                    {
                        iRet = PISOCANFD.CANFD_Init(BoardNo, (byte)CANPORT.CAN2);
                        if (iRet != 0)
                        {
                            string message = String.Format("Initialize CAN Port2 error. [CANFD_Init, iRet=0x{0:X}].", iRet);
                            string caption = "Error";
                            MessageBox.Show(message, caption, MessageBoxButtons.OK, MessageBoxIcon.Error);
                            SetDeviceParam = false;
                        }
                    }

                    //Set CAN P2 baudrate and sample point                    
                    if (SetDeviceParam == true)
                    {
                        BR_Arbit = Convert.ToUInt32(Convert.ToSingle(txtABaudrate.Text) * 1000.0);
                        BR_DataPhase = Convert.ToUInt32(Convert.ToSingle(txtDBaudrate.Text) * 1000.0);
                        BR_ArbitSP = Convert.ToUInt16(Convert.ToSingle(txtASamplePoint.Text) * 100.0);
                        BR_DataPhaseSP = Convert.ToUInt16(Convert.ToSingle(txtDSamplePoint.Text) * 100.0);
                        if ((BR_Arbit >= 5000 && BR_Arbit <= 2000000) && (BR_DataPhase >= 100000 && BR_DataPhase <= 5000000)
                            && (BR_ArbitSP > 0 && BR_ArbitSP <= 10000) && (BR_DataPhaseSP > 0 && BR_DataPhaseSP <= 10000))
                        {
                            iRet = PISOCANFD.CANFD_SetBitRateWithSP(BoardNo, (byte)CANPORT.CAN2, BR_Arbit, BR_DataPhase, BR_ArbitSP, BR_DataPhaseSP);
                            if (iRet != 0)
                            {
                                string message = String.Format("Set CAN Port2 baudrate and sample point error. [CANFD_SetBitRateWithSP, iRet=0x{0:X}].", iRet);
                                string caption = "Error";
                                MessageBox.Show(message, caption, MessageBoxButtons.OK, MessageBoxIcon.Error);
                                SetDeviceParam = false;
                            }
                        }
                        else
                        {
                            string message = String.Format("Port2 baudrate or sample point parameters error.");
                            string caption = "Error";
                            MessageBox.Show(message, caption, MessageBoxButtons.OK, MessageBoxIcon.Error);
                            SetDeviceParam = false;
                        }
                    }

                    //Set CAN P2 Port ISO mode
                    if (SetDeviceParam == true)
                    {
                        //Set CAN Port to use ISO-11898 CAN FD specification
                        ISOMode = 1; //1: ISO-11898, 0:  Bosch v1.0

                        iRet = PISOCANFD.CANFD_SetISOCRCEn(BoardNo, (byte)CANPORT.CAN2, ISOMode);
                        if (iRet != 0)
                        {
                            string message = String.Format("Set CAN Port2 ISO mode error. [CANFD_SetISOCRCEn, iRet=0x{0:X}].", iRet);
                            string caption = "Error";
                            MessageBox.Show(message, caption, MessageBoxButtons.OK, MessageBoxIcon.Error);
                            SetDeviceParam = false;
                        }
                    }

                    //Set CAN P1 Port Operation mode
                    if (SetDeviceParam == true)
                    {
                        //Set CAN Port to CAN FD Mode
                        OPMode = 0; //0: CAN FD, 1: CAN 2.0, 2: Listen Only

                        iRet = PISOCANFD.CANFD_SetOPMode(BoardNo, (byte)CANPORT.CAN2, OPMode);
                        if (iRet != 0)
                        {
                            string message = String.Format("Set CAN Port2 operation mode error. [CANFD_SetOPMode, iRet=0x{0:X}].", iRet);
                            string caption = "Error";
                            MessageBox.Show(message, caption, MessageBoxButtons.OK, MessageBoxIcon.Error);
                            SetDeviceParam = false;
                        }
                    }

                    //Install CAN Rx IRQ
                    if (SetDeviceParam == true)
                    {
                        iRet = PISOCANFD.CANFD_InstallIrq(BoardNo);
                        if (iRet != 0)
                        {
                            string message = String.Format("Set Board {0} IRQ error. [CANFD_InstallIrq, iRet=0x{1:X}].", BoardNo, iRet);
                            string caption = "Error";
                            MessageBox.Show(message, caption, MessageBoxButtons.OK, MessageBoxIcon.Error);
                            SetDeviceParam = false;
                        }
                    }

                    if (SetDeviceParam == true)
                    {
                        SelectedCardOpen = true;

                        btnScan.Enabled = false;
                        cboModuleName.Enabled = false;
                        btnActive.Text = "Close";

                        grpSendMessage.Enabled = true;
                        grpReceiveMessage.Enabled = true;

                        cboSendMsg_Mode.SelectedIndex = 0;
                        cboSendMsg_Type.SelectedIndex = 0;
                        cboSendMsg_DLC.SelectedIndex = 8;
                        cboSendMsg_Port.SelectedIndex = 0;
                        lstReceiveMessage.Items.Clear();

                        tmrRxMsg.Enabled = true;
                    }
                    else
                    {
                        PISOCANFD.CANFD_CloseBoard(BoardNo);
                    }
                }
                else
                {
                    string message = String.Format("Fail to active board.[CANFD_ActiveBoard, iRet=0x{0:X}]", iRet);
                    string caption = "Error";
                    MessageBox.Show(message, caption, MessageBoxButtons.OK, MessageBoxIcon.Error);
                }

            }
        }
        

        private void txtSendMsg_D1_KeyPress(object sender, KeyPressEventArgs e)
        {
            // this will only allow valid hex values [0-9][a-f][A-F][-] to be entered. See ASCII table
            char c = e.KeyChar;
            if ((c != '\b') && !((c <= 0x66 && c >= 0x61) || (c <= 0x46 && c >= 0x41) || (c >= 0x30 && c <= 0x39) || (c == 0x2c) || (c == '-')))
            {
                e.Handled = true;
            }
        }

        private void txtSendMsg_ID_KeyPress(object sender, KeyPressEventArgs e)
        {
            // this will only allow valid hex values [0-9][a-f][A-F] to be entered. See ASCII table
            char c = e.KeyChar;
            if (c != '\b' && !((c <= 0x66 && c >= 0x61) || (c <= 0x46 && c >= 0x41) || (c >= 0x30 && c <= 0x39) || (c == 0x2c)))
            {
                e.Handled = true;
            }
        }

        private void txtSendMsg_ID_TextChanged(object sender, EventArgs e)
        {
            if (txtSendMsg_ID.Text != "")
            {
                UInt32 value = Convert.ToUInt32(txtSendMsg_ID.Text, 16);
                if (cboSendMsg_Mode.SelectedIndex == 0) //Standard
                {
                    if (value > 0x7FF)
                        txtSendMsg_ID.Text = "7FF";

                }
                else
                {
                    if (value > 0x1FFFFFFF)
                        txtSendMsg_ID.Text = "1FFFFFFF";
                }
            }

            txtSendMsg_ID.Text = txtSendMsg_ID.Text.ToUpper();
            txtSendMsg_ID.SelectionStart = txtSendMsg_ID.Text.Length;
        }

        private void txtSendMsg_ID_Leave(object sender, EventArgs e)
        {
            if (cboSendMsg_Mode.SelectedIndex == 0) //Standard
            {
                txtSendMsg_ID.Text = txtSendMsg_ID.Text.PadLeft(3, '0');
            }
            else
            {
                txtSendMsg_ID.Text = txtSendMsg_ID.Text.PadLeft(8, '0');
            }
        }

        private void cboSendMsg_Mode_SelectedIndexChanged(object sender, EventArgs e)
        {
            UInt32 value = Convert.ToUInt32(txtSendMsg_ID.Text, 16);
            if (cboSendMsg_Mode.SelectedIndex == 0) //Standard
            {
                txtSendMsg_ID.MaxLength = 3;
                txtSendMsg_ID.Text = Convert.ToString(value, 16).PadLeft(3, '0');
            }
            else //Extended
            {
                txtSendMsg_ID.MaxLength = 8;
                txtSendMsg_ID.Text = Convert.ToString(value, 16).PadLeft(8, '0');
            }
        }

        private void cboSendMsg_Type_SelectedIndexChanged(object sender, EventArgs e)
        {
            cboSendMsg_DLC.Items.Clear();
            if (cboSendMsg_Type.SelectedIndex == 0 || cboSendMsg_Type.SelectedIndex == 1) //Normal CAN
            {
                cboSendMsg_DLC.Items.Add("0");
                cboSendMsg_DLC.Items.Add("1");
                cboSendMsg_DLC.Items.Add("2");
                cboSendMsg_DLC.Items.Add("3");
                cboSendMsg_DLC.Items.Add("4");
                cboSendMsg_DLC.Items.Add("5");
                cboSendMsg_DLC.Items.Add("6");
                cboSendMsg_DLC.Items.Add("7");
                cboSendMsg_DLC.Items.Add("8");
            }
            else //CAN FD
            {
                cboSendMsg_DLC.Items.Add("0");
                cboSendMsg_DLC.Items.Add("1");
                cboSendMsg_DLC.Items.Add("2");
                cboSendMsg_DLC.Items.Add("3");
                cboSendMsg_DLC.Items.Add("4");
                cboSendMsg_DLC.Items.Add("5");
                cboSendMsg_DLC.Items.Add("6");
                cboSendMsg_DLC.Items.Add("7");
                cboSendMsg_DLC.Items.Add("8");
                cboSendMsg_DLC.Items.Add("12");
                cboSendMsg_DLC.Items.Add("16");
                cboSendMsg_DLC.Items.Add("20");
                cboSendMsg_DLC.Items.Add("24");
                cboSendMsg_DLC.Items.Add("32");
                cboSendMsg_DLC.Items.Add("48");
                cboSendMsg_DLC.Items.Add("64");
            }
            cboSendMsg_DLC.SelectedIndex = 8;
        }

        private void btnSend_Click(object sender, EventArgs e)
        {
            int iRet;

            byte MODE;
            UInt32 ID;
            byte TYPE;
            byte DLC;
            byte[] DATA = new byte[64];

            MODE = (byte)cboSendMsg_Mode.SelectedIndex;
            ID = Convert.ToUInt32(txtSendMsg_ID.Text, 16);
            TYPE = (byte)cboSendMsg_Type.SelectedIndex;
            DLC = (byte)cboSendMsg_DLC.SelectedIndex;

            //convert data string to byte array
            string hexValues = txtSendMsg_Data.Text;
            string[] hexValuesSplit = hexValues.Split('-');
            int hexValuesLen;
            hexValuesLen = (hexValuesSplit.Length > 64) ? 64 : hexValuesSplit.Length;
            for (int i = 0; i < hexValuesLen; i++)
            {
                DATA[i] = Convert.ToByte(hexValuesSplit[i], 16);
            }

            if (cboSendMsg_Port.SelectedIndex == 0)  //CAN1
            {
                iRet = PISOCANFD.CANFD_SendCANMsg(SelectedBoardNo, (byte)CANPORT.CAN1, MODE, ID, TYPE, DLC, DATA);
                if (iRet != 0)
                {
                    string message = String.Format("Send CAN1 Message error. [CANFD_SendCANMsg, iRet=0x{0:X}].", iRet);
                    string caption = "Error";
                    MessageBox.Show(message, caption, MessageBoxButtons.OK, MessageBoxIcon.Error);
                }
            }
            else //CAN2
            {
                iRet = PISOCANFD.CANFD_SendCANMsg(SelectedBoardNo, (byte)CANPORT.CAN2, MODE, ID, TYPE, DLC, DATA);
                if (iRet != 0)
                {
                    string message = String.Format("Send CAN2 Message error. [CANFD_SendCANMsg, iRet=0x{0:X}].", iRet);
                    string caption = "Error";
                    MessageBox.Show(message, caption, MessageBoxButtons.OK, MessageBoxIcon.Error);
                }
            }

        }

        private void tmrRxMsg_Tick(object sender, EventArgs e)
        {
            int iRet;
            UInt16 DataCount =0;
            //CAN Information
            uint CAN_ID = 0;
            byte CAN_Mode = 0, CAN_Type = 0, CAN_Dlen = 0;
            byte[] CAN_Data = new byte[64];
            uint CAN_HTime = 0, CAN_LTime = 0;
            UInt64 u64Data;

            if (SelectedCardOpen == true)
            {
                //Receive CAN1 Message
                iRet = PISOCANFD.CANFD_RecvCANMsgCnt(SelectedBoardNo, (byte)CANPORT.CAN1, out DataCount);
                if (iRet == 0 && DataCount > 0)
                {
                    iRet = PISOCANFD.CANFD_RecvCANMsg(SelectedBoardNo, (byte)CANPORT.CAN1, out CAN_Mode, out CAN_ID, out CAN_Type, out CAN_Dlen, CAN_Data, out CAN_HTime, out CAN_LTime);
                    if (iRet == 0)
                    {
                        string message;
                        u64Data = (((UInt64)CAN_HTime << 32) + CAN_LTime);

                        CAN_Dlen = CANFD_DLC2DBN[CAN_Dlen];
                        if (CAN_Mode == 0)    //2.0A
                        {
                            message = String.Format("CAN1: [{0}.{1:0000000}] Mode={2}, ID={3:X3}, Type={4}, DLC={5}, ", u64Data / 10000000, u64Data % 10000000, CAN_Mode, CAN_ID, CAN_Type, CAN_Dlen);
                        }
                        else
                        {
                            message = String.Format("CAN1: [{0}.{1:0000000}] Mode={2}, ID={3:X8}, Type={4}, DLC={5}, ", u64Data / 10000000, u64Data % 10000000, CAN_Mode, CAN_ID, CAN_Type, CAN_Dlen);
                        }

                        if (CAN_Type != 0) //Not Remote frame
                        {
                            if (CAN_Dlen > 0) message = message + String.Format("DATA=");

                            for (int i = 0; i < CAN_Dlen; i++)
                            {
                                message = message + String.Format("{0:X2} ", CAN_Data[i]);
                            }
                        }

                        lstReceiveMessage.Items.Add(message);                        
                    }
                }

                //Receive CAN2 Message
                iRet = PISOCANFD.CANFD_RecvCANMsgCnt(SelectedBoardNo, (byte)CANPORT.CAN2, out DataCount);
                if (iRet == 0 && DataCount > 0)
                {
                    iRet = PISOCANFD.CANFD_RecvCANMsg(SelectedBoardNo, (byte)CANPORT.CAN2, out CAN_Mode, out CAN_ID, out CAN_Type, out CAN_Dlen, CAN_Data, out CAN_HTime, out CAN_LTime);
                    if (iRet == 0)
                    {
                        string message;
                        u64Data = (((UInt64)CAN_HTime << 32) + CAN_LTime);

                        CAN_Dlen = CANFD_DLC2DBN[CAN_Dlen];
                        if (CAN_Mode == 0)    //2.0A
                        {
                            message = String.Format("CAN2: [{0}.{1:0000000}] Mode={2}, ID={3:X3}, Type={4}, DLC={5}, ", u64Data / 10000000, u64Data % 10000000, CAN_Mode, CAN_ID, CAN_Type, CAN_Dlen);
                        }
                        else
                        {
                            message = String.Format("CAN2: [{0}.{1:0000000}] Mode={2}, ID={3:X8}, Type={4}, DLC={5}, ", u64Data / 10000000, u64Data % 10000000, CAN_Mode, CAN_ID, CAN_Type, CAN_Dlen);
                        }

                        if (CAN_Type != 0) //No Remote frame
                        {
                            if (CAN_Dlen > 0) message = message + String.Format("DATA=");

                            for (int i = 0; i < CAN_Dlen; i++)
                            {
                                message = message + String.Format("{0:X2} ", CAN_Data[i]);
                            }
                        }

                        lstReceiveMessage.Items.Add(message);
                    }
                }

                lstReceiveMessage.SelectedIndex = lstReceiveMessage.Items.Count - 1;
            }
        }

        private void btnClear_Click(object sender, EventArgs e)
        {
            lstReceiveMessage.Items.Clear();
        }

        
    }
}
