using System;
using System.CodeDom;
using System.Collections.Generic;
using System.Linq;
using System.Net;
using System.Text;
using System.Threading.Tasks;
using FiestaBot.Enums;
using FiestaBot.Instances.Networking;
using FiestaBot.Login.ClientPacket;
using FiestaBot.Login.Networking;
using FiestaBot.Manager.Networking;
using FiestaBot.Utilities;

namespace FiestaBot.Login.Handlers
{
    internal class Login03Handler
    {
        [LoginPacketHandler(3, 2)]
        public static void InvalidVersion(LoginClient Socket, Packet Packet)
        {
            Logger.Log(LogType.Login, "Invalid client version");
            Packet.Dispose();
            Socket.Disconnect();
        }

        [LoginPacketHandler(3, 3)]
        public static void CorrectVersion(LoginClient Socket, Packet Packet)
        {
            Logger.Log(LogType.Login, "Client version verified");
            Packet.Dispose();
        }

        [LoginPacketHandler(3, 5)]
        public static void CorrectHash(LoginClient Socket, Packet Packet)
        {
            Logger.Log(LogType.Login, "Client hash verified");
            Packet.Dispose();

            //Send token
            LoginPacket.SendToken(Socket);
        }

        [LoginPacketHandler(3, 9)]
        public static void LoginError(LoginClient Socket, Packet Packet)
        {
            Packet.ReadByte(out byte Response);

            switch(Response)
            {
                case 67:
                    Logger.Log(LogType.Login, "Login failed: a database error occurred");
                    break;
                case 68:
                    Logger.Log(LogType.Login, "Login failed: invalid token");
                    break;
                case 69:
                    Logger.Log(LogType.Login, "Login failed: check your id or password");
                    break;
                case 71:
                    Logger.Log(LogType.Login, "Login failed: user id has been blocked");
                    break;
                case 72:
                    Logger.Log(LogType.Login, "Login failed: world servers are under maintenance");
                    break;
                case 73:
                    Logger.Log(LogType.Login, "Login failed: login timed out");
                    break;
                case 74:
                    Logger.Log(LogType.Login, "Login failed: no reason specified");
                    break;
                case 75:
                    Logger.Log(LogType.Login, "Login failed: accept the agreement to continue");
                    break;
                default:
                    Logger.Log(LogType.Login, "Login failed: unknown error occurred");
                    break;
            }
            Packet.Dispose();
        }

        [LoginPacketHandler(3, 10)]
        public static void ReceiveWorldInfo(LoginClient Socket, Packet Packet)
        {
            Logger.Log(LogType.Login, "Login succesful");

            Socket.InWorldSelect = true;

            Packet.ReadByte(out byte worldTotal);

            if(worldTotal > 0)
            {
                Program.AvailableWorlds = worldTotal;
                Logger.Log(LogType.Login, "{0} available worlds:", worldTotal);
                for(int i = 0; i < worldTotal; i++)
                {
                    Packet.ReadByte(out byte worldID);
                    Packet.ReadString(out string worldName, 16);
                    Packet.ReadByte(out byte worldStatus);
                    Logger.Log(LogType.Login, "ID: {0} | Name: {1} | Status: {2}", worldID, worldName, (WorldStatus)worldStatus);
                }
            }
            else
            {
                Logger.Log(LogType.Login, "There are currently no worlds available");
                Socket.Disconnect();
                Packet.Dispose();
                return;
            }
            Packet.Dispose();

            if(Program.Config.AutoSelectWorld)
            {
                Logger.Log(LogType.Login, "Automatically connecting to world 0");
                LoginPacket.SendWorldSelect(Socket, Program.Config.AutoSelectWorldID);
            }
            else { Logger.Log(LogType.Login, "> Select a world with selectworld [ID]"); }
        }

        [LoginPacketHandler(3, 12)]
        public static void WorldIP(LoginClient Socket, Packet Packet)
        {
            Packet.ReadByte(out byte worldStatus);
            if(worldStatus < 6)
            {
                switch(worldStatus)
                {
                    case 0:
                        Logger.Log(LogType.World, "World connection failed: server is offline");
                        break;
                    case 1:
                        Logger.Log(LogType.World, "World connection failed: server is under maintenance");
                        break;
                    case 2:
                        Logger.Log(LogType.World, "World connection failed: server is offline");
                        break;
                    case 3:
                        Logger.Log(LogType.World, "World connection failed: server is reserved");
                        break;
                    case 4:
                        Logger.Log(LogType.World, "World connection failed: unknown error occurred");
                        break;
                    case 5:
                        Logger.Log(LogType.World, "World connection failed: server is full");
                        break;
                }
            }
            else
            {
                Packet.ReadString(out string IP, 16);
                Packet.ReadUShort(out ushort Port);
                Packet.ReadString(out string TransferKey, 64);
                Packet.Dispose();

                Program.TransferKey = TransferKey;

                Socket.Disconnect();

                new ManagerClient(IPAddress.Parse(IP), Port);
            }
        }

        [LoginPacketHandler(3, 28)]
        public static void ReReceiveWorldInfo(LoginClient Socket, Packet Packet)
        {
            Packet.ReadByte(out byte worldTotal);
            if(worldTotal > 0)
            {
                Logger.Log(LogType.Login, "{0} available worlds:", worldTotal);
                for(int i = 0; i < worldTotal; i++)
                {
                    Packet.ReadByte(out byte worldID);
                    Packet.ReadString(out string worldName, 16);
                    Packet.ReadByte(out byte worldStatus);
                    Logger.Log(LogType.Login, "ID: {0} | Name: {1} | Status: {2}", worldID, worldName, (WorldStatus)worldStatus);
                }
            }
            else
            {
                Logger.Log(LogType.Login, "There are currently no worlds available");
                Socket.Disconnect();
                Packet.Dispose();
                return;
            }
            Packet.Dispose();
        }
    }
}