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

namespace QCRPG.Framework;

public class ModeAgnosticDataReceiver : IObserver<PacketBase>
{
	private APIBase api;

	private PacketReceiveMode packetReceiveMode;

	private Queue<PacketBase> receivedPacketQueue = new Queue<PacketBase>();

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

	private bool skipNextPacket;

	public ModeAgnosticDataReceiver(APIBase api)
	{
		this.api = api;
		packetReceiveMode = api.GetPacketReceiveMode();
	}

	public void Startup()
	{
		if (packetReceiveMode != PacketReceiveMode.Asynchronous)
		{
			api.ClearReceiveQueue();
		}
		lock (receivedPacketQueue)
		{
			receivedPacketQueue.Clear();
			receivedPacketEvent.Reset();
		}
		if (packetReceiveMode != 0)
		{
			api.RegisterReceivePacketObserver(this);
		}
		if (packetReceiveMode == PacketReceiveMode.Hybrid)
		{
			api.RegisterHybridReceiveModeQueuedPacketObserver(this);
		}
	}

	public void Shutdown()
	{
		if (packetReceiveMode != 0)
		{
			api.UnregisterReceivePacketObserver(this);
		}
		if (packetReceiveMode == PacketReceiveMode.Hybrid)
		{
			api.UnregisterHybridReceiveModeQueuedPacketObserver(this);
		}
		if (packetReceiveMode != PacketReceiveMode.Asynchronous)
		{
			api.ClearReceiveQueue();
		}
		lock (receivedPacketQueue)
		{
			receivedPacketQueue.Clear();
			receivedPacketEvent.Reset();
		}
	}

	public PacketBase Receive(int timeoutMilliseconds)
	{
		if (packetReceiveMode == PacketReceiveMode.Synchronous)
		{
			return api.Receive(timeoutMilliseconds);
		}
		if (!receivedPacketEvent.WaitOne(timeoutMilliseconds))
		{
			throw new TimeoutException("Timeout waiting for any packet (" + timeoutMilliseconds + " msec)");
		}
		lock (receivedPacketQueue)
		{
			PacketBase packetBase = receivedPacketQueue.Dequeue();
			if (receivedPacketQueue.Count == 0)
			{
				receivedPacketEvent.Reset();
			}
			RemoveHybridModeQueuedPacketFromAPIQueue(packetBase, notifyObservers: true);
			return packetBase;
		}
	}

	public PacketBase Receive(Type packetType, int timeoutMilliseconds)
	{
		if (packetReceiveMode == PacketReceiveMode.Synchronous)
		{
			return api.Receive(packetType, timeoutMilliseconds);
		}
		int num = timeoutMilliseconds;
		StopWatch stopWatch = new StopWatch();
		do
		{
			if (!receivedPacketEvent.WaitOne(num))
			{
				throw new TimeoutException("Timeout waiting for packet of type " + packetType.ToString());
			}
			lock (receivedPacketQueue)
			{
				while (receivedPacketQueue.Count > 0)
				{
					PacketBase packetBase = receivedPacketQueue.Dequeue();
					if (packetBase.GetType() == packetType)
					{
						if (receivedPacketQueue.Count == 0)
						{
							receivedPacketEvent.Reset();
						}
						RemoveHybridModeQueuedPacketFromAPIQueue(packetBase, notifyObservers: true);
						return packetBase;
					}
					RemoveHybridModeQueuedPacketFromAPIQueue(packetBase, notifyObservers: false);
				}
				receivedPacketEvent.Reset();
			}
			num -= (int)stopWatch.GetElapsedTime().TotalMilliseconds;
			stopWatch.Reset();
		}
		while (num >= 0);
		throw new TimeoutException("Timeout waiting for packet of type " + packetType.ToString());
	}

	public PacketBase Receive(Type[] packetTypes, int timeoutMilliseconds)
	{
		if (packetReceiveMode == PacketReceiveMode.Synchronous)
		{
			return api.Receive(packetTypes, timeoutMilliseconds);
		}
		int num = timeoutMilliseconds;
		StopWatch stopWatch = new StopWatch();
		do
		{
			if (!receivedPacketEvent.WaitOne(num))
			{
				throw new TimeoutException("Timeout waiting for one of a list of packets: " + Conversions.GenericArrayToString<Type>(packetTypes));
			}
			lock (receivedPacketQueue)
			{
				while (receivedPacketQueue.Count > 0)
				{
					PacketBase packetBase = receivedPacketQueue.Dequeue();
					Type type = packetBase.GetType();
					bool flag = false;
					foreach (Type type2 in packetTypes)
					{
						if (type == type2)
						{
							flag = true;
						}
					}
					if (!flag)
					{
						RemoveHybridModeQueuedPacketFromAPIQueue(packetBase, notifyObservers: false);
						continue;
					}
					if (receivedPacketQueue.Count == 0)
					{
						receivedPacketEvent.Reset();
					}
					RemoveHybridModeQueuedPacketFromAPIQueue(packetBase, notifyObservers: true);
					return packetBase;
				}
				receivedPacketEvent.Reset();
			}
			num -= (int)stopWatch.GetElapsedTime().TotalMilliseconds;
			stopWatch.Reset();
		}
		while (num >= 0);
		throw new TimeoutException("Timeout waiting for one of a list of packets: " + Conversions.GenericArrayToString<Type>(packetTypes));
	}

	public List<PacketBase> ReceiveAll(Type[] packetTypes, int timeoutMilliseconds)
	{
		if (packetReceiveMode == PacketReceiveMode.Synchronous)
		{
			return api.ReceiveAll(packetTypes, timeoutMilliseconds);
		}
		List<PacketBase> list = new List<PacketBase>();
		List<bool> list2 = new List<bool>();
		for (int i = 0; i < packetTypes.Length; i++)
		{
			_ = packetTypes[i];
			list2.Add(item: false);
		}
		int num = timeoutMilliseconds;
		StopWatch stopWatch = new StopWatch();
		do
		{
			if (!receivedPacketEvent.WaitOne(num))
			{
				throw new TimeoutException("Timeout waiting for all packets in a list: " + Conversions.GenericArrayToString<Type>(packetTypes));
			}
			lock (receivedPacketQueue)
			{
				while (receivedPacketQueue.Count > 0)
				{
					PacketBase packetBase = receivedPacketQueue.Dequeue();
					Type type = packetBase.GetType();
					int index = -1;
					bool flag = false;
					for (int j = 0; j < packetTypes.Length; j++)
					{
						if (type == packetTypes[j])
						{
							index = j;
							flag = true;
						}
					}
					if (!flag)
					{
						RemoveHybridModeQueuedPacketFromAPIQueue(packetBase, notifyObservers: false);
						continue;
					}
					list.Add(packetBase);
					RemoveHybridModeQueuedPacketFromAPIQueue(packetBase, notifyObservers: true);
					list2[index] = true;
					if (list2.Contains(item: false))
					{
						continue;
					}
					if (receivedPacketQueue.Count == 0)
					{
						receivedPacketEvent.Reset();
					}
					return list;
				}
				receivedPacketEvent.Reset();
			}
			num -= (int)stopWatch.GetElapsedTime().TotalMilliseconds;
			stopWatch.Reset();
		}
		while (num >= 0);
		throw new TimeoutException("Timeout waiting for all packets in a list: " + Conversions.GenericArrayToString<Type>(packetTypes));
	}

	public PacketBase Receive(PacketMatchPattern[] packetMatchPatterns, int timeoutMilliseconds)
	{
		if (packetReceiveMode == PacketReceiveMode.Synchronous)
		{
			return api.Receive(packetMatchPatterns, timeoutMilliseconds);
		}
		int num = timeoutMilliseconds;
		StopWatch stopWatch = new StopWatch();
		do
		{
			if (!receivedPacketEvent.WaitOne(num))
			{
				throw new TimeoutException("Timeout waiting for one of a list of packet patterns: " + Conversions.GenericArrayToString<PacketMatchPattern>(packetMatchPatterns));
			}
			lock (receivedPacketQueue)
			{
				while (receivedPacketQueue.Count > 0)
				{
					PacketBase packetBase = receivedPacketQueue.Dequeue();
					packetBase.GetType();
					bool flag = false;
					foreach (PacketMatchPattern packetMatchPattern in packetMatchPatterns)
					{
						if (packetMatchPattern.CheckForMatch(packetBase))
						{
							flag = true;
							break;
						}
					}
					if (!flag)
					{
						RemoveHybridModeQueuedPacketFromAPIQueue(packetBase, notifyObservers: false);
						continue;
					}
					if (receivedPacketQueue.Count == 0)
					{
						receivedPacketEvent.Reset();
					}
					RemoveHybridModeQueuedPacketFromAPIQueue(packetBase, notifyObservers: true);
					return packetBase;
				}
				receivedPacketEvent.Reset();
			}
			num -= (int)stopWatch.GetElapsedTime().TotalMilliseconds;
			stopWatch.Reset();
		}
		while (num >= 0);
		throw new TimeoutException("Timeout waiting for one of a list of packet patterns: " + Conversions.GenericArrayToString<PacketMatchPattern>(packetMatchPatterns));
	}

	public void NotifyObserver(PacketBase packet)
	{
		if (skipNextPacket)
		{
			skipNextPacket = false;
			return;
		}
		lock (receivedPacketQueue)
		{
			receivedPacketQueue.Enqueue(packet);
			receivedPacketEvent.Set();
		}
	}

	public int GetReceiveQueueCount()
	{
		lock (receivedPacketQueue)
		{
			return receivedPacketQueue.Count;
		}
	}

	private void RemoveHybridModeQueuedPacketFromAPIQueue(PacketBase packet, bool notifyObservers)
	{
		if ((packet.Flags & (true ? 1u : 0u)) != 0)
		{
			if (notifyObservers)
			{
				skipNextPacket = true;
			}
			PacketBase packetBase = api.Receive(0, !notifyObservers);
			if (packetBase.GetType() != packet.GetType())
			{
				throw new Exception($"Packet type mismatch: {packetBase.GetType()} / {packet.GetType()}");
			}
		}
	}
}
