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

using Client = Fiesta.Networking.Client;
using PacketHandler = Fiesta.Networking.Packets.PacketHandler;

namespace World.Login
{
	public static class InterLoginManager
	{
		private static byte _worldID;

		private static PacketHandler _packetHandler;

		private static ServerInfo.LoginServerInfo _loginServerInfo;

		private static Client _client;

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

		public static void Load(byte worldID)
		{
			_worldID = worldID;

			if (!_hasLoaded)
			{
				_hasLoaded = true;

				try
				{
					_packetHandler = new PacketHandler("Login");
				}
				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("InterLoginManager has already loaded");
			}
		}

		public static void Start(ServerInfo.LoginServerInfo loginServerInfo)
		{
			if (!_hasStarted)
			{
				_hasStarted = true;

				_loginServerInfo = loginServerInfo;

				EstablishConnection();
			}
			else
			{
				throw new Exception("InterLoginManager has already started");
			}
		}

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

				_packetHandler.Dispose();
				_client?.Dispose();

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

		private static void EstablishConnection(Socket socket = null)
		{
			if (_hasStarted && !_isStopping)
			{
				if (socket == null)
				{
					socket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
				}

				socket.BeginConnect(new IPEndPoint(IPAddress.Parse(_loginServerInfo.Host), _loginServerInfo.Ports.Worlds), new AsyncCallback(Connect), socket);
			}
		}

		private static void Connect(IAsyncResult asyncResult)
		{
			Socket socket = (Socket)asyncResult.AsyncState;

			try
			{
				socket.EndConnect(asyncResult);

				_client = new Client(socket);

				_client.UseEncryption = false;

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

				_client.Send(new Networking.Packets.Misc.Out.Authentication(_worldID));

				Console.WriteLine("Connected to Login");
			}
			catch
			{
				EstablishConnection(socket);
			}
		}

		private static void Recieve(Client 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 Login packet: {raw}");

				packet.Dispose();
			}
		}

		private static void Disconnect(Client client)
		{
			Console.WriteLine("Login disconnected");

			EstablishConnection();
		}
	}
}