using System;
using System.Collections.Generic;

namespace QCRPG.Framework;

public class ProtocolCommon : ProtocolBase
{
	private static Dictionary<ConnectionBase, ConnectionSpecificProtocolInfo> connectionToConnectionSpecificProtocolInfoMap = new Dictionary<ConnectionBase, ConnectionSpecificProtocolInfo>();

	private ConnectionBase connection;

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

	private ConnectionSpecificProtocolInfo getDutSpecificState()
	{
		lock (connectionToConnectionSpecificProtocolInfoMap)
		{
			if (connection != null && connectionToConnectionSpecificProtocolInfoMap.ContainsKey(connection))
			{
				return connectionToConnectionSpecificProtocolInfoMap[connection];
			}
		}
		return null;
	}

	private void addConnectionSpecificProtocolInfo(ConnectionSpecificProtocolInfo connectionSpecificInfo)
	{
		if (connection == null)
		{
			return;
		}
		lock (connectionToConnectionSpecificProtocolInfoMap)
		{
			if (!connectionToConnectionSpecificProtocolInfoMap.ContainsKey(connection))
			{
				connectionToConnectionSpecificProtocolInfoMap.Add(connection, connectionSpecificInfo);
			}
		}
	}

	private void removeConnectionSpecificProtocolInfo()
	{
		if (connection == null)
		{
			return;
		}
		lock (connectionToConnectionSpecificProtocolInfoMap)
		{
			if (connectionToConnectionSpecificProtocolInfoMap.ContainsKey(connection))
			{
				connectionToConnectionSpecificProtocolInfoMap.Remove(connection);
			}
		}
	}

	public void addConnectionSpecificInfo(ConnectionBase connection, ConnectionSpecificProtocolInfo connectionSpecificInfo)
	{
		this.connection = connection;
		addConnectionSpecificProtocolInfo(connectionSpecificInfo);
	}

	public void removeConnectionSpecificInfo()
	{
		removeConnectionSpecificProtocolInfo();
		connection = null;
	}

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

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

	public override void NotifyObserver(IncomingPacketData packetData)
	{
		int currentByteIndex = 0;
		while (currentByteIndex < packetData.bytes.Length)
		{
			ProcessSleepProtocolBytes(packetData, ref currentByteIndex);
		}
	}

	public void SetSleepMode(ConnectionSpecificProtocolInfo.SleepModeState sleepMode)
	{
		ConnectionSpecificProtocolInfo dutSpecificState = getDutSpecificState();
		if (dutSpecificState != null)
		{
			lock (dutSpecificState)
			{
				dutSpecificState.SleepMode = sleepMode;
			}
		}
	}

	public ConnectionSpecificProtocolInfo.SleepModeState GetSleepMode()
	{
		ConnectionSpecificProtocolInfo dutSpecificState = getDutSpecificState();
		return dutSpecificState.SleepMode;
	}

	public bool ProcessSleepProtocolBytes(IncomingPacketData packetData, ref int currentByteIndex)
	{
		if (connection == null || !connection.IsOpen())
		{
			currentByteIndex = packetData.bytes.Length;
			return true;
		}
		ConnectionSpecificProtocolInfo dutSpecificState = getDutSpecificState();
		if (dutSpecificState == null)
		{
			throw new Exception("Connection specific information not initialized at ProcessSleepProtocolBytes().");
		}
		if (dutSpecificState.SleepMode == ConnectionSpecificProtocolInfo.SleepModeState.IN_BAND_ALWAYS_AWAKE || dutSpecificState.SleepMode == ConnectionSpecificProtocolInfo.SleepModeState.IN_BAND_NORMAL_HANDLING)
		{
			if (packetData.bytes[currentByteIndex] == 253)
			{
				receiveDataNotifierPostProcessing.NotifyObservers(new IncomingPacketData(new byte[1] { 253 }));
				int i;
				for (i = 0; currentByteIndex + i < packetData.bytes.Length && packetData.bytes[currentByteIndex + i] == 253; i++)
				{
				}
				dutSpecificState.FdCount += i;
				IncomingPacketData arg = new IncomingPacketData(PacketDataType.WarningMessage, $"Got wake byte from device, count {i}");
				if (!dutSpecificState.SentAckResponse || dutSpecificState.FdCount > 30)
				{
					receiveDataNotifier.NotifyObservers(arg);
					sendDataNotifier.NotifyObservers(new OutgoingPacketData(new byte[1] { 252 }));
					dutSpecificState.FdCount = 0;
					dutSpecificState.SentAckResponse = true;
				}
				else
				{
					new IncomingPacketData(PacketDataType.WarningMessage, $"Ack already sent, count {dutSpecificState.FdCount}");
				}
				currentByteIndex += i;
				return true;
			}
			dutSpecificState.SentAckResponse = false;
			if (packetData.bytes[currentByteIndex] == 254)
			{
				receiveDataNotifierPostProcessing.NotifyObservers(new IncomingPacketData(new byte[1] { 254 }));
				IncomingPacketData arg2 = new IncomingPacketData(PacketDataType.WarningMessage, "Got sleep byte from device");
				receiveDataNotifier.NotifyObservers(arg2);
				currentByteIndex++;
				return true;
			}
			if (packetData.bytes[currentByteIndex] == 252)
			{
				receiveDataNotifierPostProcessing.NotifyObservers(new IncomingPacketData(new byte[1] { 252 }));
				if (dutSpecificState.SentWakeByte)
				{
					dutSpecificState.receivedInBandWakeSleepAckEvent.Set();
					dutSpecificState.awake = true;
					IncomingPacketData arg3 = new IncomingPacketData(PacketDataType.WarningMessage, "Got wake/sleep ACK byte from device");
					receiveDataNotifier.NotifyObservers(arg3);
					dutSpecificState.SentWakeByte = false;
				}
				currentByteIndex++;
				return true;
			}
		}
		return false;
	}

	public void WakeUpDeviceIfNeeded()
	{
		if (connection == null || !connection.IsOpen())
		{
			return;
		}
		ConnectionSpecificProtocolInfo dutSpecificState = getDutSpecificState();
		if (dutSpecificState == null)
		{
			throw new Exception("Connection specific information not initialized at WakeUpDeviceIfNeeded()");
		}
		if (dutSpecificState.SleepMode != ConnectionSpecificProtocolInfo.SleepModeState.IN_BAND_ALWAYS_AWAKE && dutSpecificState.SleepMode != ConnectionSpecificProtocolInfo.SleepModeState.IN_BAND_NORMAL_HANDLING)
		{
			return;
		}
		IncomingPacketData arg;
		if (dutSpecificState.awake)
		{
			arg = new IncomingPacketData(PacketDataType.WarningMessage, "No need to wake up device, already awake");
			receiveDataNotifier.NotifyObservers(arg);
			return;
		}
		arg = new IncomingPacketData(PacketDataType.WarningMessage, "Waking up device");
		receiveDataNotifier.NotifyObservers(arg);
		dutSpecificState.receivedInBandWakeSleepAckEvent.Reset();
		sendDataNotifier.NotifyObservers(new OutgoingPacketData(new byte[1] { 253 }));
		dutSpecificState.SentWakeByte = true;
		int num = 0;
		while (!dutSpecificState.receivedInBandWakeSleepAckEvent.WaitOne(15) && num < 30)
		{
			arg = new IncomingPacketData(PacketDataType.WarningMessage, "Didn't get ACK to wake up byte, retrying");
			receiveDataNotifier.NotifyObservers(arg);
			sendDataNotifier.NotifyObservers(new OutgoingPacketData(new byte[1] { 253 }));
			num++;
		}
		if (num == 30)
		{
			throw new Exception("Didn't get ACK to wake up byte after " + num + " tries, giving up");
		}
		dutSpecificState.receivedInBandWakeSleepAckEvent.Reset();
	}

	public void PutDeviceToSleepIfNeeded()
	{
		if (connection == null || !connection.IsOpen())
		{
			return;
		}
		ConnectionSpecificProtocolInfo dutSpecificState = getDutSpecificState();
		if (dutSpecificState == null)
		{
			throw new Exception("Connection specific information not initialized at PutDeviceToSleepIfNeeded()");
		}
		if (dutSpecificState.SleepMode == ConnectionSpecificProtocolInfo.SleepModeState.IN_BAND_NORMAL_HANDLING)
		{
			IncomingPacketData arg;
			if (!dutSpecificState.awake)
			{
				arg = new IncomingPacketData(PacketDataType.WarningMessage, "No need to put device to sleep, already alseep");
				receiveDataNotifier.NotifyObservers(arg);
				return;
			}
			arg = new IncomingPacketData(PacketDataType.WarningMessage, "Putting device to sleep");
			receiveDataNotifier.NotifyObservers(arg);
			sendDataNotifier.NotifyObservers(new OutgoingPacketData(new byte[1] { 254 }));
			dutSpecificState.awake = false;
		}
	}
}
