using Fiesta.Config;
using Fiesta.Networking.Packets;
using Fiesta.Worlds.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.Worlds
{
	public static class WorldManager
	{
		public readonly static List<World> Worlds = new List<World>();

		private static Listener _listener;
		private static List<WorldConnection> _worldConnections = new List<WorldConnection>();
		private static PacketHandler _packetHandler;

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

		public static void Load(List<ServerInfo.WorldServerInfo> worldServerInfos)
		{
			if (!_hasLoaded)
			{
				_hasLoaded = true;

				try
				{
					foreach (ServerInfo.WorldServerInfo worldServerInfo in worldServerInfos)
					{
						Worlds.Add(new World(worldServerInfo));
					}

					_packetHandler = new PacketHandler("World");

					_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("WorldManager has already loaded");
			}
		}

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

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

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

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

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

				_worldConnections = new List<WorldConnection>();

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

		private static void Connect(Socket socket)
		{
			WorldConnection worldConnection = new WorldConnection(socket);

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

			_worldConnections.Add(worldConnection);

			Console.WriteLine("World connected");
		}

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

			string raw = $"Type: 0x{packet.Type.ToString("X4")}, Size: 0x{packet.BinaryReader.BaseStream.Length.ToString("X4")}, Data: {BitConverter.ToString(((MemoryStream)packet.BinaryReader.BaseStream).ToArray())}";

			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 World packet: {raw}");

				packet.Dispose();
			}
			else
			{
				Console.WriteLine($"HANDLED World packet: {raw}");
			}
		}

		private static void Disconnect(Client client)
		{
			WorldConnection worldConnection = (WorldConnection)client;

			if (worldConnection.World != null)
			{
				worldConnection.World.Status = World.WorldStatus.Offline;
			}

			_worldConnections.Remove(worldConnection);

			Console.WriteLine("World disconnected");
		}

		public static WorldConnection GetWorldConnection(byte worldID)
		{
			return _worldConnections.Find(x => x.World?.ID == worldID);
		}
	}
}