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

namespace FiestaBot.Login.Handlers
{
    internal class LoginHandlers
    {
        [LoginPacketHandler(2, 4)]
        public static void Ping(LoginClient Socket, Packet Packet)
        {
            Packet.Dispose();

            Packet p = new Packet(2, 5);
            Socket.SendPacket(p);
        }

        [LoginPacketHandler(2, 7)]
        public static void ReceiveXor(LoginClient Socket, Packet Packet)
        {
            Packet.ReadShort(out short Xor);
            //Console.WriteLine("Received xor: {0}", Xor);

            Socket.Crypto = new FiestaCrypto(Xor);

            Packet.Dispose();

            //Send version
            Packet p = new Packet(3, 1);
            p.WriteUShort(Program.Config.VersionYear);
            p.WriteUShort(Program.Config.Version);
            p.WriteByte(0);
            Socket.SendPacket(p);
        }

        [LoginPacketHandler(3, 2)]
        public static void InvalidVersion(LoginClient Socket, Packet Packet)
        {
            Console.WriteLine("Invalid client version");
            Packet.Dispose();
            Socket.Disconnect();
        }

        [LoginPacketHandler(3, 3)]
        public static void CorrectVersion(LoginClient Socket, Packet Packet)
        {
            Console.WriteLine("Client version verified");
            Packet.Dispose();

            //Send hash
            Packet p = new Packet(3, 4);
            p.WriteString(Program.Config.Hash);
            Socket.SendPacket(p);
        }

        [LoginPacketHandler(3, 5)]
        public static void CorrectHash(LoginClient Socket, Packet Packet)
        {
            Console.WriteLine("Client hash verified");
            Packet.Dispose();

            //Send token
            Packet p = new Packet(3, 32);
            p.WriteString(Program.Config.Token, 65);
            Socket.SendPacket(p);
        }

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

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

        [LoginPacketHandler(3, 10)]
        public static void ReceiveWorldInfo(LoginClient Socket, Packet Packet)
        {
            Console.WriteLine("Login succesful");
            Socket.InWorldSelect = true;
            Packet.ReadByte(out byte worldTotal);
            if(worldTotal > 0)
            {
                Program.AvailableWorlds = worldTotal;
                Console.WriteLine("{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);
                    Console.WriteLine("ID: {0} | Name: {1} | Status: {2}", worldID, worldName, (WorldStatus)worldStatus);
                }
            }
            else
            {
                Console.WriteLine("There are currently no worlds available");
                Socket.Disconnect();
                Packet.Dispose();
                return;
            }
            Packet.Dispose();

            Console.WriteLine("> Select a world with selectworld [ID]");
        }

        [LoginPacketHandler(3, 12)]
        public static void WorldServerInfo(LoginClient Socket, Packet Packet)
        {
            Packet.ReadByte(out byte worldStatus);
            if(worldStatus < 6)
            {
                switch(worldStatus)
                {
                    case 0:
                        Console.WriteLine("World connection failed: server is offline");
                        break;
                    case 1:
                        Console.WriteLine("World connection failed: server is under maintenance");
                        break;
                    case 2:
                        Console.WriteLine("World connection failed: server is offline");
                        break;
                    case 3:
                        Console.WriteLine("World connection failed: server is reserved");
                        break;
                    case 4:
                        Console.WriteLine("World connection failed: unknown error occurred");
                        break;
                    case 5:
                        Console.WriteLine("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();

                ManagerClient MC = 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)
            {
                Console.WriteLine("{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);
                    Console.WriteLine("ID: {0} | Name: {1} | Status: {2}", worldID, worldName, (WorldStatus)worldStatus);
                }
            }
            else
            {
                Console.WriteLine("There are currently no worlds available");
                Socket.Disconnect();
                Packet.Dispose();
                return;
            }
            Packet.Dispose();
        }
    }


    internal enum LoginResponse : byte
    {
        DatabaseError = 67, //DB Error.
        AuthFailed = 68, //Authentication Failed.
        CheckIdPw = 69, //Please check ID or Password.
        IdBlocked = 71, //The ID has been blocked.
        WorldMaintenance = 72, //The World Servers are down for maintenance.
        TimeOut = 73, //Authentication timed out. Please try to log in again.
        LoginFailed = 74, //Login failed.
        Agreement = 75, //Please accept the agreement to continue.
    }

    public enum WorldStatus : byte
    {
        // With messages:
        Closed = 0,        // Server is closed. Please try login later.
        Maintenance = 1,    // Server is under maintenance. Please try login later.
        Offline = 2,    // You cannot connect to an empty server.
        Reserved = 3,       // The server has been reserved for a special use.
        Unknown = 4, // Login failed due to an unknown error.
        Full = 5,           // Server is full. Please try again later.
        Low = 6,
        // LOW2 = 7,
        // LOW3 = 8,
        Medium = 9,
        High = 10,
    }
}