using System;
using System.Collections.Generic;
using System.Globalization;
using System.IO;
using System.IO.Ports;
using System.Linq;
using QCRPG.Framework;
using QCRPG.TSL.BT.Packets;

namespace QCRPG.TSL.BT;

public class BTScripter : ScripterBase
{
	private const int defaultResponseWaitTimeMs = 5000;

	protected BTAPI btAPI;

	protected BTPacketList btPacketList;

	protected BTPacketHelper btPacketHelper;

	protected BTScriptOverrideParams btScriptOverride;

	public BTScripter(BTAPI btAPI, BTPacketList btPacketList)
	{
		this.btAPI = btAPI;
		this.btPacketList = btPacketList;
		btPacketHelper = new BTPacketHelper(this.btPacketList);
		btScriptOverride = new BTScriptOverrideParams(this, btAPI);
		modeAgnosticDataReceiver = new ModeAgnosticDataReceiver(btAPI);
		tclCommandMap.Add("Poke16", Poke16_Handler);
		tclCommandMap.Add("POKE16", Poke16_Handler);
		tclCommandMap.Add("Poke32", Poke32_Handler);
		tclCommandMap.Add("POKE32", Poke32_Handler);
		tclCommandMap.Add("Peek32", Peek32_Handler);
		tclCommandMap.Add("PEEK32", Peek32_Handler);
		tclCommandMap.Add("HCI_SendACLData", HCISendACLData_Handler);
		tclCommandMap.Add("HCI_SendCmd", HCISendCmd_Handler);
		tclCommandMap.Add("HCI_WaitEvent", HCIWaitEvent_Handler);
		tclCommandMap.Add("HCI_SetFlowControl", HCISetFlowControl_Handler);
		tclCommandMap.Add("HCI_SendRawData", HCISendRawACLData_Handler);
		tclCommandMap.Add("OpenBTPort", OpenBTPort_Handler);
		tclCommandMap.Add("CloseBTPort", CloseBTPort_Handler);
		tclCommandMap.Add("DownloadBTPatch", DownloadBTPatch_Handler);
		tclCommandMap.Add("DownloadBTNVM", DownloadBTNVM_Handler);
		tclCommandMap.Add("HCI_SendRawDataNoProtocolByte", HCI_SendRawDataNoProtocolByte_Handler);
	}

	private TclResult DownloadBTPatch_Handler(string[] args)
	{
		if (args.Length != 2)
		{
			TclWrapper.SetResult($"Wrong # of arguments. Usage: {args[0]} ?patchfilename?");
			return TclResult.ERROR;
		}
		if (!btAPI.IsConnectionOpen())
		{
			TclWrapper.SetResult("Connection closed");
			return TclResult.ERROR;
		}
		try
		{
			if (!File.Exists(args[1]))
			{
				TclWrapper.SetResult("Failed to find patch file");
				return TclResult.ERROR;
			}
			btAPI.PatchFirmwareTLV(args[1]);
		}
		catch (Exception ex)
		{
			TclWrapper.SetResult($"Caught exception download patch.\r\nSource: {ex.Source}\r\nMessage: {ex.Message}");
			return TclResult.ERROR;
		}
		TclWrapper.SetResult("");
		return TclResult.OK;
	}

	private TclResult DownloadBTNVM_Handler(string[] args)
	{
		if (args.Length != 2)
		{
			TclWrapper.SetResult($"Wrong # of arguments. Usage: {args[0]} ?NVMFilename?");
			return TclResult.ERROR;
		}
		if (!btAPI.IsConnectionOpen())
		{
			TclWrapper.SetResult("Connection closed");
			return TclResult.ERROR;
		}
		try
		{
			try
			{
				if (!File.Exists(args[1]))
				{
					TclWrapper.SetResult("Failed to find NVM file");
					return TclResult.ERROR;
				}
				btAPI.WriteNVM(args[1]);
			}
			catch (Exception)
			{
			}
		}
		catch (Exception ex2)
		{
			TclWrapper.SetResult($"Caught exception download NVM.\r\nSource: {ex2.Source}\r\nMessage: {ex2.Message}");
			return TclResult.ERROR;
		}
		return TclResult.OK;
	}

	private TclResult Poke16_Handler(string[] args)
	{
		int num = 0;
		int num2 = 0;
		if (args.Length != 3)
		{
			TclWrapper.SetResult($"Wrong # of arguments. Usage: {args[0]} ?registerAddress? ?data?");
			return TclResult.ERROR;
		}
		if (!btAPI.IsConnectionOpen())
		{
			TclWrapper.SetResult("Connection closed");
			return TclResult.ERROR;
		}
		try
		{
			num2 = Conversions.PrefixedStringToInt(args[1]);
		}
		catch (Exception)
		{
			TclWrapper.SetResult($"Error converting data {args[1]} to an int");
			return TclResult.ERROR;
		}
		try
		{
			num = Conversions.PrefixedStringToInt(args[2]);
		}
		catch (Exception)
		{
			TclWrapper.SetResult($"Error converting data {args[2]} to an int");
			return TclResult.ERROR;
		}
		byte[] bytes = BitConverter.GetBytes(num);
		try
		{
			btAPI.Poke16(num2, bytes);
		}
		catch (Exception ex3)
		{
			TclWrapper.SetResult($"Caught exception poke32.\r\nSource: {ex3.Source}\r\nMessage: {ex3.Message}");
			return TclResult.ERROR;
		}
		TclWrapper.SetResult("");
		return TclResult.OK;
	}

	private TclResult Poke32_Handler(string[] args)
	{
		int num = 0;
		int num2 = 0;
		if (args.Length != 3)
		{
			TclWrapper.SetResult($"Wrong # of arguments. Usage: {args[0]} ?registerAddress? ?data?");
			return TclResult.ERROR;
		}
		if (!btAPI.IsConnectionOpen())
		{
			TclWrapper.SetResult("Connection closed");
			return TclResult.ERROR;
		}
		try
		{
			num2 = Conversions.PrefixedStringToInt(args[1]);
		}
		catch (Exception)
		{
			TclWrapper.SetResult($"Error converting data {args[1]} to an int");
			return TclResult.ERROR;
		}
		try
		{
			num = Conversions.PrefixedStringToInt(args[2]);
		}
		catch (Exception)
		{
			TclWrapper.SetResult($"Error converting data {args[2]} to an int");
			return TclResult.ERROR;
		}
		byte[] bytes = BitConverter.GetBytes(num);
		try
		{
			btAPI.Poke32(num2, bytes);
		}
		catch (Exception ex3)
		{
			TclWrapper.SetResult($"Caught exception poke32.\r\nSource: {ex3.Source}\r\nMessage: {ex3.Message}");
			return TclResult.ERROR;
		}
		TclWrapper.SetResult("");
		return TclResult.OK;
	}

	private TclResult OpenBTPort_Handler(string[] args)
	{
		try
		{
			if (args[1].ToUpper() == "SERIAL")
			{
				if (args.Length == 4)
				{
					btAPI.OpenSerialConnection(args[2], int.Parse(args[3]), Handshake.None);
				}
				else if (args.Length == 5)
				{
					if (args[4].ToUpper() == "NONE")
					{
						btAPI.OpenSerialConnection(args[2], int.Parse(args[3]), Handshake.None);
					}
					else if (args[4].ToUpper() == "HW")
					{
						btAPI.OpenSerialConnection(args[2], int.Parse(args[3]), Handshake.RequestToSend);
					}
				}
			}
			else if (args[1].ToUpper() == "USB")
			{
				UsbDeviceInfo usbDeviceInfo = default(UsbDeviceInfo);
				usbDeviceInfo.path = args[2];
				btAPI.OpenUsbConnection(usbDeviceInfo);
			}
		}
		catch (Exception ex)
		{
			TclWrapper.SetResult($"Caught exception open BT port.\r\nSource: {ex.Source}\r\nMessage: {ex.Message}");
			return TclResult.ERROR;
		}
		TclWrapper.SetResult("");
		return TclResult.OK;
	}

	private TclResult CloseBTPort_Handler(string[] args)
	{
		try
		{
			btAPI.CloseConnection();
		}
		catch (Exception ex)
		{
			TclWrapper.SetResult($"Caught exception open BT close.\r\nSource: {ex.Source}\r\nMessage: {ex.Message}");
			return TclResult.ERROR;
		}
		TclWrapper.SetResult("");
		return TclResult.OK;
	}

	private TclResult Peek32_Handler(string[] args)
	{
		int num = 0;
		if (args.Length != 2)
		{
			TclWrapper.SetResult($"Wrong # of arguments. Usage: {args[0]} ?registerAddress?");
			return TclResult.ERROR;
		}
		if (!btAPI.IsConnectionOpen())
		{
			TclWrapper.SetResult("Connection closed");
			return TclResult.ERROR;
		}
		try
		{
			num = Conversions.PrefixedStringToInt(args[1]);
		}
		catch (Exception)
		{
			TclWrapper.SetResult($"Error converting data {args[1]} to an int");
			return TclResult.ERROR;
		}
		try
		{
			EdlPeek32Request edlPeek32Request = new EdlPeek32Request();
			edlPeek32Request.UserFields.Address = (uint)num;
			btAPI.Send(edlPeek32Request);
		}
		catch (Exception ex2)
		{
			TclWrapper.SetResult($"Caught exception sending peek byte, {args[1]}.\r\nSource: {ex2.Source}\r\nMessage: {ex2.Message}");
			return TclResult.ERROR;
		}
		PacketBase packetBase;
		try
		{
			Type[] packetTypes = new Type[2]
			{
				typeof(EdlUploadResponse),
				typeof(CommandCompleteForEdlPeek32Request)
			};
			packetBase = modeAgnosticDataReceiver.Receive(packetTypes, 5000);
		}
		catch (Exception ex3)
		{
			TclWrapper.SetResult($"Caught exception waiting for response to peek byte, {args[1]}.\r\nSource: {ex3.Source}\r\nMessage: {ex3.Message}");
			return TclResult.ERROR;
		}
		byte[] array = null;
		array = ((!(packetBase is EdlUploadResponse)) ? Conversions.UintListToByteArray(((CommandCompleteForEdlPeek32Request)packetBase).UserFields.DataList) : Conversions.UintListToByteArray(((EdlUploadResponse)packetBase).UserFields.DataList));
		TclWrapper.SetResult(Conversions.ByteArrayTo0xHexString(array, singlePrefix: true));
		return TclResult.OK;
	}

	private TclResult HCISendCmd_Handler(string[] args)
	{
		if (args.Length < 2)
		{
			TclWrapper.SetResult($"Wrong # of arguments.  Usage: {args[0]} ?message?");
			return TclResult.ERROR;
		}
		if (!btAPI.IsConnectionOpen())
		{
			TclWrapper.SetResult("Connection closed");
			return TclResult.ERROR;
		}
		try
		{
			string s = args[1].Replace(" ", "");
			if (s.Length % 2 != 0)
			{
				TclWrapper.SetResult($"Wrong # of payLoad.");
				return TclResult.ERROR;
			}
			byte[] array = (from i in Enumerable.Range(0, s.Length / 2)
				select byte.Parse(s.Substring(2 * i, 2), NumberStyles.HexNumber)).ToArray();
			array[2] = Math.Max(array[2], (byte)(array.Length - 3));
			btAPI.Send(btPacketList.BytesToPacketOfType(array, new List<PacketType> { PacketType.Command }));
		}
		catch (Exception ex)
		{
			TclWrapper.SetResult($"Caught exception Send Cmd Handler.\r\nSource: {ex.Source}\r\nMessage: {ex.Message}");
			return TclResult.ERROR;
		}
		return TclResult.OK;
	}

	private TclResult HCIWaitEvent_Handler(string[] args)
	{
		int timeoutMilliseconds = 5000;
		if (args.Length < 1)
		{
			TclWrapper.SetResult($"Wrong # of arguments.  Usage: {args[0]} ?opCode? ?timeout?");
			return TclResult.ERROR;
		}
		if (args.Length > 2)
		{
			try
			{
				int num = Convert.ToInt32(args[args.Length - 1]);
				timeoutMilliseconds = ((num > 0) ? (num * 1000) : (-1));
			}
			catch (Exception)
			{
				TclWrapper.SetResult($"Error converting timeoutSeconds {args[args.Length - 1]} to an int");
				return TclResult.ERROR;
			}
		}
		if (!btAPI.IsConnectionOpen())
		{
			TclWrapper.SetResult("Connection closed");
			return TclResult.ERROR;
		}
		PacketBase packetBase;
		if (args.Length == 1 || args[1].Equals("ANY_EVENT"))
		{
			try
			{
				List<Type> list = new List<Type>();
				list.AddRange(btPacketList.GetResponsePacketTypes());
				list.AddRange(btPacketList.GetNotificationPacketTypes());
				packetBase = modeAgnosticDataReceiver.Receive(list.ToArray(), timeoutMilliseconds);
			}
			catch (TimeoutException)
			{
				TclWrapper.SetResult("Timeout waiting for any BT event");
				return TclResult.ERROR;
			}
			catch (Exception ex3)
			{
				TclWrapper.SetResult($"Caught exception waiting for any BT event.\r\nSource: {ex3.Source}\r\nMessage: {ex3.Message}");
				return TclResult.ERROR;
			}
		}
		else
		{
			List<int> list2 = new List<int>();
			int i = 1;
			try
			{
				for (i = 1; i < args.Length - 1; i++)
				{
					int num2 = 0;
					num2 = Convert.ToInt32(args[i], 16);
					if (num2 < 0 || num2 > 255)
					{
						TclWrapper.SetResult($"Invalid byte value (must be between 0x00 - 0xFF): {args[i]}");
						return TclResult.ERROR;
					}
					list2.Add((byte)num2);
				}
			}
			catch (Exception)
			{
				TclWrapper.SetResult($"Error converting argument {args[i]} to a byte");
				return TclResult.ERROR;
			}
			PacketMatchPattern packetMatchPattern = new PacketMatchPattern(0, list2.ToArray());
			try
			{
				packetBase = modeAgnosticDataReceiver.Receive(new PacketMatchPattern[1] { packetMatchPattern }, timeoutMilliseconds);
			}
			catch (TimeoutException)
			{
				TclWrapper.SetResult(string.Format("Timeout waiting for specific BT event that starts with {0}", BitConverter.ToString(list2.Select((int x) => (byte)x).ToArray()).Replace("-", " ")));
				return TclResult.ERROR;
			}
			catch (Exception ex6)
			{
				TclWrapper.SetResult($"Caught exception waiting for specific BT event, {args[1]}.\r\nSource: {ex6.Source}\r\nMessage: {ex6.Message}");
				return TclResult.ERROR;
			}
		}
		TclWrapper.SetResult("04 " + Conversions.ByteArrayToSpacedHexString(packetBase.ToByteArray()));
		return TclResult.OK;
	}

	private TclResult HCISendRawACLData_Handler(string[] args)
	{
		if (args.Length < 2)
		{
			TclWrapper.SetResult($"Wrong # of arguments.  Usage: {args[0]} ?ACLPacket?");
			return TclResult.ERROR;
		}
		if (!btAPI.IsConnectionOpen())
		{
			TclWrapper.SetResult("Connection closed");
			return TclResult.ERROR;
		}
		string[] array = TclWrapper.SplitList(args[1]);
		int num = array.Length;
		byte[] array2 = new byte[num];
		for (int i = 0; i < num; i++)
		{
			byte.TryParse(array[i], NumberStyles.HexNumber, null, out array2[i]);
		}
		int num2 = 0;
		byte connectionHandle = array2[num2++];
		int flags = array2[num2++];
		ushort num3 = array2[num2++];
		num3 += (ushort)(array2[num2++] << 8);
		int num4 = array2.Length - num2;
		byte[] array3 = new byte[num4];
		Array.Copy(array2, num2, array3, 0, num4);
		try
		{
			btAPI.ACLData(connectionHandle, flags, num3, array3);
		}
		catch (Exception ex)
		{
			TclWrapper.SetResult($"Caught exception send ACL Data.\r\nSource: {ex.Source}\r\nMessage: {ex.Message}");
			return TclResult.ERROR;
		}
		TclWrapper.SetResult("");
		return TclResult.OK;
	}

	private TclResult HCISendACLData_Handler(string[] args)
	{
		if (args.Length < 4)
		{
			TclWrapper.SetResult($"Wrong # of arguments.  Usage: {args[0]} ?connHandle? ?pbf? ?bcf? ?data?");
			return TclResult.ERROR;
		}
		if (!btAPI.IsConnectionOpen())
		{
			TclWrapper.SetResult("Connection closed");
			return TclResult.ERROR;
		}
		int connectionHandle = 0;
		int.TryParse(args[1], NumberStyles.HexNumber, null, out connectionHandle);
		int num = 0;
		int.TryParse(args[2], NumberStyles.HexNumber, null, out num);
		int flags = 0;
		int.TryParse(args[3], NumberStyles.HexNumber, null, out flags);
		string[] array = TclWrapper.SplitList(args[4]);
		int num2 = array.Length;
		if (num2 > 65535)
		{
			TclWrapper.SetResult($"Wrong # of arguments: Packet length invalid, should be < 65535!");
			return TclResult.ERROR;
		}
		byte[] array2 = new byte[num2];
		for (int i = 0; i < num2; i++)
		{
			byte.TryParse(array[i], NumberStyles.HexNumber, null, out array2[i]);
		}
		try
		{
			btAPI.ACLData(connectionHandle, flags, num2, array2);
		}
		catch (Exception ex)
		{
			TclWrapper.SetResult($"Caught exception send ACL Data.\r\nSource: {ex.Source}\r\nMessage: {ex.Message}");
			return TclResult.ERROR;
		}
		TclWrapper.SetResult("");
		return TclResult.OK;
	}

	private TclResult HCISetFlowControl_Handler(string[] args)
	{
		if (args.Length < 2)
		{
			TclWrapper.SetResult($"Wrong # of arguments.  Usage: {args[0]} ?<ON> <OFF>?");
			return TclResult.ERROR;
		}
		if (!btAPI.IsConnectionOpen())
		{
			TclWrapper.SetResult("Connection closed");
			return TclResult.ERROR;
		}
		if (args[1].Equals("ON"))
		{
			SetHciFlowControl(flowControl: true);
			return TclResult.OK;
		}
		if (args[1].Equals("OFF"))
		{
			SetHciFlowControl(flowControl: false);
			return TclResult.OK;
		}
		TclWrapper.SetResult($"Unknow arguments.  Usage: {args[0]} ?<ON> <OFF>?");
		return TclResult.ERROR;
	}

	private static ushort ReverseBytes(ushort val)
	{
		byte[] bytes = BitConverter.GetBytes(val);
		Array.Reverse(bytes);
		return BitConverter.ToUInt16(bytes, 0);
	}

	private void SetHciFlowControl(bool flowControl)
	{
		try
		{
			btAPI.FlowControl(flowControl);
		}
		catch (Exception ex)
		{
			TclWrapper.SetResult($"Caught exception Flow Control.\r\nSource: {ex.Source}\r\nMessage: {ex.Message}");
		}
	}

	private TclResult HCISendRawData_Handler(string[] args)
	{
		if (args.Length < 2)
		{
			TclWrapper.SetResult($"Wrong # of arguments.  Usage: {args[0]} ?message?");
			return TclResult.ERROR;
		}
		if (!btAPI.IsConnectionOpen())
		{
			TclWrapper.SetResult("Connection closed");
			return TclResult.ERROR;
		}
		try
		{
			string text = args[1];
			text = text.Replace(" ", "");
			_ = text.Length;
			ushort val = 0;
			ushort.TryParse(text.Substring(0, 4), NumberStyles.HexNumber, null, out val);
			val = ReverseBytes(val);
			int num = 0;
			int.TryParse(text.Substring(4, 2), NumberStyles.HexNumber, null, out num);
			string text2 = text.Substring(6);
			int num2 = 0;
			num2 = text2.Length;
			if (num2 % 2 != 0)
			{
				TclWrapper.SetResult($"Wrong # of payLoad.");
				return TclResult.ERROR;
			}
			num2 /= 2;
			byte[] array = new byte[num2];
			for (int i = 0; i < num2; i++)
			{
				byte.TryParse(text2.Substring(i * 2, 2), NumberStyles.HexNumber, null, out array[i]);
			}
			if (num < num2)
			{
				num = num2;
			}
			btAPI.SendGenericCommand(val, num, array);
		}
		catch (Exception ex)
		{
			TclWrapper.SetResult($"Caught exception Send Raw Data Handler.\r\nSource: {ex.Source}\r\nMessage: {ex.Message}");
			return TclResult.ERROR;
		}
		return TclResult.OK;
	}

	private TclResult HCI_SendRawDataNoProtocolByte_Handler(string[] args)
	{
		if (args.Length < 2)
		{
			TclWrapper.SetResult($"Wrong # of arguments.  Usage: {args[0]} ?message?");
			return TclResult.ERROR;
		}
		if (!btAPI.IsConnectionOpen())
		{
			TclWrapper.SetResult("Connection closed");
			return TclResult.ERROR;
		}
		try
		{
			string text = args[1];
			text = text.Replace(" ", "");
			byte[] array = new byte[text.Length / 2];
			int num = 0;
			int num2 = 0;
			while (num < text.Length)
			{
				array[num2] = byte.Parse(text.Substring(num, 2), NumberStyles.HexNumber);
				num++;
				num++;
				num2++;
			}
			btAPI.Send(array, performValidPacketCheck: true);
		}
		catch (Exception ex)
		{
			TclWrapper.SetResult($"Caught exception Send Raw Data Handler.\r\nSource: {ex.Source}\r\nMessage: {ex.Message}");
			return TclResult.ERROR;
		}
		return TclResult.OK;
	}
}
