#define CONSOLE_DBUGGING

using System;
using System.Collections.Generic;
using System.Linq;
using System.Runtime.InteropServices;
using System.Text;
using System.Threading;
using System.Collections;

namespace INT69DB_2A.SerialManger
{
    public partial class SerialMgrComm
    {
        public delegate void ObjectEventHandler(object data);
        public static event ObjectEventHandler SerialPortMessageReceive;
        public static event ObjectEventHandler SerialPortErrMessageReceive;
        public static event ObjectEventHandler SerialPortGetVersionDataEvent;

        public static void IntializeSerialManager()
        {
            //Console.Write("Initialize Serial Manager \n");

            // Register Callback
            fnSerialMgrDebugCallback = serialDebugCallback;
            DLL_SerialMgr_Register_DebugCB(fnSerialMgrDebugCallback);

            fnSerialMgrPortConnectCallback = serialPortConnectCallback;
            DLL_SerialMgr_Register_ConnectCB(fnSerialMgrPortConnectCallback);

            fnSerialVersionCallback = serialVersionCallback;
            DLL_SerialMgr_Register_VersionCB(fnSerialVersionCallback);

            fnSerialMgrRcvPacketCallback = serialRcvPacketMsgCallback;
            DLL_SerialMgr_Register_RcvPacketCB(fnSerialMgrRcvPacketCallback);

            fnSerialMgrRcvErrPacketCallback = serialRcvErrPacketMsgCallback;
            DLL_SerialMgr_Register_RcvErrPacketCB(fnSerialMgrRcvErrPacketCallback);

            // test
            //DLL_SerialMgr_HelloWorld();

            // get the lib version 
            //DLL_SerialMgr_Getversion();

            // activate the dll thread 
            DLL_SerialMgr_Activation();

        }

        #region DLL_CALLBACK
        private static void serialVersionCallback(StringBuilder versionInfo)
        {
            try
            {
                //Console.Write("\n-------------------------------\n");
                //Console.Write("lib version = " + versionInfo + "\n");
                //Console.Write("-------------------------------\n");

                if (SerialPortGetVersionDataEvent != null)
                    SerialPortGetVersionDataEvent(versionInfo);
            }
            catch (Exception e)
            {
                throw (e);
            }
        }

        private static void serialPortConnectCallback(StringBuilder commPort, int status, int errDetail)
        {
            try
            {
                //Console.Write(commPort + ": status = " + status.ToString() +" , detail = " + errDetail.ToString() + "\n");
            }
            catch (Exception e)
            {
                throw (e);
            }
        }

        private static void serialDebugCallback(int type, StringBuilder pMessage)
        {
            try
            {
                //Console.Write(pMessage);
            }
            catch (Exception e)
            {
                throw (e);
            }
        }

        // https://stackoverflow.com/questions/22938756/how-to-marshal-c-array-to-c-sharp-via-intptr
        public static void serialRcvPacketMsgCallback(StringBuilder commPort, IntPtr pData, int length)
        {
            try
            {
                if (commPort.Length == 0)
                    return;

                if ( (pData == IntPtr.Zero) || (length == 0) )
                    return;

                byte[] data = new byte[length];
                Marshal.Copy(pData, data, 0, (int)length);

                string sdata = Encoding.Default.GetString(data, 0, data.Length);

                if (SerialPortMessageReceive != null)
                    SerialPortMessageReceive(sdata);

#if TEST_CB
                String commPortStr = commPort.ToString();

                serialPortMessageByte(commPortStr, data, length);
#endif


                //Console.Write("comm port : " + commPort + ", length = " + length.ToString() + "\n");
#if TEST_DEBUG
                // https://stackoverflow.com/questions/1342922/console-writeline-as-hexadecimal
                int index = 0;
                foreach (byte temp in data)
                {
                    Console.Write(temp.ToString("X2") + " ");
                    index++;

                    if (index % 16 == 0) Console.Write("\n");
                }

                Console.Write("\n");
#endif

                //data = null;

                //GC.Collect();
                //GC.WaitForPendingFinalizers();
            }
            catch (Exception e)
            {
                throw (e);
            }
        }

        public static void serialRcvErrPacketMsgCallback(StringBuilder commPort, IntPtr pData, int length)
        {
            try
            {
                if (commPort.Length == 0)
                    return;

                if ((pData == IntPtr.Zero) || (length == 0))
                    return;

                byte[] data = new byte[length];
                Marshal.Copy(pData, data, 0, (int)length);

                string sdata = Encoding.Default.GetString(data, 0, data.Length);

                if (SerialPortErrMessageReceive != null)
                    SerialPortErrMessageReceive(sdata);

                //String commPortStr = commPort.ToString();
                //serialPortMessageByte(commPortStr, data, length);

                //Console.Write("Err : comm port : " + commPort + ", length = " + length.ToString() + "\n");

#if (TEST_DEBUG1)
                // https://stackoverflow.com/questions/1342922/console-writeline-as-hexadecimal
                int index = 0;
                foreach (byte temp in data)
                {
                    Console.Write(temp.ToString("X2") + " ");
                    index++;

                    if (index == 5) break;

                    if (index % 16 == 0) Console.Write("\n");
                }

                Console.Write("\n");
#endif

                // https://dhshin94.tistory.com/135
                //data = null;

                //GC.Collect();
                //GC.WaitForPendingFinalizers();

            }
            catch (Exception e)
            {
                throw (e);
            }
        }
        #endregion  // DLL_CALLBACK

        #region DLL_COMMAND

        public static void serialPortOpen(String commPort, int BaudRate, int stopBit, int parity, int dataBit)
        {
            IntPtr pCommPort = Marshal.StringToBSTR(commPort.ToString());

            DLL_SerialMgr_PortOpen(pCommPort, BaudRate, stopBit, parity, dataBit);
        }

        public static void serialPortClose(String commPort)
        {
            IntPtr pCommPort = Marshal.StringToBSTR(commPort.ToString());

            DLL_SerialMgr_PortClose(pCommPort);
        }


        public static void serialPortMessageByte(String commport, byte[] data, int length)
        {
            IntPtr pCommPort = Marshal.StringToBSTR(commport.ToString());

            IntPtr ptr = Marshal.AllocHGlobal(length);
            Marshal.Copy(data, 0, ptr, length);

            DLL_SerialMgr_PortSendMsg(pCommPort, ptr, length);

            //string sdata = Encoding.Default.GetString(data, 0, data.Length);

            Marshal.FreeHGlobal(ptr);

          //  if (SerialPortMessageReceive != null)
          //      SerialPortMessageReceive(sdata);
        }


        //https://stackoverflow.com/questions/13418795/convert-byte-array-to-structure-in-the-compact-framework
        public static void serialPortMessage(String commPort, String Message)
        {
            IntPtr pCommPort = Marshal.StringToBSTR(commPort.ToString());

            StringBuilder sb = new StringBuilder();
            sb.Append((char)0x02);
            sb.Append(Message);
            sb.Append((char)0x03);

            Message = sb.ToString();

            byte[] buffer = Encoding.ASCII.GetBytes(Message);
            int count = Encoding.ASCII.GetByteCount(Message);
            //byte[] buffer = { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 };
            //int count = 10;


            IntPtr ptr = Marshal.AllocHGlobal(count);
            Marshal.Copy(buffer, 0, ptr, count);

            DLL_SerialMgr_PortSendMsg(pCommPort, ptr, count);

            Marshal.FreeHGlobal(ptr);
        }
        
        public static void serialPort_GetVersion()
        {
            DLL_SerialMgr_Getversion();
        }
        #endregion 

    }
}
