using System;
using System.Net;
using System.Net.Sockets;
using System.Threading;

namespace QCRPG.Framework;

public class AsyncClient
{
	public delegate void LogHandler(byte[] message);

	private const int ReadBufferSize = 65536;

	private byte[] buffer = new byte[65536];

	private Socket client;

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

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

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

	public bool initializationComplete;

	public event LogHandler Log;

	private void OnLog(byte[] message)
	{
		if (this.Log != null)
		{
			this.Log(message);
		}
	}

	private void ConnectCallback(IAsyncResult ar)
	{
		client = (Socket)ar.AsyncState;
		client.EndConnect(ar);
		connectDone.Set();
	}

	private void EnableReceive()
	{
		client.BeginReceive(buffer, 0, 65536, SocketFlags.None, ReceiveCallback, buffer);
	}

	private void ReceiveCallback(IAsyncResult ar)
	{
		buffer = (byte[])ar.AsyncState;
		if (client == null || !client.Connected)
		{
			return;
		}
		int num = client.EndReceive(ar);
		if (num > 0)
		{
			byte[] array = new byte[num];
			Array.Copy(buffer, array, num);
			if (initializationComplete)
			{
				OnLog(array);
			}
			client.BeginReceive(buffer, 0, 65536, SocketFlags.None, ReceiveCallback, buffer);
		}
	}

	private void Send(Socket client, byte[] byteData)
	{
		client.BeginSend(byteData, 0, byteData.Length, SocketFlags.None, SendCallback, client);
	}

	private void SendCallback(IAsyncResult ar)
	{
		Socket socket = (Socket)ar.AsyncState;
		if (socket != null && socket.Connected)
		{
			socket.EndSend(ar);
			sendDone.Set();
		}
	}

	private void InitializeWillAndDos()
	{
		byte[] data = new byte[3] { 255, 253, 3 };
		Send(data);
		data = new byte[3] { 255, 251, 3 };
		Send(data);
		data = new byte[3] { 255, 253, 0 };
		Send(data);
		data = new byte[3] { 255, 251, 0 };
		Send(data);
		data = new byte[3] { 255, 251, 1 };
		Send(data);
	}

	public void StartClient(string ipAddr, int portNumber, bool initializeWillsAndDos)
	{
		IPAddress address = IPAddress.Parse(ipAddr);
		IPEndPoint remoteEP = new IPEndPoint(address, portNumber);
		Socket socket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
		if (socket != null)
		{
			socket.BeginConnect(remoteEP, ConnectCallback, socket);
			connectDone.WaitOne();
			EnableReceive();
			if (initializeWillsAndDos)
			{
				InitializeWillAndDos();
			}
		}
	}

	public void Send(byte[] data)
	{
		Send(client, data);
		sendDone.WaitOne();
	}

	public void DisconnectClient()
	{
		connectDone.Reset();
		receiveDone.Reset();
		sendDone.Reset();
		client.Shutdown(SocketShutdown.Both);
		client.Close();
		client = null;
	}
}
