using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

using LFP_Manager.DataStructure;

namespace LFP_Manager.Function
{
    public static class csSbCanAPI
    {
        #region CONST DATA

        /*************************************
		define  
		**************************************/
        public static bool ISDAR(byte x)
        {
            return (x & 0x01) != 0x00;
        }

        public static bool ISABOR(byte x)
        {
            return (x & 0x02) != 0x00;
        }

        public static void SETDAR(ref byte x)
        {
            x |= 0x01;
        }
        public static void SETABOR(ref byte x)
        {
            x |= 0x02;
        }

        // VCP Status Const data
        public const byte CS_CANv2_ActiveMode = 0x11;
		public const byte CS_CANv2_SetupMode = 0x10;
		public const byte uCAN_VCOM = 0x21;
		public const byte eCAN_VCOM = 0x31;
		public const byte NotConnect = 0x00;
		public const byte NotFind = 0xFF;

		// CAN
		public const byte CR = 0x0D;

		// Error Code
		public const byte Invalid_Arg = 0x01;
		public const byte No_Error = 0x00;

        #endregion

        public static bool IsSTD(byte format)
		{
			if ((format & 0x06) == 0x04)
				return true;
			else
				return false;
		}
		public static bool IsEXT(byte format)
		{
			if ((format & 0x06) == 0x06)
				return true;
			else
				return false;
		}
		public static bool IsDATA(byte format)
		{
			if ((format & 0x05) == 0x04)
				return true;
			else
				return false;
		}
		public static bool IsREMOTE(byte format)
		{
			if ((format & 0x05) == 0x05)
				return true;
			else
				return false;
		}

		public static byte SendCANTxFrame(CAN_Struct Tx, ref byte[] SerialBuffer, byte BufferSize, ref byte ConvertSize)
		{
			byte ind = 0;
			byte tmp_index = 0;
			byte[] buf = new byte[40];

			switch (Tx.Format)
			{
				case (byte)CAN_StructFormat.TX_STD_DATA: buf[ind++] = (byte)CAN_SerialCommandHeader.STD_DATA; break;
				case (byte)CAN_StructFormat.TX_STD_REMOTE: buf[ind++] = (byte)CAN_SerialCommandHeader.STD_REMOTE; break;
				case (byte)CAN_StructFormat.TX_EXT_DATA: buf[ind++] = (byte)CAN_SerialCommandHeader.EXT_DATA; break;
				case (byte)CAN_StructFormat.TX_EXT_REMOTE: buf[ind++] = (byte)CAN_SerialCommandHeader.EXT_REMOTE; break;
				default: return Invalid_Arg;
			}

			if (IsSTD(Tx.Format))
				tmp_index = 3;
			else if (IsEXT(Tx.Format))
				tmp_index = 8;

			for (int i = 1; i <= tmp_index; i++)
			{
				buf[ind++] = toASCII_HEX(Div_4(Tx.ID, (byte)(tmp_index - i)));
			}

			buf[ind++] = toASCII_HEX(Tx.DLC);

			if (IsDATA(Tx.Format))
			{

				for (int i = 0; i < Tx.DLC; i++)
				{
					buf[ind++] = toASCII_HEX(Div_4(Tx.DATA[i], 1));
					buf[ind++] = toASCII_HEX(Div_4(Tx.DATA[i], 0));
				}
			}

			buf[ind++] = CR;

			if (ind > BufferSize)
				return Invalid_Arg;

			ConvertSize = ind;

			for (int i = 0; i < ind; i++)
            {
				SerialBuffer[i] = buf[i];
			}

			return No_Error;
		}

		public static byte SetSerialConfig(SerialConfigInfo info, ref byte[] SerialBuffer, byte BufferSize, ref byte ConvertSize)
		{

			byte len;
			byte[] buf = new byte[21];

			buf[0] = (byte)'W';
			buf[1] = (byte)'S';
			switch (info.flow)
			{
				case (byte)FlowControl.NoFlowControl: buf[2] = (byte)'N'; break;
				case (byte)FlowControl.HardwareControl: buf[2] = (byte)'H'; break;
				default: return Invalid_Arg;
			}

			switch (info.data)
			{
				case (byte)DataBits.Data8: buf[3] = (byte)'8'; break;
				default: return Invalid_Arg;
			}

			switch (info.parity)
			{
				case (byte)Parity.NoParity: buf[4] = (byte)'N'; break;
				case (byte)Parity.EvenParity: buf[4] = (byte)'E'; break;
				case (byte)Parity.OddParity: buf[4] = (byte)'O'; break;
				case (byte)Parity.SpaceParity: buf[4] = (byte)'S'; break;
				case (byte)Parity.MarkParity: buf[4] = (byte)'M'; break;
				default: return Invalid_Arg;
			}

			switch (info.stop)
			{
				case (byte)StopBits.OneStop: buf[5] = (byte)'1'; break;
				case (byte)StopBits.TwoStop: buf[5] = (byte)'2'; break;
				default: return Invalid_Arg;
			}
			byte[] value = new byte[20 - 6];
			len = int2ascii(info.Baudrate, ref value, 20 - 6);
			if (len == 0)
			{
				return Invalid_Arg;
			}
			for (int i = 0; i < len; i++)
				buf[6 + i] = value[i];

			buf[6 + len] = CR;

			if ((len + 7) > BufferSize)
				return Invalid_Arg;

			ConvertSize = (byte)(len + 7);

			for (int i = 0; i < (len + 7); i++)
            {
				SerialBuffer[i] = buf[i];
            }

			return No_Error;
		}

		static byte SetCANConfig(CANConfigInfo info, ref byte[] SerialBuffer, byte BufferSize, ref byte ConvertSize)
		{
			byte len;
			byte ind;
			byte[] buf = new byte[31];
			byte[] value = new byte[30];
			byte function;
			function = 0;

			buf[0] = (byte)'W';
			buf[1] = (byte)'C';
			switch (info.Spec)
			{
				case (byte)CANSpec.CAN_A: buf[2] = (byte)'A'; break;
				case (byte)CANSpec.CAN_B: buf[2] = (byte)'B'; break;
				default: return Invalid_Arg;
			}
			buf[3] = (byte)',';

			len = int2ascii(info.Baudrate, ref value, 4);
			if (len == 0)
			{
				return Invalid_Arg;
			}
			for (int i = 0; i < len; i++)
				buf[4 + i] = value[i];

			buf[len + 4] = (byte)',';
			ind = (byte)(len + 5);

			len = hex2ID(info.ID, info.Spec, ref value, 8);
			if (len == 0)
			{
				return Invalid_Arg;
			}
			for (int i = 0; i < len; i++)
				buf[ind + i] = value[i];

			buf[len + ind] = (byte)',';
			ind = (byte)(len + ind + 1);

			len = hex2ID(info.Mask, info.Spec, ref value, 8);
			if (len == 0)
			{
				return Invalid_Arg;
			}
			for (int i = 0; i < len; i++)
				buf[ind + i] = value[i];

			buf[len + ind] = (byte)',';
			ind = (byte)(len + ind + 1);

			if (info.DAR)
				SETDAR(ref function);
			if (info.ABOR)
				SETABOR(ref function);

			buf[ind++] = toASCII_HEX(function);
			buf[ind++] = CR;


			if (ind > BufferSize)
				return Invalid_Arg;

			ConvertSize = ind;

			for (int i = 0; i < ind; i++)
				SerialBuffer[i] = buf[i];

			return No_Error;
		}

		static byte GetSerialConfig(ref byte[] SerialBuffer, byte BufferSize, ref byte ConvertSize)
		{
			if (3 > BufferSize)
				return Invalid_Arg;
			ConvertSize = 3;

			SerialBuffer[0] = (byte)'R';
			SerialBuffer[1] = (byte)'S';
			SerialBuffer[2] = CR;

			return No_Error;
		}

		static byte GetCANConfig(ref byte[] SerialBuffer, byte BufferSize, ref byte ConvertSize)
		{
			if (3 > BufferSize)
				return Invalid_Arg;
			ConvertSize = 3;

			SerialBuffer[0] = (byte)'R';
			SerialBuffer[1] = (byte)'C';
			SerialBuffer[2] = CR;

			return No_Error;
		}

		static byte SaveConfig(ref byte[] SerialBuffer, byte BufferSize, ref byte ConvertSize)
		{
			if (3 > BufferSize)
				return Invalid_Arg;
			ConvertSize = 3;

			SerialBuffer[0] = (byte)'S';
			SerialBuffer[1] = (byte)'V';
			SerialBuffer[2] = CR;

			return No_Error;
		}

		static byte FirmwareUpgrade(ref byte[] SerialBuffer, byte BufferSize, ref byte ConvertSize)
		{
			if (3 > BufferSize)
				return Invalid_Arg;
			ConvertSize = 3;

			SerialBuffer[0] = (byte)'F';
			SerialBuffer[1] = (byte)'U';
			SerialBuffer[2] = CR;

			return No_Error;
		}
		static byte FirmwareVersion(ref byte[] SerialBuffer, byte BufferSize, ref byte ConvertSize)
		{
			if (3 > BufferSize)
				return Invalid_Arg;
			ConvertSize = 3;

			SerialBuffer[0] = (byte)'F';
			SerialBuffer[1] = (byte)'V';
			SerialBuffer[2] = CR;

			return No_Error;
		}

		static byte GetErrorInfo(ref byte[] SerialBuffer, byte BufferSize, ref byte ConvertSize)
		{
			if (1 > BufferSize)
				return Invalid_Arg;
			ConvertSize = 1;

			SerialBuffer[0] = (byte)'!';

			return No_Error;
		}



		static byte ParsingSerialInfo(ref SerialConfigInfo info, ref byte[] SerialBuffer, byte BufferSize)
		{
			SerialConfigInfo tmp;
			UInt32 baudrate;
			byte flow, stop, data, parity;

			flow = SerialBuffer[0];
			data = SerialBuffer[1];
			parity = SerialBuffer[2];
			stop = SerialBuffer[3];

			byte[] bBuf = new byte[BufferSize - 5];
			for (int i = 0; i < BufferSize - 5; i++)
				bBuf[i] = SerialBuffer[4 + i];

			baudrate = ascii2int(ref bBuf, (byte)(BufferSize - 5));

			switch (flow)
			{
				case (byte)'N': tmp.flow = (byte)FlowControl.NoFlowControl; break;
				case (byte)'H': tmp.flow = (byte)FlowControl.HardwareControl; break;
				default: return Invalid_Arg;
			}

			switch (data)
			{
				case (byte)'8': tmp.data = (byte)DataBits.Data8; break;
				default: return Invalid_Arg;
			}

			switch (parity)
			{
				case (byte)'N': tmp.parity = (byte)Parity.NoParity; break;
				case (byte)'E': tmp.parity = (byte)Parity.EvenParity; break;
				case (byte)'O': tmp.parity = (byte)Parity.OddParity; break;
				case (byte)'S': tmp.parity = (byte)Parity.SpaceParity; break;
				case (byte)'M': tmp.parity = (byte)Parity.MarkParity; break;
				default: return Invalid_Arg;
			}
			switch (stop)
			{
				case (byte)'1': tmp.stop = (byte)StopBits.OneStop; break;
				case (byte)'2': tmp.stop = (byte)StopBits.TwoStop; break;
				default: return Invalid_Arg;
			}
			if ((baudrate % 100) > 0)
				return Invalid_Arg;
			else
			{
				tmp.Baudrate = baudrate;
			}

			//*info = tmp;
			info = tmp;

			return No_Error;
		}
		static byte ParsingCANInfo(ref CANConfigInfo info, ref byte[] SerialBuffer, byte BufferSize)
		{
			CANConfigInfo tmp;
			byte[] org = new byte[50];
			byte[][] tt = new byte[5][];
			byte function;

			switch (SerialBuffer[0])
			{
				case (byte)'A':
					tmp.Spec = (byte)CANSpec.CAN_A;
					if (14 >= BufferSize || BufferSize >= 18)
						return Invalid_Arg;
					break;
				case (byte)'B':
					if (24 >= BufferSize || BufferSize >= 28)
						return Invalid_Arg;
					tmp.Spec = (byte)CANSpec.CAN_B;
					break;
				default: return Invalid_Arg;
			}

			org = SerialBuffer;

			//for (int i = 0; i < tt.GetLength(0); i++)
			//	for (int j = 0; j < tt.GetLength(1); j++)
			//		tt[i, j] = 0x00;

			string equation = Encoding.Default.GetString(org);
			string[] equationTokens = equation.Split(new char[1] { ',' });

			for (int i = 0; i < equationTokens.Length; i++)
            {
				tt[i] = Encoding.UTF8.GetBytes(equationTokens[i]);
            }

			tmp.Baudrate = ascii2int(ref tt[0], (byte)tt[0].Length);
			tmp.ID = (uint)ascii2ID_Rev(ref tt[1], tmp.Spec);
			tmp.Mask = (uint)ascii2ID_Rev(ref tt[2], tmp.Spec);
			function = (byte)ascii2int(ref tt[3], 1);

			if (function > 255)
				return No_Error;

			if (ISDAR(function))
				tmp.DAR = true;
			else
				tmp.DAR = false;

			if (ISABOR(function))
				tmp.ABOR = true;
			else
				tmp.ABOR = false;

			//*info = tmp;
			info = tmp;

			return No_Error;
		}

		static byte ParsingErrorInfo(ref CANErorrInfo info, ref byte[] SerialBuffer, byte BufferSize)
		{
			CANErorrInfo tmp = new CANErorrInfo();

			info = tmp;

			return No_Error;
		}




		/**********************************************************************

							   private functions 

		**********************************************************************/


		static byte Div_8(UInt32 data, byte index)
		{
			byte ret;
			data = data >> (index * 8);
			ret = (byte)(data & 0xFF);
			return ret;
		}

		public static int ascii2hex(byte ch)
		{
			if (0x30 <= ch && ch <= 0x39)
			{//0~9
				ch &= 0x0f;
				return ch;
			}
			else if (0x61 <= ch && ch <= 0x66)
			{ //a~f
				ch &= 0x0f;
				ch += 9;
				return ch;
			}
			else if (0x41 <= ch && ch <= 0x46)
			{ //A~F
				ch &= 0x0f;
				ch += 9;
				return ch;
			}
			else
				return -1;
		}

		static int ascii2ID_Rev(ref byte[] SourceBuffer, byte idCount)
		{
			int idTmp = 0;
			int tmp = 0, i = 0;
			for (i = 0; i < idCount; i++)
			{
				tmp = ascii2hex(SourceBuffer[i]);
				if (tmp != -1)
				{
					idTmp = idTmp << 4;
					idTmp |= tmp;
				}
			}
			return idTmp;
		}



		static byte Div_4(UInt32 data, byte index)
		{
			byte ret;
			data = data >> (index * 4);
			ret = (byte)(data & 0x0F);
			return ret;
		}
		static byte int2ascii(UInt32 val, ref byte[] SourceBuffer, byte size)
		{
			UInt32 check = 10;
			byte su = 1;

			if (val < 10)
				return 0;

			while ((val / check) > 0)
			{
				su++;
				check *= 10;
			}

			if (su > size)
				return 0;

			string tmpStr = String.Format("{0}", val);

			SourceBuffer = Encoding.UTF8.GetBytes(tmpStr);

			return su;
		}
		static UInt32 ascii2int(ref byte[] SourceBuffer, byte size)
		{
			UInt32 val = 0;
			byte ind = 0;
			int ret = 0;
			UInt32 mul = 1;

			while (ret != -1)
			{
				ret = ascii2hex(SourceBuffer[ind++]);
			}
			ind--;

			while (ind > 0)
			{
				ind--;
				ret = ascii2hex(SourceBuffer[ind]);
				val = (UInt32)(val + (ret * mul));
				mul *= 10;
			}
			return val;
		}



		static byte hex2ID(UInt32 val, byte idCount, ref byte[] SourceBuffer, byte size)
		{

			UInt32 comp;

			switch (idCount)
			{
				case (byte)CANSpec.CAN_A: comp = 0x07ff; break;
				case (byte)CANSpec.CAN_B: comp = 0x1fffffff; break;
				default: return 0;
			}

			if (val > comp)
				return 0;

			if (idCount > size)
				return 0;

			switch (idCount)
			{
				case (byte)CANSpec.CAN_A: SourceBuffer = Encoding.UTF8.GetBytes(String.Format("{0:X3}", val)); break;
				case (byte)CANSpec.CAN_B: SourceBuffer = Encoding.UTF8.GetBytes(String.Format("{0:X8}", val)); break;
				default: return 0;
			}

			return idCount;
		}

		static byte toASCII_HEX(byte int_hex)
		{
			byte ret = 0;
			if (0 <= int_hex && int_hex <= 9)
			{
				ret = (byte)('0' + int_hex);
			}
			else if (0x0A <= int_hex && int_hex <= 0x0F)
			{
				ret = (byte)('A' + (int_hex % 10));
			}
			return ret;
		}
	}
}
