using System;
using System.Collections.Generic;
using QCRPG.Framework;

namespace QCRPG.TSL.FMHCI;

public class ProtocolHCI : ProtocolCommon
{
	private enum ReceiveState
	{
		NEW_PACKET,
		GETTING_PACKET_IDENTIFIER,
		GETTING_PAYLOAD_LENGTH,
		GETTING_PAYLOAD
	}

	public const byte uartProtocolIDByteCommand = 17;

	public const byte uartProtocolIDByteEvent = 20;

	public const byte uartProtocolIDByteBTCommand = 1;

	public const byte uartProtocolIDByteBTEvent = 4;

	private List<byte> currentPacketReceived = new List<byte>();

	private FMHCIPacketDataType currentPacketTypeReceived;

	private int gotPacketIdentifierLength;

	private int payloadLength;

	private int gotFullHeaderLength;

	private List<byte> incomingDataToSendAfterPostProcessing = new List<byte>();

	private bool useBTProtocolIDBytes;

	public bool autoDetectProtocolIDByteToUse;

	private ReceiveState receiveState;

	public bool UseBTProtocolIDBytes
	{
		get
		{
			return useBTProtocolIDBytes;
		}
		set
		{
			if (!autoDetectProtocolIDByteToUse)
			{
				useBTProtocolIDBytes = value;
			}
		}
	}

	public bool AutoDetectProtocolIDByteToUse
	{
		get
		{
			return autoDetectProtocolIDByteToUse;
		}
		set
		{
			autoDetectProtocolIDByteToUse = value;
		}
	}

	public override bool ReceiveStateIsNewPacket => receiveState == ReceiveState.NEW_PACKET;

	public void Reset()
	{
		ClearCurrentPacket(notifyObserversAboutPostProcessingDataFirst: false);
		currentPacketTypeReceived = FMHCIPacketDataType.Raw;
		receiveState = ReceiveState.NEW_PACKET;
	}

	public override void Send(OutgoingPacketData packetData)
	{
		OutgoingPacketData arg = AddUARTProtocolIDByte(packetData);
		WakeUpDeviceIfNeeded();
		sendDataNotifier.NotifyObservers(arg);
		PutDeviceToSleepIfNeeded();
	}

	private OutgoingPacketData AddUARTProtocolIDByte(OutgoingPacketData packetData)
	{
		byte b = 0;
		if (!(packetData is FMHCIOutgoingPacketData))
		{
			IncomingPacketData arg = new IncomingPacketData(PacketDataType.WarningMessage, "Got non-FMHCIOutgoingPacketData, not adding UART protocol byte");
			receiveDataNotifier.NotifyObservers(arg);
			return packetData;
		}
		FMHCIOutgoingPacketData fMHCIOutgoingPacketData = (FMHCIOutgoingPacketData)packetData;
		if (fMHCIOutgoingPacketData.fmhciPacketType == FMHCIPacketDataType.Raw)
		{
			return packetData;
		}
		if (fMHCIOutgoingPacketData.fmhciPacketType == FMHCIPacketDataType.Command)
		{
			b = (byte)(useBTProtocolIDBytes ? 1 : 17);
		}
		else
		{
			if (fMHCIOutgoingPacketData.fmhciPacketType != FMHCIPacketDataType.Event)
			{
				throw new Exception("Unsupported FMHCI packet type: " + fMHCIOutgoingPacketData.fmhciPacketType);
			}
			b = (byte)((!useBTProtocolIDBytes) ? 20 : 4);
		}
		List<byte> list = new List<byte>();
		list.Add(b);
		list.AddRange(packetData.bytes);
		return new OutgoingPacketData(list.ToArray());
	}

	public override void NotifyObserver(IncomingPacketData packetData)
	{
		if (packetData.type != 0)
		{
			receiveDataNotifier.NotifyObservers(packetData);
		}
		else
		{
			ReceiveWithProtocolIDCheck(packetData);
		}
	}

	private void ReceiveWithProtocolIDCheck(IncomingPacketData packetData)
	{
		for (int i = 0; i < packetData.bytes.Length; i++)
		{
			if (receiveState == ReceiveState.NEW_PACKET)
			{
				if (ProcessSleepProtocolBytes(packetData, ref i))
				{
					continue;
				}
				incomingDataToSendAfterPostProcessing.Add(packetData.bytes[i]);
				bool flag = true;
				if (autoDetectProtocolIDByteToUse)
				{
					if (packetData.bytes[i] == 20 || packetData.bytes[i] == 17)
					{
						useBTProtocolIDBytes = false;
					}
					else if (packetData.bytes[i] == 4 || packetData.bytes[i] == 1)
					{
						useBTProtocolIDBytes = true;
					}
				}
				if (packetData.bytes[i] == 20)
				{
					flag = false;
					currentPacketTypeReceived = FMHCIPacketDataType.Event;
					UpdatePacketParsingInfoAndReceiveState();
				}
				else if (packetData.bytes[i] == 17)
				{
					flag = false;
					currentPacketTypeReceived = FMHCIPacketDataType.Command;
					UpdatePacketParsingInfoAndReceiveState();
				}
				else if (useBTProtocolIDBytes)
				{
					if (packetData.bytes[i] == 4)
					{
						flag = false;
						currentPacketTypeReceived = FMHCIPacketDataType.Event;
						UpdatePacketParsingInfoAndReceiveState();
					}
					else if (packetData.bytes[i] == 1)
					{
						flag = false;
						currentPacketTypeReceived = FMHCIPacketDataType.Command;
						UpdatePacketParsingInfoAndReceiveState();
					}
				}
				if (flag)
				{
					IncomingPacketData arg = new IncomingPacketData(PacketDataType.WarningMessage, "Unexpected protocol ID byte: 0x" + packetData.bytes[i].ToString("X2"));
					receiveDataNotifier.NotifyObservers(arg);
					ClearCurrentPacket(notifyObserversAboutPostProcessingDataFirst: true);
				}
			}
			else if (receiveState == ReceiveState.GETTING_PACKET_IDENTIFIER)
			{
				currentPacketReceived.Add(packetData.bytes[i]);
				incomingDataToSendAfterPostProcessing.Add(packetData.bytes[i]);
				if (currentPacketReceived.Count == gotPacketIdentifierLength)
				{
					receiveState = ReceiveState.GETTING_PAYLOAD_LENGTH;
				}
			}
			else if (receiveState == ReceiveState.GETTING_PAYLOAD_LENGTH)
			{
				currentPacketReceived.Add(packetData.bytes[i]);
				incomingDataToSendAfterPostProcessing.Add(packetData.bytes[i]);
				if (currentPacketReceived.Count == gotFullHeaderLength)
				{
					payloadLength = currentPacketReceived[gotFullHeaderLength - 1];
					receiveState = ReceiveState.GETTING_PAYLOAD;
					if (payloadLength == 0)
					{
						HandleCompletePacket();
						receiveState = ReceiveState.NEW_PACKET;
					}
				}
			}
			else
			{
				if (receiveState != ReceiveState.GETTING_PAYLOAD)
				{
					throw new Exception("Unsupported receive state: " + receiveState);
				}
				currentPacketReceived.Add(packetData.bytes[i]);
				incomingDataToSendAfterPostProcessing.Add(packetData.bytes[i]);
				if (currentPacketReceived.Count == gotFullHeaderLength + payloadLength)
				{
					HandleCompletePacket();
					receiveState = ReceiveState.NEW_PACKET;
				}
			}
		}
	}

	private void UpdatePacketParsingInfoAndReceiveState()
	{
		receiveState = ReceiveState.GETTING_PACKET_IDENTIFIER;
		if (currentPacketTypeReceived == FMHCIPacketDataType.Event)
		{
			gotPacketIdentifierLength = 1;
			gotFullHeaderLength = 2;
			return;
		}
		if (currentPacketTypeReceived == FMHCIPacketDataType.Command)
		{
			gotPacketIdentifierLength = 2;
			gotFullHeaderLength = 3;
			return;
		}
		IncomingPacketData arg = new IncomingPacketData(PacketDataType.WarningMessage, "Unexpected packet type: " + currentPacketTypeReceived);
		receiveDataNotifier.NotifyObservers(arg);
		ClearCurrentPacket(notifyObserversAboutPostProcessingDataFirst: true);
		receiveState = ReceiveState.NEW_PACKET;
	}

	public new void RegisterReceiveDataObserverPostProcessing(QCRPG.Framework.IObserver<IncomingPacketData> observer)
	{
		receiveDataNotifierPostProcessing.RegisterObserver(observer);
	}

	public new void UnregisterReceiveDataObserverPostProcessing(QCRPG.Framework.IObserver<IncomingPacketData> observer)
	{
		receiveDataNotifierPostProcessing.UnregisterObserver(observer);
	}

	private void HandleCompletePacket()
	{
		IncomingPacketData arg = new IncomingPacketData(incomingDataToSendAfterPostProcessing.ToArray());
		receiveDataNotifierPostProcessing.NotifyObservers(arg);
		FMHCIIncomingPacketData fMHCIIncomingPacketData = new FMHCIIncomingPacketData(currentPacketReceived.ToArray());
		fMHCIIncomingPacketData.fmhciPacketType = currentPacketTypeReceived;
		receiveDataNotifier.NotifyObservers(fMHCIIncomingPacketData);
		ClearCurrentPacket(notifyObserversAboutPostProcessingDataFirst: false);
	}

	private void ClearCurrentPacket(bool notifyObserversAboutPostProcessingDataFirst)
	{
		if (notifyObserversAboutPostProcessingDataFirst)
		{
			IncomingPacketData arg = new IncomingPacketData(incomingDataToSendAfterPostProcessing.ToArray());
			receiveDataNotifierPostProcessing.NotifyObservers(arg);
		}
		incomingDataToSendAfterPostProcessing.Clear();
		currentPacketReceived.Clear();
	}
}
