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

namespace QCRPG.Framework;

public class SharedConnectionRouter : IObserver<IncomingPacketData>, IObserver<OutgoingPacketData>
{
	private const int INVALID_ID = int.MaxValue;

	internal const ushort DEFAULT_SUBID = ushort.MaxValue;

	private ConnectionBase connection;

	private int currentId = int.MaxValue;

	private int numberOfBytesDiscarded;

	private Dictionary<int, ProtocolBase> incomingDataReceiverMap = new Dictionary<int, ProtocolBase>();

	private Dictionary<int, int> subIdOffsetMap = new Dictionary<int, int>();

	private Thread receiveThread;

	private Queue<IncomingPacketData> receiveQueue = new Queue<IncomingPacketData>();

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

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

	private List<IncomingPacketData> receiveDataList = new List<IncomingPacketData>();

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

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

	private int timeout = -1;

	public SharedConnectionRouter(ConnectionBase connection, int timeout = -1)
	{
		this.connection = connection;
		connection.RegisterReceiveDataObserver(this);
		this.timeout = timeout;
		receiveThread = new Thread(ReceiveThread);
		receiveThread.Name = "SharedConnectionRouter-Receive";
		receiveThread.IsBackground = true;
		receiveThread.Start();
	}

	public void RegisterReceiveProtocol(byte id, ProtocolBase protocol, int subIdOffset = 0)
	{
		lock (incomingDataReceiverMap)
		{
			if (!incomingDataReceiverMap.ContainsKey(id))
			{
				incomingDataReceiverMap.Add(id, protocol);
				if (protocol == null)
				{
					subIdOffsetMap.Add(id, subIdOffset);
					return;
				}
			}
			protocol.RegisterSendDataObserver(this);
		}
	}

	public void RegisterReceiveProtocol(ushort subId, ProtocolBase protocol)
	{
		lock (incomingDataReceiverMap)
		{
			if (!incomingDataReceiverMap.ContainsKey(subId))
			{
				incomingDataReceiverMap.Add(subId, protocol);
			}
			protocol.RegisterSendDataObserver(this);
		}
	}

	public void UnregisterReceiveProtocol(byte id)
	{
		lock (incomingDataReceiverMap)
		{
			if (incomingDataReceiverMap.ContainsKey(id))
			{
				if (incomingDataReceiverMap[id] != null)
				{
					incomingDataReceiverMap[id].UnregisterSendDataObserver(this);
				}
				else if (subIdOffsetMap.ContainsKey(id))
				{
					subIdOffsetMap.Remove(id);
				}
				incomingDataReceiverMap.Remove(id);
			}
		}
	}

	public void UnregisterReceiveProtocol(ushort subId)
	{
		lock (incomingDataReceiverMap)
		{
			if (incomingDataReceiverMap.ContainsKey(subId))
			{
				incomingDataReceiverMap[subId].UnregisterSendDataObserver(this);
				incomingDataReceiverMap.Remove(subId);
			}
		}
	}

	public void UnregisterAllReceiveProtocols()
	{
		lock (incomingDataReceiverMap)
		{
			foreach (int item in incomingDataReceiverMap.Keys.ToList())
			{
				if (incomingDataReceiverMap[item] != null)
				{
					incomingDataReceiverMap[item].UnregisterSendDataObserver(this);
				}
				else if (subIdOffsetMap.ContainsKey(item))
				{
					subIdOffsetMap.Remove(item);
				}
				incomingDataReceiverMap.Remove(item);
			}
		}
	}

	public void NotifyObserver(OutgoingPacketData packetData)
	{
		LogDebugLine("NotifyObserver(OutgoingPacketData arg): " + Conversions.ByteArrayToHexString(packetData.bytes));
		if (packetData.type == PacketDataType.Notification && packetData.bytes[0] == 11)
		{
			LogDebugLine("Got a packet complete notification");
			currentId = int.MaxValue;
		}
	}

	public void NotifyObserver(IncomingPacketData packetData)
	{
		lock (receiveQueue)
		{
			receiveQueue.Enqueue(packetData);
			receiveDataQueuedEvent.Set();
		}
	}

	public int GetNumberOfBytesDiscarded()
	{
		return numberOfBytesDiscarded;
	}

	public void ResetNumberOfBytesDiscarded()
	{
		numberOfBytesDiscarded = 0;
	}

	public void ResetState()
	{
		currentId = int.MaxValue;
	}

	private bool SharedReceiveStateIsNewPacket()
	{
		return incomingDataReceiverMap.All((KeyValuePair<int, ProtocolBase> x) => x.Value == null || (x.Value != null && x.Value.ReceiveStateIsNewPacket));
	}

	private void HandleIncomingPacketData(IncomingPacketData packetData)
	{
		LogDebugLine("HandleIncomingPacketData(IncomingPacketData arg): " + Conversions.ByteArrayToHexString(packetData.bytes));
		lock (incomingDataReceiverMap)
		{
			byte[] bytes = packetData.bytes;
			for (int i = 0; i < bytes.Length; i++)
			{
				byte b = bytes[i];
				LogDebugLine("Byte = 0x" + b.ToString("X2") + ", Current ID = 0x" + currentId.ToString("X2"));
				if ((currentId == int.MaxValue || SharedReceiveStateIsNewPacket()) && holdDataList.Count == 0)
				{
					if (incomingDataReceiverMap.ContainsKey(b))
					{
						currentId = b;
						LogDebugLine("New current ID = 0x" + currentId.ToString("X2"));
					}
					else
					{
						LogDebugLine("Discarding byte");
						numberOfBytesDiscarded++;
					}
				}
				if (!incomingDataReceiverMap.ContainsKey(currentId))
				{
					continue;
				}
				ProtocolBase value = incomingDataReceiverMap[currentId];
				if (value != null)
				{
					LogDebugLine("Notifying observer up the chain");
					value.NotifyObserver(new IncomingPacketData(b));
					continue;
				}
				LogDebugLine("Holding data until subId is received");
				holdDataList.Add(b);
				int num = subIdOffsetMap[currentId];
				if (holdDataList.Count == num + 2)
				{
					currentId = (int)Conversions.ByteArrayToUint(holdDataList.Skip(num).ToArray(), 2, elementZeroIsMsb: false);
					if (incomingDataReceiverMap.TryGetValue(currentId, out value) || incomingDataReceiverMap.TryGetValue(65535, out value))
					{
						LogDebugLine("Notifying observer up the chain");
						value.NotifyObserver(new IncomingPacketData(holdDataList.ToArray()));
					}
					else
					{
						LogDebugLine("Discarding hold bytes");
						numberOfBytesDiscarded += holdDataList.Count;
					}
					holdDataList.Clear();
				}
			}
		}
	}

	private void ReceiveThread()
	{
		ManualResetEvent[] array = new ManualResetEvent[2] { receiveDataQueuedEvent, shuttingDownEvent };
		while (true)
		{
			int num = WaitHandle.WaitAny(array, timeout);
			if (num == 258)
			{
				foreach (int item in from p in incomingDataReceiverMap
					where p.Value != null
					select p.Key)
				{
					connection.Send(new OutgoingPacketData(PacketDataType.Notification, BitConverter.GetBytes((ushort)item)));
				}
			}
			else if (array[num] == receiveDataQueuedEvent)
			{
				receiveDataList.Clear();
				lock (receiveQueue)
				{
					while (receiveQueue.Count > 0)
					{
						receiveDataList.Add(receiveQueue.Dequeue());
					}
					receiveDataQueuedEvent.Reset();
				}
				foreach (IncomingPacketData receiveData in receiveDataList)
				{
					HandleIncomingPacketData(receiveData);
				}
			}
			else if (array[num] == shuttingDownEvent)
			{
				break;
			}
		}
	}

	private void LogDebugLine(string line)
	{
	}

	~SharedConnectionRouter()
	{
		shuttingDownEvent.Set();
		receiveThread.Join();
	}
}
