using System;
using System.Collections.Generic;
using System.Threading;

namespace QCRPG.Framework;

public class ProtocolUARTToI2C : ProtocolBase
{
	private const byte EndOfPacketByte = 10;

	private const byte StopByte = 80;

	private const byte StartByte = 83;

	private const byte InterruptReceivedByte = 105;

	private const byte PayloadLengthOffset = 3;

	private const int delayBeforeSendAfterReadingI2cResponseMs = 50;

	private byte i2cSlaveAddress;

	private int i2cReadResponseTimeoutMs = 3000;

	private bool bufferOutgoingData;

	private List<OutgoingPacketData> outgoingDataBuffer = new List<OutgoingPacketData>();

	public bool ignoreInterrupts;

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

	private readonly List<byte> WaitForInterruptMacro = new List<byte>();

	private readonly List<byte> ValidPacketBytes = new List<byte>();

	private ManualResetEvent receivedI2CReadResponsePacketEvent = new ManualResetEvent(initialState: false);

	private IncomingPacketData lastI2CReadResponseReceived = new IncomingPacketData(new byte[0]);

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

	private GenericNotifier<IncomingPacketData> receiveDataNotifierPostProcessing = new GenericNotifier<IncomingPacketData>();

	private bool setSkipReceiveEnqueueFlag;

	private ManualResetEvent receivedInterruptEvent = new ManualResetEvent(initialState: false);

	private bool justReadI2cResponse;

	public byte I2CSlaveAddress
	{
		get
		{
			return i2cSlaveAddress;
		}
		set
		{
			if (value > 127)
			{
				throw new Exception("Invalid I2C slave address: 0x" + value.ToString("X2"));
			}
			i2cSlaveAddress = value;
		}
	}

	public int I2CReadResponseTimeoutMs
	{
		get
		{
			return i2cReadResponseTimeoutMs;
		}
		set
		{
			i2cReadResponseTimeoutMs = value;
		}
	}

	public bool BufferOutgoingData
	{
		get
		{
			return bufferOutgoingData;
		}
		set
		{
			bufferOutgoingData = value;
		}
	}

	public bool IgnoreInterrupts
	{
		get
		{
			return ignoreInterrupts;
		}
		set
		{
			ignoreInterrupts = value;
		}
	}

	public ProtocolUARTToI2C()
	{
		InitializeReadOnlyLists();
	}

	public override void Send(OutgoingPacketData packetData)
	{
		if (packetData.type == PacketDataType.Data)
		{
			SendI2CWrite(packetData);
			return;
		}
		if (packetData.type == PacketDataType.Notification)
		{
			if (packetData.bytes[0] == 2)
			{
				SendI2CRead(packetData.bytes[1]);
				return;
			}
			if (packetData.bytes[0] == 3)
			{
				FlushOutgoingData();
				return;
			}
			throw new Exception("Unsupported packet data notification: " + packetData.bytes[0]);
		}
		throw new Exception("Unsupported packet data type: " + packetData.type);
	}

	public void SendI2CWrite(OutgoingPacketData packetData, bool includeStopAndWaitForInterruptMacro = true)
	{
		if (justReadI2cResponse)
		{
			justReadI2cResponse = false;
			Thread.Sleep(50);
		}
		List<byte> list = new List<byte>();
		list.Add(83);
		list.AddRange(Conversions.ByteToHexAsciiByteArray((byte)(i2cSlaveAddress << 1)));
		list.AddRange(packetData.ToHexAsciiByteArray());
		if (includeStopAndWaitForInterruptMacro)
		{
			list.Add(80);
			list.AddRange(WaitForInterruptMacro);
		}
		SendOutGoingData(new OutgoingPacketData(list.ToArray()));
	}

	public void SendI2CRead(byte length)
	{
		if (justReadI2cResponse)
		{
			justReadI2cResponse = false;
			Thread.Sleep(50);
		}
		if (length != 0)
		{
			List<byte> list = new List<byte>();
			list.Add(83);
			list.AddRange(Conversions.ByteToHexAsciiByteArray((byte)((uint)(i2cSlaveAddress << 1) | 1u)));
			list.AddRange(Conversions.ByteToHexAsciiByteArray(length));
			list.Add(80);
			list.AddRange(WaitForInterruptMacro);
			SendOutGoingData(new OutgoingPacketData(list.ToArray()));
		}
	}

	public void FlushOutgoingData()
	{
		lock (outgoingDataBuffer)
		{
			foreach (OutgoingPacketData item in outgoingDataBuffer)
			{
				sendDataNotifier.NotifyObservers(item);
			}
			outgoingDataBuffer.Clear();
		}
	}

	public void ClearBufferedOutgoingData()
	{
		lock (outgoingDataBuffer)
		{
			outgoingDataBuffer.Clear();
		}
	}

	public IncomingPacketData SendI2CReadAndGetResponse(byte length, bool setSkipReceiveEnqueueFlag = false)
	{
		if (length == 0)
		{
			return new IncomingPacketData(new byte[0]);
		}
		this.setSkipReceiveEnqueueFlag = setSkipReceiveEnqueueFlag;
		SendI2CRead(length);
		receivedI2CReadResponsePacketEvent.Reset();
		if (receivedI2CReadResponsePacketEvent.WaitOne(i2cReadResponseTimeoutMs))
		{
			lock (lastI2CReadResponseReceived)
			{
				if (lastI2CReadResponseReceived.bytes.Length != length)
				{
					throw new Exception("Length mismatch in read response");
				}
				receivedI2CReadResponsePacketEvent.Reset();
				justReadI2cResponse = true;
				return lastI2CReadResponseReceived.DeepCopy();
			}
		}
		this.setSkipReceiveEnqueueFlag = false;
		currentPacket.Clear();
		throw new TimeoutException("Timeout waiting for read response");
	}

	public bool WaitForInterrupt(int timeoutMs)
	{
		bool result = receivedInterruptEvent.WaitOne(timeoutMs);
		receivedInterruptEvent.Reset();
		return result;
	}

	public override void NotifyObserver(IncomingPacketData packetData)
	{
		if (packetData.type != 0)
		{
			receiveDataNotifier.NotifyObservers(packetData);
			return;
		}
		byte[] bytes = packetData.bytes;
		foreach (byte b in bytes)
		{
			incomingDataToSendAfterPostProcessing.Add(b);
			switch (b)
			{
			case 105:
				receiveDataNotifierPostProcessing.NotifyObservers(new IncomingPacketData(incomingDataToSendAfterPostProcessing.ToArray()));
				incomingDataToSendAfterPostProcessing.Clear();
				currentPacket.Clear();
				if (!ignoreInterrupts)
				{
					IncomingPacketData arg = new IncomingPacketData(PacketDataType.Notification, 1);
					receiveDataNotifier.NotifyObservers(arg);
				}
				else
				{
					IncomingPacketData arg2 = new IncomingPacketData(PacketDataType.WarningMessage, "Ignoring interrupt notification");
					receiveDataNotifier.NotifyObservers(arg2);
				}
				receivedInterruptEvent.Set();
				break;
			case 10:
				if (currentPacket.Count % 2 == 0)
				{
					receiveDataNotifierPostProcessing.NotifyObservers(new IncomingPacketData(incomingDataToSendAfterPostProcessing.ToArray()));
					incomingDataToSendAfterPostProcessing.Clear();
					IncomingPacketData incomingPacketData = new IncomingPacketData(currentPacket.ToArray(), performHexAsciiConversion: true);
					currentPacket.Clear();
					lock (lastI2CReadResponseReceived)
					{
						lastI2CReadResponseReceived = incomingPacketData.DeepCopy();
						lastI2CReadResponseReceived.skipReceiveEnqueue = setSkipReceiveEnqueueFlag;
						setSkipReceiveEnqueueFlag = false;
						receivedI2CReadResponsePacketEvent.Set();
					}
					receiveDataNotifier.NotifyObservers(lastI2CReadResponseReceived);
				}
				else
				{
					receiveDataNotifierPostProcessing.NotifyObservers(new IncomingPacketData(incomingDataToSendAfterPostProcessing.ToArray()));
					incomingDataToSendAfterPostProcessing.Clear();
					currentPacket.Clear();
					IncomingPacketData arg3 = new IncomingPacketData(PacketDataType.WarningMessage, "Received invalid length packet in ProtocolUARTToI2C.NotifyObserver(IncomingPacketData)");
					receiveDataNotifier.NotifyObservers(arg3);
				}
				break;
			default:
				if (ValidPacketBytes.Contains(b))
				{
					currentPacket.Add(b);
				}
				break;
			}
		}
	}

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

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

	private void InitializeReadOnlyLists()
	{
		WaitForInterruptMacro.AddRange(new byte[3] { 62, 54, 48 });
		ValidPacketBytes.AddRange(new byte[10] { 48, 49, 50, 51, 52, 53, 54, 55, 56, 57 });
		ValidPacketBytes.AddRange(new byte[6] { 65, 66, 67, 68, 69, 70 });
	}

	private void SendOutGoingData(OutgoingPacketData packetData)
	{
		if (bufferOutgoingData)
		{
			lock (outgoingDataBuffer)
			{
				outgoingDataBuffer.Add(packetData);
				return;
			}
		}
		sendDataNotifier.NotifyObservers(packetData);
	}
}
