using System;
using System.Collections.Generic;
using QCRPG.Framework;
using QCRPG.TSL.FMHCI.Packets;

namespace QCRPG.TSL.FMHCI;

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

	private FMHCIAPI fmhciAPI;

	private FMHCIPacketList fmhciPacketList;

	public FMHCIScripter(FMHCIAPI fmAPI, FMHCIPacketList fmhciPacketList)
	{
		fmhciAPI = fmAPI;
		this.fmhciPacketList = fmhciPacketList;
		modeAgnosticDataReceiver = new ModeAgnosticDataReceiver(fmhciAPI);
		tclCommandMap.Add("HCI_SendFMCmd", HCI_SendFMCmd_Handler);
		tclCommandMap.Add("HciFMPoke", HciFMPoke_Handler);
		tclCommandMap.Add("HciFMPokeWord", HciFMPokeWord_Handler);
		tclCommandMap.Add("HCIFMPOKEWORD", HciFMPokeWord_Handler);
		tclCommandMap.Add("POKEWORD", HciFMPokeWord_Handler);
		tclCommandMap.Add("PokeWord", HciFMPokeWord_Handler);
		tclCommandMap.Add("PeekWord", PeekWord_Handler);
		tclCommandMap.Add("PEEKWORD", PeekWord_Handler);
		tclCommandMap.Add("HciFMPeek", HciFMPeek_Handler);
		tclCommandMap.Add("HciFMSSBIWrite", HciFMSSBIWrite_Handler);
		tclCommandMap.Add("HciFMSSBIRead", HciFMSSBIRead_Handler);
		tclCommandMap.Add("HciFMWaitEvent", HciFMWaitEvent_Handler);
	}

	private TclResult HCI_SendFMCmd_Handler(string[] args)
	{
		if (args.Length != 2)
		{
			TclWrapper.SetResult($"Wrong # of arguments.  Usage: {args[0]} ?commandPacket? ");
			return TclResult.ERROR;
		}
		if (!fmhciAPI.IsConnectionOpen())
		{
			TclWrapper.SetResult("Connection closed");
			return TclResult.ERROR;
		}
		string[] array;
		try
		{
			array = TclWrapper.SplitList(args[1]);
		}
		catch (Exception)
		{
			TclWrapper.SetResult($"Error splitting {args[1]}");
			return TclResult.ERROR;
		}
		if (array.Length < 3 || array.Length > 258)
		{
			TclWrapper.SetResult("Invalid packet length (must be 3-258)");
			return TclResult.ERROR;
		}
		byte[] array2 = new byte[array.Length];
		for (int i = 0; i < array.Length; i++)
		{
			try
			{
				array2[i] = Conversions.HexStringToByte(array[i]);
			}
			catch (Exception)
			{
				TclWrapper.SetResult($"Error converting {array[i]} to a byte");
				return TclResult.ERROR;
			}
		}
		try
		{
			array2[2] = (byte)(array2.Length - 3);
		}
		catch (Exception ex3)
		{
			TclWrapper.SetResult($"Caught exception adjusting payload length, {args[1]}.\r\nSource: {ex3.Source}\r\nMessage: {ex3.Message}");
			return TclResult.ERROR;
		}
		FMHCIOutgoingPacketData fMHCIOutgoingPacketData;
		try
		{
			fMHCIOutgoingPacketData = new FMHCIOutgoingPacketData(array2);
			fMHCIOutgoingPacketData.fmhciPacketType = FMHCIPacketDataType.Command;
			fmhciAPI.Send(fMHCIOutgoingPacketData, performValidPacketCheck: true);
		}
		catch (Exception ex4)
		{
			TclWrapper.SetResult($"Caught exception sending data, {args[1]}.\r\nSource: {ex4.Source}\r\nMessage: {ex4.Message}");
			return TclResult.ERROR;
		}
		uint num = (uint)((array2[1] << 8) | array2[0]);
		try
		{
			List<PacketMatchPattern> list = new List<PacketMatchPattern>();
			int[] array3 = new int[5] { 15, -1, -1, 0, 0 };
			array3[3] = array2[0];
			array3[4] = array2[1];
			list.Add(new PacketMatchPattern(0, array3));
			int[] array4 = new int[6] { 16, -1, -1, -1, 0, 0 };
			array4[4] = array2[0];
			array4[5] = array2[1];
			list.Add(new PacketMatchPattern(0, array4));
			modeAgnosticDataReceiver.Receive(list.ToArray(), 5000);
		}
		catch (TimeoutException)
		{
			TclWrapper.SetResult("Timeout receiving packet matching command opCode, 0x" + num.ToString("X4"));
			return TclResult.ERROR;
		}
		catch (Exception ex6)
		{
			TclWrapper.SetResult(string.Format("Caught exception receiving packet matching command opCode, 0x{0}.\r\nSource: {1}\r\nMessage: {2}", num.ToString("X4"), ex6.Source, ex6.Message));
			return TclResult.ERROR;
		}
		string text = ((byte)17).ToString("X2") + " " + Conversions.ByteArrayToSpacedHexString(fMHCIOutgoingPacketData.bytes);
		TclWrapper.SetResult(text);
		return TclResult.OK;
	}

	private TclResult HciFMPoke_Handler(string[] args)
	{
		if (args.Length < 5)
		{
			TclWrapper.SetResult($"Wrong # of arguments.  Usage: {args[0]} ?subOpCode? ?startAddress? ?length? ?value0? ?value1? ... etc., where subOpCode = 0x01 (byte) or 0x0C (word)");
			return TclResult.ERROR;
		}
		List<int> list = new List<int>();
		int num;
		try
		{
			num = Conversions.PrefixedStringToInt(args[1]);
		}
		catch (Exception)
		{
			TclWrapper.SetResult($"Error converting subOpCode {args[1]} to an int");
			return TclResult.ERROR;
		}
		int address;
		try
		{
			address = Conversions.PrefixedStringToInt(args[2]);
		}
		catch (Exception)
		{
			TclWrapper.SetResult($"Error converting startAddress {args[2]} to an int");
			return TclResult.ERROR;
		}
		int num2;
		try
		{
			num2 = Conversions.PrefixedStringToInt(args[3]);
		}
		catch (Exception)
		{
			TclWrapper.SetResult($"Error converting length {args[3]} to an int");
			return TclResult.ERROR;
		}
		for (int i = 4; i < args.Length; i++)
		{
			try
			{
				list.Add(Conversions.PrefixedStringToInt(args[i]));
			}
			catch (Exception)
			{
				TclWrapper.SetResult($"Error converting value {args[i]} to an int");
				return TclResult.ERROR;
			}
		}
		if (list.Count != num2)
		{
			int num3 = args.Length - 4;
			TclWrapper.SetResult($"Length does not match number of arguments.  Expected either a length parameter value of {num3} or {num2} byte values after the length parameter.");
			return TclResult.ERROR;
		}
		switch (num)
		{
		case 1:
		{
			PokeByte pokeByte = new PokeByte();
			pokeByte.UserFields.Address = (uint)address;
			pokeByte.UserFields.DataLength = (uint)num2;
			for (int k = 0; k < num2; k++)
			{
				pokeByte.UserFields.DataList[k] = (uint)list[k];
			}
			try
			{
				fmhciAPI.Send(pokeByte);
			}
			catch (Exception ex6)
			{
				TclWrapper.SetResult($"Caught exception sending poke byte, {args[1]}.\r\nSource: {ex6.Source}\r\nMessage: {ex6.Message}");
				return TclResult.ERROR;
			}
			break;
		}
		case 12:
		{
			PokeWord pokeWord = new PokeWord();
			pokeWord.UserFields.Address = (uint)address;
			pokeWord.UserFields.DataLength = (uint)num2;
			for (int j = 0; j < num2; j++)
			{
				int num4 = 0;
				if (j % 4 == 0)
				{
					num4 = j + 3;
				}
				else if (j % 4 == 1)
				{
					num4 = j + 1;
				}
				else if (j % 4 == 2)
				{
					num4 = j - 1;
				}
				else if (j % 4 == 3)
				{
					num4 = j - 3;
				}
				if (num4 > num2 - 1)
				{
					num4 = j;
				}
				pokeWord.UserFields.DataList[j] = (uint)list[num4];
			}
			try
			{
				fmhciAPI.Send(pokeWord);
			}
			catch (Exception ex5)
			{
				TclWrapper.SetResult($"Caught exception sending poke word, {args[1]}.\r\nSource: {ex5.Source}\r\nMessage: {ex5.Message}");
				return TclResult.ERROR;
			}
			break;
		}
		default:
			TclWrapper.SetResult(string.Format("Unsupported SubOpCode (must be 0x01 or 0x0C): 0x" + num.ToString("X")));
			return TclResult.ERROR;
		}
		TclWrapper.SetResult("");
		return TclResult.OK;
	}

	private TclResult HciFMPokeWord_Handler(string[] args)
	{
		if (args.Length < 3)
		{
			TclWrapper.SetResult($"Wrong # of arguments.  Usage: {args[0]} ?Address? ?value? ");
			return TclResult.ERROR;
		}
		int num = 0;
		int num2 = 0;
		new List<int>();
		try
		{
			num2 = Conversions.PrefixedStringToInt(args[1]);
		}
		catch (Exception)
		{
			TclWrapper.SetResult($"Error converting Address {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
		{
			fmhciAPI.SendPokeWord(num2, bytes);
		}
		catch (Exception ex3)
		{
			TclWrapper.SetResult($"Caught exception pokeword.\r\nSource: {ex3.Source}\r\nMessage: {ex3.Message}");
			return TclResult.ERROR;
		}
		TclWrapper.SetResult("");
		return TclResult.OK;
	}

	private TclResult HciFMPeek_Handler(string[] args)
	{
		if (args.Length != 4)
		{
			TclWrapper.SetResult($"Wrong # of arguments.  Usage: {args[0]} ?subOpCode? ?startAddress? ?length?, where subOpCode = 0x02 (byte) or 0x0D (word)");
			return TclResult.ERROR;
		}
		int num;
		try
		{
			num = Conversions.PrefixedStringToInt(args[1]);
		}
		catch (Exception)
		{
			TclWrapper.SetResult($"Error converting subOpCode {args[1]} to an int");
			return TclResult.ERROR;
		}
		int address;
		try
		{
			address = Conversions.PrefixedStringToInt(args[2]);
		}
		catch (Exception)
		{
			TclWrapper.SetResult($"Error converting startAddress {args[2]} to an int");
			return TclResult.ERROR;
		}
		int dataLength;
		try
		{
			dataLength = Conversions.PrefixedStringToInt(args[3]);
		}
		catch (Exception)
		{
			TclWrapper.SetResult($"Error converting length {args[3]} to an int");
			return TclResult.ERROR;
		}
		switch (num)
		{
		case 2:
		{
			PeekByte peekByte = new PeekByte();
			peekByte.UserFields.Address = (uint)address;
			peekByte.UserFields.DataLength = (uint)dataLength;
			try
			{
				fmhciAPI.Send(peekByte);
			}
			catch (Exception ex6)
			{
				TclWrapper.SetResult($"Caught exception sending peek byte, {args[1]}.\r\nSource: {ex6.Source}\r\nMessage: {ex6.Message}");
				return TclResult.ERROR;
			}
			CommandCompleteForPeekByte commandCompleteForPeekByte;
			try
			{
				commandCompleteForPeekByte = (CommandCompleteForPeekByte)modeAgnosticDataReceiver.Receive(typeof(CommandCompleteForPeekByte), 5000);
			}
			catch (Exception ex7)
			{
				TclWrapper.SetResult($"Caught exception waiting for response to peek byte, {args[1]}.\r\nSource: {ex7.Source}\r\nMessage: {ex7.Message}");
				return TclResult.ERROR;
			}
			byte[] byteArray2 = Conversions.UintListToByteArray(commandCompleteForPeekByte.UserFields.DataList);
			TclWrapper.SetResult(Conversions.ByteArrayTo0xHexString(byteArray2));
			return TclResult.OK;
		}
		case 13:
		{
			PeekWord peekWord = new PeekWord();
			peekWord.UserFields.Address = (uint)address;
			peekWord.UserFields.DataLength = (uint)dataLength;
			try
			{
				fmhciAPI.Send(peekWord);
			}
			catch (Exception ex4)
			{
				TclWrapper.SetResult($"Caught exception sending peek word, {args[1]}.\r\nSource: {ex4.Source}\r\nMessage: {ex4.Message}");
				return TclResult.ERROR;
			}
			CommandCompleteForPeekWord commandCompleteForPeekWord;
			try
			{
				commandCompleteForPeekWord = (CommandCompleteForPeekWord)modeAgnosticDataReceiver.Receive(typeof(CommandCompleteForPeekWord), 5000);
			}
			catch (Exception ex5)
			{
				TclWrapper.SetResult($"Caught exception waiting for response to peek word, {args[1]}.\r\nSource: {ex5.Source}\r\nMessage: {ex5.Message}");
				return TclResult.ERROR;
			}
			byte[] byteArray = Conversions.UintListToByteArray(commandCompleteForPeekWord.UserFields.DataList);
			TclWrapper.SetResult(Conversions.ByteArrayTo0xHexString(byteArray));
			return TclResult.OK;
		}
		default:
			TclWrapper.SetResult(string.Format("Unsupported SubOpCode (must be 0x02 or 0x0D): 0x" + num.ToString("X")));
			return TclResult.ERROR;
		}
	}

	private TclResult PeekWord_Handler(string[] args)
	{
		if (args.Length != 2)
		{
			TclWrapper.SetResult($"Wrong # of arguments.  Usage: PeekWord ?startAddress?");
			return TclResult.ERROR;
		}
		int address;
		try
		{
			address = Conversions.PrefixedStringToInt(args[1]);
		}
		catch (Exception)
		{
			TclWrapper.SetResult($"Error converting startAddress {args[1]} to an int");
			return TclResult.ERROR;
		}
		PeekWord peekWord = new PeekWord();
		peekWord.UserFields.Address = (uint)address;
		peekWord.UserFields.DataLength = 4u;
		try
		{
			fmhciAPI.Send(peekWord);
		}
		catch (Exception ex2)
		{
			TclWrapper.SetResult($"Caught exception sending peek word, {args[1]}.\r\nSource: {ex2.Source}\r\nMessage: {ex2.Message}");
			return TclResult.ERROR;
		}
		CommandCompleteForPeekWord commandCompleteForPeekWord;
		try
		{
			commandCompleteForPeekWord = (CommandCompleteForPeekWord)modeAgnosticDataReceiver.Receive(typeof(CommandCompleteForPeekWord), 5000);
		}
		catch (Exception ex3)
		{
			TclWrapper.SetResult($"Caught exception waiting for response to peek word, {args[1]}.\r\nSource: {ex3.Source}\r\nMessage: {ex3.Message}");
			return TclResult.ERROR;
		}
		byte[] byteArray = Conversions.UintListToByteArray(commandCompleteForPeekWord.UserFields.DataList);
		TclWrapper.SetResult(Conversions.ByteArrayTo0xHexString(byteArray));
		return TclResult.OK;
	}

	private TclResult HciFMSSBIWrite_Handler(string[] args)
	{
		if (args.Length != 3)
		{
			TclWrapper.SetResult($"Wrong # of arguments.  Usage: {args[0]} ?address? ?value?");
			return TclResult.ERROR;
		}
		int address;
		try
		{
			address = Conversions.PrefixedStringToInt(args[1]);
		}
		catch (Exception)
		{
			TclWrapper.SetResult($"Error converting address {args[1]} to an int");
			return TclResult.ERROR;
		}
		int data;
		try
		{
			data = Conversions.PrefixedStringToInt(args[2]);
		}
		catch (Exception)
		{
			TclWrapper.SetResult($"Error converting value {args[2]} to an int");
			return TclResult.ERROR;
		}
		SsbiPokeRegister ssbiPokeRegister = new SsbiPokeRegister();
		ssbiPokeRegister.UserFields.Address = (uint)address;
		ssbiPokeRegister.UserFields.Data = (uint)data;
		try
		{
			fmhciAPI.Send(ssbiPokeRegister);
		}
		catch (Exception ex3)
		{
			TclWrapper.SetResult($"Caught exception sending SSBI poke, {args[1]}.\r\nSource: {ex3.Source}\r\nMessage: {ex3.Message}");
			return TclResult.ERROR;
		}
		TclWrapper.SetResult("");
		return TclResult.OK;
	}

	private TclResult HciFMSSBIRead_Handler(string[] args)
	{
		if (args.Length != 2)
		{
			TclWrapper.SetResult($"Wrong # of arguments.  Usage: {args[0]} ?address?");
			return TclResult.ERROR;
		}
		int address;
		try
		{
			address = Conversions.PrefixedStringToInt(args[1]);
		}
		catch (Exception)
		{
			TclWrapper.SetResult($"Error converting address {args[1]} to an int");
			return TclResult.ERROR;
		}
		SsbiPeekRegister ssbiPeekRegister = new SsbiPeekRegister();
		ssbiPeekRegister.UserFields.Address = (uint)address;
		try
		{
			fmhciAPI.Send(ssbiPeekRegister);
		}
		catch (Exception ex2)
		{
			TclWrapper.SetResult($"Caught exception sending SSBI peek, {args[1]}.\r\nSource: {ex2.Source}\r\nMessage: {ex2.Message}");
			return TclResult.ERROR;
		}
		CommandCompleteForSsbiPeekRegister commandCompleteForSsbiPeekRegister;
		try
		{
			commandCompleteForSsbiPeekRegister = (CommandCompleteForSsbiPeekRegister)modeAgnosticDataReceiver.Receive(typeof(CommandCompleteForSsbiPeekRegister), 5000);
		}
		catch (Exception ex3)
		{
			TclWrapper.SetResult($"Caught exception waiting for response to SSBI peek, {args[1]}.\r\nSource: {ex3.Source}\r\nMessage: {ex3.Message}");
			return TclResult.ERROR;
		}
		TclWrapper.SetResult("0x" + commandCompleteForSsbiPeekRegister.UserFields.Data.ToString("X2"));
		return TclResult.OK;
	}

	private TclResult HciFMWaitEvent_Handler(string[] args)
	{
		if (args.Length < 2 || args.Length > 4)
		{
			TclWrapper.SetResult(string.Format("Wrong # of arguments.  Usage: {0} ?opCode? or {0} ?opCode? ?timeoutSeconds?", args[0]));
			return TclResult.ERROR;
		}
		int timeoutMilliseconds = 5000;
		if (args.Length == 3 || args.Length == 4)
		{
			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 (!fmhciAPI.IsConnectionOpen())
		{
			TclWrapper.SetResult("Connection closed");
			return TclResult.ERROR;
		}
		PacketBase packetBase;
		if (args[1] == "ANY_EVENT")
		{
			try
			{
				List<Type> list = new List<Type>();
				list.AddRange(fmhciPacketList.GetResponsePacketTypes());
				list.AddRange(fmhciPacketList.GetNotificationPacketTypes());
				packetBase = modeAgnosticDataReceiver.Receive(list.ToArray(), timeoutMilliseconds);
			}
			catch (TimeoutException)
			{
				TclWrapper.SetResult("Timeout waiting for any FM HCI event");
				return TclResult.ERROR;
			}
			catch (Exception ex3)
			{
				TclWrapper.SetResult($"Caught exception waiting for any FM HCI event.\r\nSource: {ex3.Source}\r\nMessage: {ex3.Message}");
				return TclResult.ERROR;
			}
		}
		else
		{
			uint num2 = 0u;
			try
			{
				num2 = Convert.ToUInt32(args[1], 16);
			}
			catch (Exception)
			{
				TclWrapper.SetResult($"Error converting eventCode {args[1]} to a uint");
				return TclResult.ERROR;
			}
			if (num2 > 255)
			{
				TclWrapper.SetResult($"Invalid eventCode value (must be 00-FF): {num2}");
				return TclResult.ERROR;
			}
			try
			{
				List<PacketMatchPattern> list2 = new List<PacketMatchPattern>();
				list2.Add(new PacketMatchPattern(0, new int[1] { (int)num2 }));
				packetBase = modeAgnosticDataReceiver.Receive(list2.ToArray(), timeoutMilliseconds);
			}
			catch (TimeoutException)
			{
				TclWrapper.SetResult($"Timeout waiting for specific FM HCI event, {args[1]}");
				return TclResult.ERROR;
			}
			catch (Exception ex6)
			{
				TclWrapper.SetResult($"Caught exception waiting for specific FM HCI event, {args[1]}.\r\nSource: {ex6.Source}\r\nMessage: {ex6.Message}");
				return TclResult.ERROR;
			}
		}
		string text = ((byte)20).ToString("X2") + " " + Conversions.ByteArrayToSpacedHexString(packetBase.ToByteArray());
		TclWrapper.SetResult(text);
		return TclResult.OK;
	}
}
