/***********************************************************************/
/**
* @file		    SerialCOM.cs
* @brief		class for serial comunication
* @version		1.0
* @date		    21. April. 2014
* @author		Michal Semrad
**************************************************************************/

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.IO.Ports;
using System.Windows.Forms;

namespace GUIapp
{
    class SerialCOM
    {

        private enum SlipChar
        {
            END = 192, ESC = 219, ESC_END = 220, ESC_ESC = 221
        };

        private enum SlipID
        {
            MESSAGE = 20,
            SENDAGAIN = 53
        };

        private SerialPort sp;
        //Form1 form1;

        public const int MAX_PACKET_LENGTH = 2000;

        private byte[] packet;
        private int received = 0;
        private bool escBefore = false;

        public Queue<byte> receivedPacket;
        public byte ident_previous;
        public byte[] data_previous;
        public int length_previous;

        public SerialCOM(int baudRate)
		{
  
			packet = new byte[MAX_PACKET_LENGTH];
            receivedPacket = new Queue<byte>();
			
			sp = new SerialPort("COM6", baudRate, Parity.None, 8, StopBits.One);
			try
			{
                sp.DataReceived += sp_DataReceived;
			}
			catch
			{
				throw;
			}
		}

        public int Connect(){
       
            try{
                 sp.Open(); 
            }
            catch{
                MessageBox.Show("Cable is not connected", "Error Message",MessageBoxButtons.OK, MessageBoxIcon.Error);
                return 0;
            }
            return 1;
        }

        public void Disconnect()
        {
            sp.Close();
        }

        void sp_DataReceived(object sender, SerialDataReceivedEventArgs e)
        {
            byte rec;
            while (sp.BytesToRead>0)
            {
                rec =(byte) sp.ReadByte();
                receivedPacket.Enqueue(rec);

                switch (rec)
                {
                    
                    case (byte)SlipChar.END:
                        if (received > 0)
                        {


                            UInt16 crc = crc_get(packet,(received-3),true);//-3 means id at the begining + 2 bytes crc in the end
                            UInt16 crc2 = System.BitConverter.ToUInt16(packet,received-2);

                            if (crc == crc2)
                            {
                                if (System.Windows.Forms.Application.OpenForms["Form1"] != null)
                                {
                                    (System.Windows.Forms.Application.OpenForms["Form1"] as Form1).SerialProcessPacket(packet, received);
                                }
                            }
                            else
                            {
                                SendPacket((byte)SlipID.SENDAGAIN, null, 0);//send request to send the previous packet one more time
                            }

                               

                        }
                        received = 0;
                        break;

                 
                    case (byte)SlipChar.ESC:
                        escBefore = true;
                        break;
           

                    case (byte)SlipChar.ESC_END:
                        if (escBefore)
                        {
                            if (received < MAX_PACKET_LENGTH)
                                packet[received++] = (byte)SlipChar.END;
                            escBefore = false;
                        }
                        else
                        {
                            if (received < MAX_PACKET_LENGTH)
                                packet[received++] = rec;
                        }
                        break;
             
                    case (byte)SlipChar.ESC_ESC:
                        if (escBefore)
                        {
                            if (received < MAX_PACKET_LENGTH)
                                packet[received++] = (byte)SlipChar.ESC;
                            escBefore = false;
                        }
                        else
                        {
                            if (received < MAX_PACKET_LENGTH)
                                packet[received++] = rec;
                        }
                        break;
                    // no special character
                    default:
                        if (received < MAX_PACKET_LENGTH)
                            packet[received++] = rec;
                        break;
                }
            }
        }

        public int SendPacket(byte ident, byte[] data, int length)
        {
            int current = 0;
            byte[] temp = new byte[MAX_PACKET_LENGTH * 2 + 2];

            ident_previous = ident;
            data_previous = data;
            length_previous = length;

            lock (this)
            {
                //end
                temp[current++] = (byte)SlipChar.END;
                // identificator
                temp[current++] = ident;

                //data
                for (int i = 0; i < length; i++)
                {
                    switch (data[i])
                    {
                        case (byte)SlipChar.END:
                            temp[current++] = (byte)SlipChar.ESC;//send_char(ESC);
                            temp[current++] = (byte)SlipChar.ESC_END;//send_char(ESC_END);
                            break;
                        case (byte)SlipChar.ESC:
                            temp[current++] = (byte)SlipChar.ESC;//send_char(ESC);
                            temp[current++] = (byte)SlipChar.ESC_ESC;//send_char(ESC_ESC);
                            break;
                        default:
                            temp[current++] = data[i];
                            break;
                    }
                }


                UInt16 crc16 = crc_get(data, length, false);
                byte[] crc = BitConverter.GetBytes(crc16);
                
                //crc
                for (int i = 0; i < crc.Length; i++)
                {
                    switch (crc[i])
                    {
                        case (byte)SlipChar.END:
                            temp[current++] = (byte)SlipChar.ESC;//send_char(ESC);
                            temp[current++] = (byte)SlipChar.ESC_END;//send_char(ESC_END);
                            break;
                        case (byte)SlipChar.ESC:
                            temp[current++] = (byte)SlipChar.ESC;//send_char(ESC);
                            temp[current++] = (byte)SlipChar.ESC_ESC;//send_char(ESC_ESC);
                            break;
                        default:
                            temp[current++] = crc[i];
                            break;
                    }
                }

                //end
                temp[current++] = (byte)SlipChar.END;
                temp[current++] = (byte)SlipChar.END;

                try
                {
                    sp.Write(temp, 0, current);
                }
                catch
                {
                    MessageBox.Show("Cable is not connected", "Error Message", MessageBoxButtons.OK, MessageBoxIcon.Error);
                    return 0;
                }
                   
            }

            return 1;
        }

        UInt16 crc_get(byte[] data, int len, bool rec)
        {
            byte[] crc = new byte[2];
            byte tmp = 0;
            int i;
            UInt16 crc16=0;

            crc[0] = 0;
            crc[1] = 0;


                for (i = 0; i < len; i++)
                {
                    tmp = crc[0];
                    if(rec)
                        crc[0] = (byte)(crc[1] ^ data[i + 1]);
                    else
                        crc[0] = (byte)(crc[1] ^ data[i]);
                    crc[1] = tmp;
                    crc[0] ^= (byte)(crc[0] << 4);
                    crc[1] ^= (byte)(crc[0] >> 4);

                    crc16 = (UInt16)(crc[0] | crc[1] << 8);
                    crc16 ^= (UInt16)(crc16 << 3);

                    crc = BitConverter.GetBytes(crc16);
                    //*((uint16_t *) crc) ^= (*(uint16_t*)(crc)) << 3;
                } // for i
            return crc16;
        } // crc_get()


    }
}
