using System;
using System.Collections.Generic;
using System.Linq;
using System.Net;
using System.Threading;
using Hazel;
using Hazel.Udp;
using UnityEngine;

namespace InnerNet
{
	public class InnerNetServer : DestroyableSingleton<InnerNetServer>
	{
        // 单局最大玩家数
		public const int MaxPlayers = 10; 

		public bool Running;

        // 是本地游戏的话id是这个32
		public const int LocalGameId = 32;

		private const int InvalidHost = -1;

		private int HostId = -1;

		public HashSet<string> ipBans = new HashSet<string>();

		public int Port = 22023;

        /*
        public enum GameStates : byte
        {
            NotStarted,
            Started,
            Ended,
            Destroyed
        }
        */
        [SerializeField]
		private GameStates GameState;

		private NetworkConnectionListener listener;

		private List<InnerNetServer.Player> Clients = new List<InnerNetServer.Player>();

        // 某个玩家的连接
		protected class Player
		{
			private static int IdCount = 1;

			public int Id;

			public Connection Connection;

			public LimboStates LimboState;

			public Player(Connection connection)
			{
				this.Id = Interlocked.Increment(ref InnerNetServer.Player.IdCount);
				this.Connection = connection;
			}
		}

        // 销毁这个单例
		public override void OnDestroy()
		{
			this.StopServer();
			base.OnDestroy();
		}

        // 开启本地服务器,监听端口 
        // 在HostGameButton.CoStartGame中调用
		public void StartAsServer()
		{
			if (this.listener != null)
			{
				this.StopServer();
			}

			this.GameState = GameStates.NotStarted;

			this.listener = new UdpConnectionListener(new IPEndPoint(IPAddress.Any, this.Port), IPMode.IPv4, null);
			this.listener.NewConnection += this.OnServerConnect;
			this.listener.Start();

			this.Running = true;
		}

        // 停止服务器
		public void StopServer()
		{
			this.HostId = -1;
			this.Running = false;
			this.GameState = GameStates.Destroyed;
			if (this.listener != null)
			{
				this.listener.Close();
				this.listener.Dispose();
				this.listener = null;
			}
			List<InnerNetServer.Player> clients = this.Clients;
			lock (clients)
			{
				this.Clients.Clear();
			}
		}

		public static bool IsCompatibleVersion(int version)
		{
			return Constants.CompatVersions.Contains(version);
		}

        // 收到某个玩家的连接
		private void OnServerConnect(NewConnectionEventArgs evt)
		{
			MessageReader handshakeData = evt.HandshakeData;
			try
			{
				if (evt.HandshakeData.Length < 5)
				{
					InnerNetServer.SendIncorrectVersion(evt.Connection);
					return;
				}
				if (!InnerNetServer.IsCompatibleVersion(handshakeData.ReadInt32()))
				{
					InnerNetServer.SendIncorrectVersion(evt.Connection);
					return;
				}
			}
			finally
			{
				handshakeData.Recycle();
			}
			InnerNetServer.Player client = new InnerNetServer.Player(evt.Connection);

			Debug.Log(string.Format("Client {0} added: {1}", client.Id, evt.Connection.EndPoint));

			UdpConnection udpConnection = (UdpConnection)evt.Connection;
			udpConnection.KeepAliveInterval = 1500;
			udpConnection.DisconnectTimeout = 6000;
			udpConnection.ResendPingMultiplier = 1.5f;
			udpConnection.DataReceived += delegate(DataReceivedEventArgs e)
			{
				this.OnDataReceived(client, e);
			};
			udpConnection.Disconnected += delegate(object o, DisconnectedEventArgs e)
			{
				this.ClientDisconnect(client);
			};
		}

		private static void SendIncorrectVersion(Connection connection)
		{
			MessageWriter messageWriter = MessageWriter.Get(SendOption.Reliable);
			messageWriter.StartMessage(1);
			messageWriter.Write(5);
			messageWriter.EndMessage();
			connection.Send(messageWriter);
			messageWriter.Recycle();
		}

		private void Connection_DataSentRaw(byte[] data, int length)
		{
			Debug.Log("Server Sent: " + string.Join(" ", (from b in data
			select b.ToString()).ToArray<string>(), 0, length));
		}

		private void OnDataReceived(InnerNetServer.Player client, DataReceivedEventArgs evt)
		{
			MessageReader message = evt.Message;
			if (message.Length <= 0)
			{
				Debug.Log("Server got 0 bytes");
				message.Recycle();
				return;
			}
			try
			{
				while (message.Position < message.Length)
				{
					this.HandleMessage(client, message.ReadMessage(), evt.SendOption);
				}
			}
			catch (Exception arg)
			{
				Debug.Log(string.Format("{0}\r\n{1}", string.Join<byte>(" ", message.Buffer), arg));
			}
			finally
			{
				message.Recycle();
			}
		}

        /*
        public static class Tags
        {
            public const byte HostGame = 0;

            public const byte JoinGame = 1;

            public const byte StartGame = 2;

            public const byte RemoveGame = 3;

            public const byte RemovePlayer = 4;

            public const byte GameData = 5;

            public const byte GameDataTo = 6;

            public const byte JoinedGame = 7;

            public const byte EndGame = 8;

            public const byte GetGameList = 9;

            public const byte AlterGame = 10;

            public const byte KickPlayer = 11;

            public const byte WaitForHost = 12;

            public const byte Redirect = 13;
        }
        */

        // 处理收到的消息,在网络线程执行
        // reader是数据
        private void HandleMessage(InnerNetServer.Player client, MessageReader reader, SendOption sendOption)
		{
            switch (reader.Tag)
			{
			case 0: // HostGame
			{
				Debug.Log("Server got host game");
				MessageWriter messageWriter = MessageWriter.Get(SendOption.Reliable);
				messageWriter.StartMessage(0); // tag
				messageWriter.Write(LocalGameId);  // gameId
				messageWriter.EndMessage();
				client.Connection.Send(messageWriter);
				messageWriter.Recycle();
				return;
			}
			case 1: // JoinGame
			{
				Debug.Log("Server got join game");
				if (reader.ReadInt32() == LocalGameId)
				{
					this.JoinGame(client);
					return;
				}
				MessageWriter messageWriter2 = MessageWriter.Get(SendOption.Reliable);
				messageWriter2.StartMessage(1); // tag
				messageWriter2.Write(3); // 
				messageWriter2.EndMessage();
				client.Connection.Send(messageWriter2);
				messageWriter2.Recycle();
				return;
			}
			case 2: // StartGame
				if (reader.ReadInt32() == 32)
				{
					this.StartGame(reader, client);
					return;
				}
				break;
			case 3: // RemoveGame , MsgRead:RemoveGame
				if (reader.ReadInt32() == 32)
				{
					this.ClientDisconnect(client);
					return;
				}
				break;
			case 4: // remove player 
			case 7: // joined game 
			case 9: // get game list 
			case 10: // alter game
				break;
			case 5: // Tags.GameData
				if (this.Clients.Contains(client))
				{
					if (reader.ReadInt32() == 32)
					{
						MessageWriter messageWriter3 = MessageWriter.Get(sendOption);
						messageWriter3.CopyFrom(reader);
                        // 广播这个消息
						this.Broadcast(messageWriter3, client);
						messageWriter3.Recycle();
						return;
					}
				}
				else if (this.GameState == GameStates.Started) // 没有这个连接就踢掉这个错误的连接
				{
					client.Connection.Dispose();
					return;
				}
				break;
			case 6: // GameDataTo 到指定玩家,和Tags.GameData不同,包括Rpc
				if (this.Clients.Contains(client))
				{
					if (reader.ReadInt32() == 32)
					{
						int targetId = reader.ReadPackedInt32();
						MessageWriter messageWriter4 = MessageWriter.Get(sendOption);
						messageWriter4.CopyFrom(reader);
						this.SendTo(messageWriter4, targetId);
						messageWriter4.Recycle();
						return;
					}
				}
				else if (this.GameState == GameStates.Started)
				{
					Debug.Log("GameDataTo: Server didn't have client");
					client.Connection.Dispose();
					return;
				}
				break;
			case 8: // EndGame
				if (reader.ReadInt32() == 32)
				{
					this.EndGame(reader, client);
					return;
				}
				break;
			case 11: // kick player
				if (reader.ReadInt32() == 32)
				{
					this.KickPlayer(reader.ReadPackedInt32(), reader.ReadBoolean());
				}
				break;
			default:
				return;
			}
		}

        // 踢玩家(断开它的连接)
		private void KickPlayer(int targetId, bool ban)
		{
			List<InnerNetServer.Player> clients = this.Clients;
			lock (clients)
			{
				InnerNetServer.Player player = null;
                // 找到对应的玩家的连接
				for (int i = 0; i < this.Clients.Count; i++)
				{
					if (this.Clients[i].Id == targetId)
					{
						player = this.Clients[i];
						break;
					}
				}
				if (player != null)
				{
					if (ban) // 是否ban掉这个ip
					{
						HashSet<string> obj = this.ipBans;
						lock (obj)
						{
							IPEndPoint endPoint = player.Connection.EndPoint;
							this.ipBans.Add(endPoint.Address.ToString());
						}
					}
					MessageWriter messageWriter = MessageWriter.Get(SendOption.Reliable);
					messageWriter.StartMessage(11);
					messageWriter.Write(32);
					messageWriter.WritePacked(targetId);
					messageWriter.Write(ban);
					messageWriter.EndMessage();
					this.Broadcast(messageWriter, null);
					messageWriter.Recycle();
				}
			}
		}

		protected void JoinGame(InnerNetServer.Player client)
		{
			HashSet<string> obj = this.ipBans;
			lock (obj)
			{
				IPEndPoint endPoint = client.Connection.EndPoint;
                // 如果这个ip被ban掉了,踢掉这个玩家
				if (this.ipBans.Contains(endPoint.Address.ToString()))
				{
					MessageWriter messageWriter = MessageWriter.Get(SendOption.Reliable);
					messageWriter.StartMessage(1);
					messageWriter.Write(6); // 6 = DisconnectReasons.Banned
                    messageWriter.EndMessage();
					client.Connection.Send(messageWriter);
					messageWriter.Recycle();
					return;
				}
			}
			List<InnerNetServer.Player> clients = this.Clients;
			lock (clients)
			{
				switch (this.GameState)
				{
				case GameStates.NotStarted:
					this.HandleNewGameJoin(client);
					break;
				default:
				{
					MessageWriter messageWriter2 = MessageWriter.Get(SendOption.Reliable);
					messageWriter2.StartMessage(1);
					messageWriter2.Write(2);
					messageWriter2.EndMessage();
					client.Connection.Send(messageWriter2);
					messageWriter2.Recycle();
					break;
				}
				case GameStates.Ended:
					this.HandleRejoin(client);
					break;
				}
			}
		}

		private void HandleRejoin(InnerNetServer.Player client)
		{
			if (client.Id == this.HostId)
			{
				this.GameState = GameStates.NotStarted;
				this.HandleNewGameJoin(client);
				MessageWriter messageWriter = MessageWriter.Get(SendOption.Reliable);
				for (int i = 0; i < this.Clients.Count; i++)
				{
					InnerNetServer.Player player = this.Clients[i];
					if (player != client)
					{
						try
						{
							this.WriteJoinedMessage(player, messageWriter, true);
							player.Connection.Send(messageWriter);
						}
						catch
						{
						}
					}
				}
				messageWriter.Recycle();
				return;
			}
			if (this.Clients.Count >= 9)
			{
				MessageWriter messageWriter2 = MessageWriter.Get(SendOption.Reliable);
				messageWriter2.StartMessage(1);
				messageWriter2.Write(1);
				messageWriter2.EndMessage();
				client.Connection.Send(messageWriter2);
				messageWriter2.Recycle();
				return;
			}
			this.Clients.Add(client);
			client.LimboState = LimboStates.WaitingForHost;
            // MsgWrite:WaitForHost
			MessageWriter messageWriter3 = MessageWriter.Get(SendOption.Reliable);
			try
			{
				messageWriter3.StartMessage(12);
				messageWriter3.Write(32);
				messageWriter3.Write(client.Id);
				messageWriter3.EndMessage();
				client.Connection.Send(messageWriter3);
				this.BroadcastJoinMessage(client, messageWriter3);
			}
			catch
			{
			}
			finally
			{
				messageWriter3.Recycle();
			}
		}

		private void HandleNewGameJoin(InnerNetServer.Player client)
		{
			if (this.Clients.Count >= 10)
			{
				MessageWriter messageWriter = MessageWriter.Get(SendOption.Reliable);
				try
				{
					messageWriter.StartMessage(1);
					messageWriter.Write(1);
					messageWriter.EndMessage();
					client.Connection.Send(messageWriter);
				}
				catch
				{
				}
				finally
				{
					messageWriter.Recycle();
				}
				return;
			}
			this.Clients.Add(client);
			client.LimboState = LimboStates.PreSpawn;
			if (this.HostId == -1)
			{
				this.HostId = this.Clients[0].Id;
			}
			if (this.HostId == client.Id)
			{
				client.LimboState = LimboStates.NotLimbo;
			}
			MessageWriter messageWriter2 = MessageWriter.Get(SendOption.Reliable);
			try
			{
				this.WriteJoinedMessage(client, messageWriter2, true);
				client.Connection.Send(messageWriter2);
				this.BroadcastJoinMessage(client, messageWriter2);
			}
			catch
			{
			}
			finally
			{
				messageWriter2.Recycle();
			}
		}

        // 游戏结束
		private void EndGame(MessageReader message, InnerNetServer.Player source)
		{
			if (source.Id == this.HostId)
			{
				this.GameState = GameStates.Ended;
				MessageWriter messageWriter = MessageWriter.Get(SendOption.Reliable);
				messageWriter.CopyFrom(message);
				this.Broadcast(messageWriter, null);
				messageWriter.Recycle();
				List<InnerNetServer.Player> clients = this.Clients;
				lock (clients)
				{
					this.Clients.Clear();
					return;
				}
			}
			Debug.LogWarning("Reset request rejected from: " + source.Id);
		}

        // 开始游戏,房主点击了开始按钮后,服务器向其他玩家广播开始消息
		private void StartGame(MessageReader message, InnerNetServer.Player source)
		{
			this.GameState = GameStates.Started;
			MessageWriter messageWriter = MessageWriter.Get(SendOption.Reliable);
			messageWriter.CopyFrom(message);
			this.Broadcast(messageWriter, null);
			messageWriter.Recycle();
		}

		private void ClientDisconnect(InnerNetServer.Player client)
		{
			Debug.Log("Server DC client " + client.Id);
			List<InnerNetServer.Player> clients = this.Clients;
			lock (clients)
			{
				this.Clients.Remove(client);
				client.Connection.Dispose();
				if (this.Clients.Count > 0)
				{
					this.HostId = this.Clients[0].Id;
				}
			}
			MessageWriter messageWriter = MessageWriter.Get(SendOption.Reliable);
			messageWriter.StartMessage(4);
			messageWriter.Write(32);
			messageWriter.Write(client.Id);
			messageWriter.Write(this.HostId);
			messageWriter.Write(0);
			messageWriter.EndMessage();
			this.Broadcast(messageWriter, null);
			messageWriter.Recycle();
		}

        // 精确发给某个玩家,而不是广播
		protected void SendTo(MessageWriter msg, int targetId)
		{
			List<InnerNetServer.Player> clients = this.Clients;
			lock (clients)
			{
				for (int i = 0; i < this.Clients.Count; i++)
				{
					InnerNetServer.Player player = this.Clients[i];
					if (player.Id == targetId)
					{
						try
						{
							player.Connection.Send(msg);
							break;
						}
						catch (Exception exception)
						{
							Debug.LogException(exception);
							break;
						}
					}
				}
			}
		}

        // 广播消息
		protected void Broadcast(MessageWriter msg, InnerNetServer.Player source)
		{
			List<InnerNetServer.Player> clients = this.Clients;
			lock (clients)
			{
                // 遍历所有的玩家,广播数据
				for (int i = 0; i < this.Clients.Count; i++)
				{
					InnerNetServer.Player player = this.Clients[i];
					if (player != source)
					{
						try
						{
                            // 发送数据
							player.Connection.Send(msg);
						}
						catch
						{
						}
					}
				}
			}
		}

		private void BroadcastJoinMessage(InnerNetServer.Player client, MessageWriter msg)
		{
			msg.Clear(SendOption.Reliable);
			msg.StartMessage(1);
			msg.Write(32);
			msg.Write(client.Id);
			msg.Write(this.HostId);
			msg.EndMessage();
			this.Broadcast(msg, client);
		}

        // MsgWrite:JoinedGame
		private void WriteJoinedMessage(InnerNetServer.Player client, MessageWriter msg, bool clear)
		{
			if (clear)
			{
				msg.Clear(SendOption.Reliable);
			}
			msg.StartMessage(7);
			msg.Write(32);
			msg.Write(client.Id);
			msg.Write(this.HostId);
			msg.WritePacked(this.Clients.Count - 1);
			for (int i = 0; i < this.Clients.Count; i++)
			{
				InnerNetServer.Player player = this.Clients[i];
				if (player != client)
				{
					msg.WritePacked(player.Id);
				}
			}
			msg.EndMessage();
		}
	}
}