using System;
using System.Collections.Generic;
using System.Net.Sockets;
using System.Threading;
using Leb128;
using Server.Connectings;
using Server.Connectings.Events;

namespace Server.Helper.Sock5;

public class Client
{
	public delegate void RHandler(Client client);

	public delegate void BHandler(Client client, long Sent, long Recive);

	public const int BufferSize = 12000;

	public const int TimeOut = 5000;

	private byte[] Buffer;

	private Socket socket;

	public Server server;

	public Clients ClientReverse;

	private Timer TimeOutTimer;

	private DateTime lastPing;

	private bool Auth;

	public object Tag;

	public bool ItsConnect { get; private set; }

	public int ConnectionId => socket.Handle.ToInt32();

	public string IPAddress { get; private set; }

	public string Port { get; private set; }

	public long Sents { get; private set; }

	public long Recives { get; private set; }

	public event BHandler ResponceEvent;

	public event RHandler DisconnectEvent;

	public event RHandler ConnectEvent;

	public Client(Socket socket, Server server)
	{
		this.socket = socket;
		this.server = server;
		ItsConnect = true;
		Auth = false;
		Sents = 0L;
		Recives = 0L;
		Last();
		TimeOutTimer = new Timer(Check, null, 1, 5000);
		server.ClientReverse[Randomizer.random.Next(server.ClientReverse.Count)].Send(new object[3] { "Accept", ConnectionId, server.port });
	}

	public void Accept(Clients ClientReverse)
	{
		this.ClientReverse = ClientReverse;
		this.ClientReverse.eventDisconnect += Disconnect;
		Buffer = new byte[12000];
		socket.BeginReceive(Buffer, 0, 12000, SocketFlags.None, ReadAsyncConnect, null);
	}

	public void Disconnect(object sender, EventDisconnect eventDisconnect)
	{
		if (ItsConnect)
		{
			ItsConnect = false;
			TimeOutTimer?.Dispose();
			socket?.Dispose();
			lock (server.clients)
			{
				server.clients.Remove(this);
			}
			if (this.DisconnectEvent != null)
			{
				this.DisconnectEvent(this);
			}
		}
	}

	public void Disconnect()
	{
		if (ItsConnect)
		{
			ItsConnect = false;
			TimeOutTimer?.Dispose();
			socket?.Dispose();
			ClientReverse?.Disconnect();
			lock (server.clients)
			{
				server.clients.Remove(this);
			}
			if (this.DisconnectEvent != null)
			{
				this.DisconnectEvent(this);
			}
		}
	}

	public void Send(byte[] Data)
	{
		if (ItsConnect)
		{
			Last();
			socket.Send(Data);
			Recives += Data.LongLength;
			if (this.ResponceEvent != null)
			{
				this.ResponceEvent(this, 0L, Data.LongLength);
			}
		}
	}

	public void ReadAsyncConnect(IAsyncResult ar)
	{
		if (!ItsConnect || !ClientReverse.itsConnect)
		{
			return;
		}
		try
		{
			int num = socket.EndReceive(ar);
			if (num > 0)
			{
				byte[] array = new byte[num];
				Array.Copy(Buffer, array, num);
				Last();
				if (array[0] == 5)
				{
					if (Auth)
					{
						if (array.Length >= 6)
						{
							if (array[1] == 1)
							{
								if (array[3] == 4)
								{
									Send(new byte[2] { 5, 7 });
									Disconnect();
									return;
								}
								Socks5Request socks5Request = Socks5Request.Parse(array);
								byte[] array2 = LEB128.Write(new object[3]
								{
									"Connect",
									socks5Request.DestinationAddress,
									(int)socks5Request.DestinationPort
								});
								IPAddress = socks5Request.DestinationAddress;
								Port = socks5Request.DestinationPort.ToString();
								ClientReverse.Send(array2);
								Sents += array2.LongLength;
								if (this.ResponceEvent != null)
								{
									this.ResponceEvent(this, array2.LongLength, 0L);
								}
							}
							else
							{
								Send(new byte[2] { 5, 7 });
								Disconnect();
							}
						}
						else
						{
							socket.BeginReceive(Buffer, 0, 12000, SocketFlags.None, ReadAsyncConnect, null);
						}
					}
					else if (Auth)
					{
						Send(new byte[2] { 5, 255 });
						Disconnect();
					}
					else
					{
						Auth = true;
						Send(new byte[2] { 5, 0 });
						socket.BeginReceive(Buffer, 0, 12000, SocketFlags.None, ReadAsyncConnect, null);
					}
				}
				else
				{
					Send(new byte[2] { 5, 7 });
					Disconnect();
				}
			}
			else
			{
				Disconnect();
			}
		}
		catch
		{
			Disconnect();
		}
	}

	public void ReadAsyncData(IAsyncResult ar)
	{
		if (!ItsConnect || !ClientReverse.itsConnect)
		{
			return;
		}
		try
		{
			int num = socket.EndReceive(ar);
			if (num > 0)
			{
				byte[] array = new byte[num];
				Array.Copy(Buffer, array, num);
				Last();
				ClientReverse.Send(new object[2] { "Data", array });
				Sents += array.LongLength;
				if (this.ResponceEvent != null)
				{
					this.ResponceEvent(this, array.LongLength, 0L);
				}
				socket.BeginReceive(Buffer, 0, 12000, SocketFlags.None, ReadAsyncData, null);
			}
			else
			{
				Disconnect();
			}
		}
		catch
		{
			Disconnect();
		}
	}

	public void HandleCommandResponse(object[] response)
	{
		if (!ItsConnect || !ClientReverse.itsConnect)
		{
			return;
		}
		byte b = 2;
		if (Convert.ToBoolean(response[b]))
		{
			try
			{
				List<byte> list = new List<byte>();
				list.Add(5);
				list.Add(0);
				list.Add(0);
				list.Add(1);
				list.AddRange((byte[])response[b + 1]);
				list.Add((byte)Math.Floor((decimal)(int)response[b + 2] / 256m));
				list.Add((byte)((int)response[b + 2] % 256));
				Send(list.ToArray());
				Last();
				socket.BeginReceive(Buffer, 0, 12000, SocketFlags.None, ReadAsyncData, null);
				if (this.ConnectEvent != null)
				{
					this.ConnectEvent(this);
				}
				return;
			}
			catch (Exception)
			{
				Send(new byte[2] { 5, 1 });
				Disconnect();
				return;
			}
		}
		Send(new byte[2] { 5, 4 });
		Disconnect();
	}

	private double DiffSeconds(DateTime startTime, DateTime endTime)
	{
		return Math.Abs(new TimeSpan(endTime.Ticks - startTime.Ticks).TotalSeconds);
	}

	private void Check(object obj)
	{
		if (DiffSeconds(lastPing, DateTime.Now) > 30.0)
		{
			Disconnect();
		}
	}

	public void Last()
	{
		lastPing = DateTime.Now;
	}
}
