using Fiesta.Config;
using Fiesta.Networking.Encryption;
using Fiesta.Networking.Packets;
using Fiesta.Players.Networking;
using System;
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Net.Sockets;
using System.Text;
using System.Threading.Tasks;
using Villain.Networking.Packets;

using Console = Villain.Output.Console;
using Client = Fiesta.Networking.Client;
using PacketHandler = Fiesta.Networking.Packets.PacketHandler;

namespace Fiesta.Players
{
	public static class PlayerManager
	{
		private static Listener _listener;

		private static List<PlayerConnection> _playerConnections = new List<PlayerConnection>();
		private static PacketHandler _packetHandler;

		public static List<PlayerGUID> PlayerGUIDs = new List<PlayerGUID>();

		private static bool _hasLoaded = false;
		private static bool _hasStarted = false;
		private static bool _isStopping = false;

		public static void Load()
		{
			if (!_hasLoaded)
			{
				_hasLoaded = true;

				try
				{
					_packetHandler = new PacketHandler("Player");

					_listener = new Listener();
					_listener.OnConnect = Connect;
				}
				catch (PacketHandlerException exception)
				{
					PacketHandlerAttribute attribute = (PacketHandlerAttribute)exception.Handler.Attribute;

					Console.WriteLine(exception.Message, ConsoleColor.Yellow);
					Console.WriteLine($"Handler For: {attribute.ForID}, Key: {attribute.Key}", ConsoleColor.Yellow);
				}
			}
			else
			{
				throw new Exception("PlayerManager has already loaded");
			}
		}

		public static void Start(string host, Int32 port)
		{
			if (!_hasStarted)
			{
				_hasStarted = true;

				_listener.Listen(host, port);
			}
			else
			{
				throw new Exception("PlayerManager has already started");
			}
		}

		public static void Stop()
		{
			if (_hasStarted && !_isStopping)
			{
				_isStopping = true;

				_packetHandler.Dispose();
				_listener.Dispose();

				foreach (Client client in _playerConnections)
				{
					client.Dispose();
				}

				_playerConnections = new List<PlayerConnection>();

				_hasLoaded = false;
				_hasStarted = false;
				_isStopping = false;
			}
		}

		private static void Connect(Socket socket)
		{
			PlayerConnection playerConnection = new PlayerConnection(socket);

			playerConnection.OnDisconnect = Disconnect;
			playerConnection.OnRecieve = (packet) =>
			{
				Recieve(playerConnection, packet);
			};

			_playerConnections.Add(playerConnection);

			Console.WriteLine("Player connected");

			playerConnection.Send(new Networking.Packets.Misc.Out.EncryptionInitialization(((XorEncryption)playerConnection.Encryption).xorPosition));
		}

		private static void Recieve(PlayerConnection client, InPacket packet)
		{
			bool wasHandled = _packetHandler.Handle(client, packet);

			if (!wasHandled)
			{
				string raw = $"Type: 0x{packet.Type.ToString("X4")}, Size: 0x{packet.BinaryReader.BaseStream.Length.ToString("X4")}, Data: {BitConverter.ToString(((MemoryStream)packet.BinaryReader.BaseStream).ToArray())}";
				Console.WriteLine($"Unhandled Player packet: {raw}");

				packet.Dispose();
			}
		}

		private static void Disconnect(Client client)
		{
			_playerConnections.Remove((PlayerConnection)client);

			Console.WriteLine("Player disconnected");
		}

		public static Int32 GetConnectionsCount()
		{
			return _playerConnections.Count;
		}

		public static PlayerConnection GetPlayerConnection(UInt32 userID)
		{
			return _playerConnections.Find(x => x.Account.ID == userID);
		}

		public static PlayerGUID GrabPlayerGUID(UInt16[] guid)
		{
			PlayerGUIDs.RemoveAll(x => x.Created < DateTime.Now.AddMinutes(-5));

			PlayerGUID playerGUID = PlayerGUIDs.Find(x => x.GUID.SequenceEqual(guid));

			if (playerGUID != null)
			{
				PlayerGUIDs.Remove(playerGUID);
			}

			return playerGUID;
		}
	}
}