using System;
using System.Collections.Generic;
using System.Runtime.InteropServices;
using System.Text;

namespace SharpQuake
{
	internal static class net
	{
		public static INetDriver[] Drivers
		{
			get
			{
				return net._Drivers;
			}
		}

		public static INetLanDriver[] LanDrivers
		{
			get
			{
				return net._LanDrivers;
			}
		}

		public static IEnumerable<qsocket_t> ActiveSockets
		{
			get
			{
				return net._ActiveSockets;
			}
		}

		public static IEnumerable<qsocket_t> FreeSockets
		{
			get
			{
				return net._ActiveSockets;
			}
		}

		public static int MessagesSent
		{
			get
			{
				return net._MessagesSent;
			}
		}

		public static int MessagesReceived
		{
			get
			{
				return net._MessagesReceived;
			}
		}

		public static int UnreliableMessagesSent
		{
			get
			{
				return net._UnreliableMessagesSent;
			}
		}

		public static int UnreliableMessagesReceived
		{
			get
			{
				return net._UnreliableMessagesReceived;
			}
		}

		public static string HostName
		{
			get
			{
				return net._HostName.String;
			}
		}

		public static string MyTcpIpAddress
		{
			get
			{
				return net._MyTcpIpAddress;
			}
			set
			{
				net._MyTcpIpAddress = value;
			}
		}

		public static int DefaultHostPort
		{
			get
			{
				return net._DefHostPort;
			}
		}

		public static bool TcpIpAvailable
		{
			get
			{
				return net_tcp_ip.Instance.IsInitialized;
			}
		}

		public static hostcache_t[] HostCache
		{
			get
			{
				return net._HostCache;
			}
		}

		public static int DriverLevel
		{
			get
			{
				return net._DriverLevel;
			}
		}

		public static INetLanDriver LanDriver
		{
			get
			{
				return net._LanDrivers[net.LanDriverLevel];
			}
		}

		public static INetDriver Driver
		{
			get
			{
				return net._Drivers[net._DriverLevel];
			}
		}

		public static bool SlistInProgress
		{
			get
			{
				return net._SlistInProgress;
			}
		}

		public static double Time
		{
			get
			{
				return net._Time;
			}
		}

		public static void Init()
		{
			for (int i = 0; i < net._HostCache.Length; i++)
			{
				net._HostCache[i] = new hostcache_t();
			}
			if (net._Drivers == null)
			{
				if (common.HasParam("-playback"))
				{
					net._Drivers = new INetDriver[]
					{
						new net_vcr()
					};
				}
				else
				{
					net._Drivers = new INetDriver[]
					{
						new net_loop(),
						net_datagram.Instance
					};
				}
			}
			if (net._LanDrivers == null)
			{
				net._LanDrivers = new INetLanDriver[]
				{
					net_tcp_ip.Instance
				};
			}
			if (common.HasParam("-record"))
			{
				net._IsRecording = true;
			}
			int j = common.CheckParm("-port");
			if (j == 0)
			{
				j = common.CheckParm("-udpport");
			}
			if (j == 0)
			{
				j = common.CheckParm("-ipxport");
			}
			if (j > 0)
			{
				if (j < common.Argc - 1)
				{
					net._DefHostPort = common.atoi(common.Argv(j + 1));
				}
				else
				{
					sys.Error("Net.Init: you must specify a number after -port!", Array.Empty<object>());
				}
			}
			net.HostPort = net._DefHostPort;
			if (common.HasParam("-listen") || client.cls.state == cactive_t.ca_dedicated)
			{
				net._IsListening = true;
			}
			int num = server.svs.maxclientslimit;
			if (client.cls.state != cactive_t.ca_dedicated)
			{
				num++;
			}
			net._FreeSockets = new List<qsocket_t>(num);
			net._ActiveSockets = new List<qsocket_t>(num);
			for (j = 0; j < num; j++)
			{
				net._FreeSockets.Add(new qsocket_t());
			}
			net.SetNetTime();
			net.Message = new MsgWriter(8192);
			net.Reader = new MsgReader(net.Message);
			if (net._MessageTimeout == null)
			{
				net._MessageTimeout = new cvar("net_messagetimeout", "300");
				net._HostName = new cvar("hostname", "UNNAMED");
			}
			cmd.Add("slist", new xcommand_t(net.Slist_f));
			cmd.Add("listen", new xcommand_t(net.Listen_f));
			cmd.Add("maxplayers", new xcommand_t(net.MaxPlayers_f));
			cmd.Add("port", new xcommand_t(net.Port_f));
			net._DriverLevel = 0;
			foreach (INetDriver netDriver in net._Drivers)
			{
				netDriver.Init();
				if (netDriver.IsInitialized && net._IsListening)
				{
					netDriver.Listen(true);
				}
				net._DriverLevel++;
			}
			if (!string.IsNullOrEmpty(net._MyTcpIpAddress))
			{
				Con.DPrint("TCP/IP address {0}\n", new object[]
				{
					net._MyTcpIpAddress
				});
			}
		}

		public static void Shutdown()
		{
			net.SetNetTime();
			if (net._ActiveSockets != null)
			{
				qsocket_t[] array = net._ActiveSockets.ToArray();
				for (int i = 0; i < array.Length; i++)
				{
					net.Close(array[i]);
				}
			}
			if (net._Drivers != null)
			{
				net._DriverLevel = 0;
				while (net._DriverLevel < net._Drivers.Length)
				{
					if (net._Drivers[net._DriverLevel].IsInitialized)
					{
						net._Drivers[net._DriverLevel].Shutdown();
					}
					net._DriverLevel++;
				}
			}
		}

		public static qsocket_t CheckNewConnections()
		{
			net.SetNetTime();
			net._DriverLevel = 0;
			while (net._DriverLevel < net._Drivers.Length)
			{
				if (net._Drivers[net._DriverLevel].IsInitialized && (net._DriverLevel <= 0 || net._IsListening))
				{
					qsocket_t qsocket_t = net.Driver.CheckNewConnections();
					if (qsocket_t != null)
					{
						if (net._IsRecording)
						{
							net._VcrConnect.time = host.Time;
							net._VcrConnect.op = 1;
							net._VcrConnect.session = 1L;
							byte[] array = sys.StructureToBytes<VcrRecord>(ref net._VcrConnect);
							host.VcrWriter.Write(array, 0, array.Length);
							array = Encoding.ASCII.GetBytes(qsocket_t.address);
							int num = Math.Min(array.Length, 64);
							int num2 = 64 - num;
							host.VcrWriter.Write(array, 0, num);
							for (int i = 0; i < num2; i++)
							{
								host.VcrWriter.Write(0);
							}
						}
						return qsocket_t;
					}
				}
				net._DriverLevel++;
			}
			if (net._IsRecording)
			{
				net._VcrConnect.time = host.Time;
				net._VcrConnect.op = 1;
				net._VcrConnect.session = 0L;
				byte[] array2 = sys.StructureToBytes<VcrRecord>(ref net._VcrConnect);
				host.VcrWriter.Write(array2, 0, array2.Length);
			}
			return null;
		}

		public static qsocket_t Connect(string host)
		{
			int num = net._Drivers.Length;
			net.SetNetTime();
			if (string.IsNullOrEmpty(host))
			{
				host = null;
			}
			if (host != null)
			{
				if (common.SameText(host, "local"))
				{
					goto IL_103;
				}
				if (net.HostCacheCount > 0)
				{
					foreach (hostcache_t hostcache_t in net._HostCache)
					{
						if (common.SameText(hostcache_t.name, host))
						{
							host = hostcache_t.cname;
							goto IL_103;
						}
					}
				}
			}
			net.SlistSilent = (host != null);
			net.Slist_f();
			while (net._SlistInProgress)
			{
				net.Poll();
			}
			if (host == null)
			{
				if (net.HostCacheCount != 1)
				{
					return null;
				}
				host = net._HostCache[0].cname;
				Con.Print("Connecting to...\n{0} @ {1}\n\n", new object[]
				{
					net._HostCache[0].name,
					host
				});
			}
			net._DriverLevel = 0;
			foreach (hostcache_t hostcache_t2 in net._HostCache)
			{
				if (common.SameText(host, hostcache_t2.name))
				{
					host = hostcache_t2.cname;
					break;
				}
				net._DriverLevel++;
			}
			IL_103:
			net._DriverLevel = 0;
			foreach (INetDriver netDriver in net._Drivers)
			{
				if (netDriver.IsInitialized)
				{
					qsocket_t qsocket_t = netDriver.Connect(host);
					if (qsocket_t != null)
					{
						return qsocket_t;
					}
					net._DriverLevel++;
				}
			}
			if (host != null)
			{
				Con.Print("\n", Array.Empty<object>());
				net.PrintSlistHeader();
				net.PrintSlist();
				net.PrintSlistTrailer();
			}
			return null;
		}

		public static bool CanSendMessage(qsocket_t sock)
		{
			if (sock == null)
			{
				return false;
			}
			if (sock.disconnected)
			{
				return false;
			}
			net.SetNetTime();
			bool flag = net._Drivers[sock.driver].CanSendMessage(sock);
			if (net._IsRecording)
			{
				net._VcrSendMessage.time = host.Time;
				net._VcrSendMessage.op = 4;
				net._VcrSendMessage.session = 1L;
				net._VcrSendMessage.ret = (flag ? 1 : 0);
				byte[] array = sys.StructureToBytes<net.VcrRecord2>(ref net._VcrSendMessage);
				host.VcrWriter.Write(array, 0, array.Length);
			}
			return flag;
		}

		public static int GetMessage(qsocket_t sock)
		{
			if (sock == null)
			{
				return -1;
			}
			if (sock.disconnected)
			{
				Con.Print("NET_GetMessage: disconnected socket\n", Array.Empty<object>());
				return -1;
			}
			net.SetNetTime();
			int message = net._Drivers[sock.driver].GetMessage(sock);
			if (message == 0 && sock.driver != 0 && net._Time - sock.lastMessageTime > (double)net._MessageTimeout.Value)
			{
				net.Close(sock);
				return -1;
			}
			if (message > 0)
			{
				if (sock.driver != 0)
				{
					sock.lastMessageTime = net._Time;
					if (message == 1)
					{
						net._MessagesReceived++;
					}
					else if (message == 2)
					{
						net._UnreliableMessagesReceived++;
					}
				}
				if (net._IsRecording)
				{
					net._VcrGetMessage.time = host.Time;
					net._VcrGetMessage.op = 2;
					net._VcrGetMessage.session = 1L;
					net._VcrGetMessage.ret = message;
					byte[] array = sys.StructureToBytes<net.VcrRecord2>(ref net._VcrGetMessage);
					host.VcrWriter.Write(array, 0, array.Length);
					host.VcrWriter.Write(net.Message.Length);
					host.VcrWriter.Write(net.Message.Data, 0, net.Message.Length);
				}
			}
			else if (net._IsRecording)
			{
				net._VcrGetMessage.time = host.Time;
				net._VcrGetMessage.op = 2;
				net._VcrGetMessage.session = 1L;
				net._VcrGetMessage.ret = message;
				byte[] array2 = sys.StructureToBytes<net.VcrRecord2>(ref net._VcrGetMessage);
				host.VcrWriter.Write(array2, 0, array2.Length);
			}
			return message;
		}

		public static int SendMessage(qsocket_t sock, MsgWriter data)
		{
			if (sock == null)
			{
				return -1;
			}
			if (sock.disconnected)
			{
				Con.Print("NET_SendMessage: disconnected socket\n", Array.Empty<object>());
				return -1;
			}
			net.SetNetTime();
			int num = net._Drivers[sock.driver].SendMessage(sock, data);
			if (num == 1 && sock.driver != 0)
			{
				net._MessagesSent++;
			}
			if (net._IsRecording)
			{
				net._VcrSendMessage.time = host.Time;
				net._VcrSendMessage.op = 3;
				net._VcrSendMessage.session = 1L;
				net._VcrSendMessage.ret = num;
				byte[] array = sys.StructureToBytes<net.VcrRecord2>(ref net._VcrSendMessage);
				host.VcrWriter.Write(array, 0, array.Length);
			}
			return num;
		}

		public static int SendUnreliableMessage(qsocket_t sock, MsgWriter data)
		{
			if (sock == null)
			{
				return -1;
			}
			if (sock.disconnected)
			{
				Con.Print("NET_SendMessage: disconnected socket\n", Array.Empty<object>());
				return -1;
			}
			net.SetNetTime();
			int num = net._Drivers[sock.driver].SendUnreliableMessage(sock, data);
			if (num == 1 && sock.driver != 0)
			{
				net._UnreliableMessagesSent++;
			}
			if (net._IsRecording)
			{
				net._VcrSendMessage.time = host.Time;
				net._VcrSendMessage.op = 3;
				net._VcrSendMessage.session = 1L;
				net._VcrSendMessage.ret = num;
				byte[] buffer = sys.StructureToBytes<net.VcrRecord2>(ref net._VcrSendMessage);
				host.VcrWriter.Write(buffer);
			}
			return num;
		}

		public static int SendToAll(MsgWriter data, int blocktime)
		{
			bool[] array = new bool[16];
			bool[] array2 = new bool[16];
			int i = 0;
			for (int j = 0; j < server.svs.maxclients; j++)
			{
				host.HostClient = server.svs.clients[j];
				if (host.HostClient.netconnection != null)
				{
					if (host.HostClient.active)
					{
						if (host.HostClient.netconnection.driver == 0)
						{
							net.SendMessage(host.HostClient.netconnection, data);
							array[j] = true;
							array2[j] = true;
						}
						else
						{
							i++;
							array[j] = false;
							array2[j] = false;
						}
					}
					else
					{
						array[j] = true;
						array2[j] = true;
					}
				}
			}
			double floatTime = sys.GetFloatTime();
			while (i > 0)
			{
				i = 0;
				for (int k = 0; k < server.svs.maxclients; k++)
				{
					host.HostClient = server.svs.clients[k];
					if (!array[k])
					{
						if (net.CanSendMessage(host.HostClient.netconnection))
						{
							array[k] = true;
							net.SendMessage(host.HostClient.netconnection, data);
						}
						else
						{
							net.GetMessage(host.HostClient.netconnection);
						}
						i++;
					}
					else if (!array2[k])
					{
						if (net.CanSendMessage(host.HostClient.netconnection))
						{
							array2[k] = true;
						}
						else
						{
							net.GetMessage(host.HostClient.netconnection);
						}
						i++;
					}
				}
				if (sys.GetFloatTime() - floatTime > (double)blocktime)
				{
					break;
				}
			}
			return i;
		}

		public static void Close(qsocket_t sock)
		{
			if (sock == null)
			{
				return;
			}
			if (sock.disconnected)
			{
				return;
			}
			net.SetNetTime();
			net._Drivers[sock.driver].Close(sock);
			net.FreeSocket(sock);
		}

		public static void FreeSocket(qsocket_t sock)
		{
			if (!net._ActiveSockets.Remove(sock))
			{
				sys.Error("NET_FreeQSocket: not active\n", Array.Empty<object>());
			}
			net._FreeSockets.Add(sock);
			sock.disconnected = true;
		}

		public static void Poll()
		{
			net.SetNetTime();
			PollProcedure pollProcedure = net._PollProcedureList;
			while (pollProcedure != null && pollProcedure.nextTime <= net._Time)
			{
				net._PollProcedureList = pollProcedure.next;
				pollProcedure.procedure(pollProcedure.arg);
				pollProcedure = pollProcedure.next;
			}
		}

		public static double SetNetTime()
		{
			net._Time = sys.GetFloatTime();
			return net._Time;
		}

		public static void Slist_f()
		{
			if (net._SlistInProgress)
			{
				return;
			}
			if (!net.SlistSilent)
			{
				Con.Print("Looking for Quake servers...\n", Array.Empty<object>());
				net.PrintSlistHeader();
			}
			net._SlistInProgress = true;
			net._SlistStartTime = sys.GetFloatTime();
			net.SchedulePollProcedure(net._SlistSendProcedure, 0.0);
			net.SchedulePollProcedure(net._SlistPollProcedure, 0.1);
			net.HostCacheCount = 0;
		}

		public static qsocket_t NewSocket()
		{
			if (net._FreeSockets.Count == 0)
			{
				return null;
			}
			if (net.ActiveConnections >= server.svs.maxclients)
			{
				return null;
			}
			int index = net._FreeSockets.Count - 1;
			qsocket_t qsocket_t = net._FreeSockets[index];
			net._FreeSockets.RemoveAt(index);
			net._ActiveSockets.Add(qsocket_t);
			qsocket_t.disconnected = false;
			qsocket_t.connecttime = net._Time;
			qsocket_t.address = "UNSET ADDRESS";
			qsocket_t.driver = net._DriverLevel;
			qsocket_t.socket = null;
			qsocket_t.driverdata = null;
			qsocket_t.canSend = true;
			qsocket_t.sendNext = false;
			qsocket_t.lastMessageTime = net._Time;
			qsocket_t.ackSequence = 0u;
			qsocket_t.sendSequence = 0u;
			qsocket_t.unreliableSendSequence = 0u;
			qsocket_t.sendMessageLength = 0;
			qsocket_t.receiveSequence = 0u;
			qsocket_t.unreliableReceiveSequence = 0u;
			qsocket_t.receiveMessageLength = 0;
			return qsocket_t;
		}

		private static void PrintSlistHeader()
		{
			Con.Print("Server          Map             Users\n", Array.Empty<object>());
			Con.Print("--------------- --------------- -----\n", Array.Empty<object>());
			net._SlistLastShown = 0;
		}

		private static void PrintSlist()
		{
			int i;
			for (i = net._SlistLastShown; i < net.HostCacheCount; i++)
			{
				hostcache_t hostcache_t = net._HostCache[i];
				if (hostcache_t.maxusers != 0)
				{
					Con.Print("{0,-15} {1,-15}\n {2,2}/{3,2}\n", new object[]
					{
						common.Copy(hostcache_t.name, 15),
						common.Copy(hostcache_t.map, 15),
						hostcache_t.users,
						hostcache_t.maxusers
					});
				}
				else
				{
					Con.Print("{0,-15} {1,-15}\n", new object[]
					{
						common.Copy(hostcache_t.name, 15),
						common.Copy(hostcache_t.map, 15)
					});
				}
			}
			net._SlistLastShown = i;
		}

		private static void PrintSlistTrailer()
		{
			if (net.HostCacheCount != 0)
			{
				Con.Print("== end list ==\n\n", Array.Empty<object>());
				return;
			}
			Con.Print("No Quake servers found.\n\n", Array.Empty<object>());
		}

		private static void SchedulePollProcedure(PollProcedure proc, double timeOffset)
		{
			proc.nextTime = sys.GetFloatTime() + timeOffset;
			PollProcedure pollProcedure = net._PollProcedureList;
			PollProcedure pollProcedure2 = null;
			while (pollProcedure != null && pollProcedure.nextTime < proc.nextTime)
			{
				pollProcedure2 = pollProcedure;
				pollProcedure = pollProcedure.next;
			}
			if (pollProcedure2 == null)
			{
				proc.next = net._PollProcedureList;
				net._PollProcedureList = proc;
				return;
			}
			proc.next = pollProcedure;
			pollProcedure2.next = proc;
		}

		private static void Listen_f()
		{
			if (cmd.Argc != 2)
			{
				Con.Print("\"listen\" is \"{0}\"\n", new object[]
				{
					net._IsListening ? 1 : 0
				});
				return;
			}
			net._IsListening = (common.atoi(cmd.Argv(1)) != 0);
			foreach (INetDriver netDriver in net._Drivers)
			{
				if (netDriver.IsInitialized)
				{
					netDriver.Listen(net._IsListening);
				}
			}
		}

		private static void MaxPlayers_f()
		{
			if (cmd.Argc != 2)
			{
				Con.Print("\"maxplayers\" is \"%u\"\n", new object[]
				{
					server.svs.maxclients
				});
				return;
			}
			if (server.sv.active)
			{
				Con.Print("maxplayers can not be changed while a server is running.\n", Array.Empty<object>());
				return;
			}
			int num = common.atoi(cmd.Argv(1));
			if (num < 1)
			{
				num = 1;
			}
			if (num > server.svs.maxclientslimit)
			{
				num = server.svs.maxclientslimit;
				Con.Print("\"maxplayers\" set to \"{0}\"\n", new object[]
				{
					num
				});
			}
			if (num == 1 && net._IsListening)
			{
				Cbuf.AddText("listen 0\n");
			}
			if (num > 1 && !net._IsListening)
			{
				Cbuf.AddText("listen 1\n");
			}
			server.svs.maxclients = num;
			if (num == 1)
			{
				cvar.Set("deathmatch", "0");
				return;
			}
			cvar.Set("deathmatch", "1");
		}

		private static void Port_f()
		{
			if (cmd.Argc != 2)
			{
				Con.Print("\"port\" is \"{0}\"\n", new object[]
				{
					net.HostPort
				});
				return;
			}
			int num = common.atoi(cmd.Argv(1));
			if (num < 1 || num > 65534)
			{
				Con.Print("Bad value, must be between 1 and 65534\n", Array.Empty<object>());
				return;
			}
			net._DefHostPort = num;
			net.HostPort = num;
			if (net._IsListening)
			{
				Cbuf.AddText("listen 0\n");
				Cbuf.AddText("listen 1\n");
			}
		}

		private static void SlistSend(object arg)
		{
			net._DriverLevel = 0;
			while (net._DriverLevel < net._Drivers.Length)
			{
				if ((net.SlistLocal || net._DriverLevel != 0) && net._Drivers[net._DriverLevel].IsInitialized)
				{
					net._Drivers[net._DriverLevel].SearchForHosts(true);
				}
				net._DriverLevel++;
			}
			if (sys.GetFloatTime() - net._SlistStartTime < 0.5)
			{
				net.SchedulePollProcedure(net._SlistSendProcedure, 0.75);
			}
		}

		private static void SlistPoll(object arg)
		{
			net._DriverLevel = 0;
			while (net._DriverLevel < net._Drivers.Length)
			{
				if ((net.SlistLocal || net._DriverLevel != 0) && net._Drivers[net._DriverLevel].IsInitialized)
				{
					net._Drivers[net._DriverLevel].SearchForHosts(false);
				}
				net._DriverLevel++;
			}
			if (!net.SlistSilent)
			{
				net.PrintSlist();
			}
			if (sys.GetFloatTime() - net._SlistStartTime < 1.5)
			{
				net.SchedulePollProcedure(net._SlistPollProcedure, 0.1);
				return;
			}
			if (!net.SlistSilent)
			{
				net.PrintSlistTrailer();
			}
			net._SlistInProgress = false;
			net.SlistSilent = false;
			net.SlistLocal = true;
		}

		public const int NET_PROTOCOL_VERSION = 3;

		public const int HOSTCACHESIZE = 8;

		public const int NET_NAMELEN = 64;

		public const int NET_MAXMESSAGE = 8192;

		public const int NET_HEADERSIZE = 8;

		public const int NET_DATAGRAMSIZE = 1032;

		public static int HostPort;

		public static int ActiveConnections;

		public static MsgWriter Message;

		public static MsgReader Reader;

		public static int HostCacheCount;

		public static bool SlistSilent;

		public static bool SlistLocal = true;

		public static int LanDriverLevel;

		private static readonly PollProcedure _SlistSendProcedure = new PollProcedure(null, 0.0, new PollHandler(net.SlistSend), null);

		private static readonly PollProcedure _SlistPollProcedure = new PollProcedure(null, 0.0, new PollHandler(net.SlistPoll), null);

		private static INetDriver[] _Drivers;

		private static INetLanDriver[] _LanDrivers;

		private static bool _IsRecording;

		private static int _DefHostPort = 26000;

		private static bool _IsListening;

		private static List<qsocket_t> _FreeSockets;

		private static List<qsocket_t> _ActiveSockets;

		private static double _Time;

		private static string _MyTcpIpAddress;

		private static int _MessagesSent = 0;

		private static int _MessagesReceived = 0;

		private static int _UnreliableMessagesSent = 0;

		private static int _UnreliableMessagesReceived = 0;

		private static cvar _MessageTimeout;

		private static cvar _HostName;

		private static PollProcedure _PollProcedureList;

		private static hostcache_t[] _HostCache = new hostcache_t[8];

		private static bool _SlistInProgress;

		private static int _SlistLastShown;

		private static double _SlistStartTime;

		private static int _DriverLevel;

		private static VcrRecord _VcrConnect = new VcrRecord();

		private static net.VcrRecord2 _VcrGetMessage = new net.VcrRecord2();

		private static net.VcrRecord2 _VcrSendMessage = new net.VcrRecord2();

		[StructLayout(LayoutKind.Sequential, Pack = 1)]
		private class VcrRecord2 : VcrRecord
		{
			public int ret;
		}
	}
}
