/*  
	EQ2Emulator:  Everquest II Server Emulator
	Copyright (C) 2007  EQ2EMulator Development Team (http://www.eq2emulator.net)

	This file is part of EQ2Emulator.
*/

#pragma once

// Standard library includes
#include <cstdint>
#include <map>
#include <memory>
#include <mutex>
#include <string>
#include <string_view>
#include <vector>

// Project includes
#include "../common/linked_list.h"
#include "../common/timer.h"
#include "../common/TCPConnection.h"
#include "../common/PacketStruct.h"
#include "login_structs.h"
#include "LoginAccount.h"

// Forward declarations
class DelayQue;
class EQStream;
class EQ2Packet;
class EQApplicationPacket;

/**
 * @brief Login modes for client state management
 */
enum class LoginMode : std::uint8_t
{
    None,         // No login state
    Normal,       // Normal login process
    Registration  // Account registration mode
};

/**
 * @brief Represents a connected client in the login server
 * 
 * This class manages individual client connections, handling:
 * - Authentication and login process
 * - Character selection and creation
 * - World server communication
 * - Packet processing and queuing
 */
class Client
{
public:
    /**
     * @brief Constructs a new client instance
     * @param ieqnc The EQStream connection for this client
     */
    explicit Client(EQStream* ieqnc);
    
    /**
     * @brief Destructor - ensures proper cleanup of client resources
     */
    ~Client();
    
    // Disable copy operations
    Client(const Client&) = delete;
    Client& operator=(const Client&) = delete;
    
    // Login response methods
    /**
     * @brief Sends a login denied message to the client
     */
    void SendLoginDenied();
    
    /**
     * @brief Sends a login denied message due to version mismatch
     */
    void SendLoginDeniedBadVersion();
    
    /**
     * @brief Sends a login accepted message to the client
     * @param account_id The account ID (default: 1)
     * @param login_response The login response code (default: 0)
     */
    void SendLoginAccepted(std::int32_t account_id = 1, std::int8_t login_response = 0);
    
    // World and character list methods
    /**
     * @brief Sends the world server list to the client
     */
    void SendWorldList();
    
    /**
     * @brief Sends the character list to the client
     */
    void SendCharList();
    
    /**
     * @brief Adds a world server to the list buffer
     * @param buffer Output buffer for world data
     * @param name World server name
     * @param id World server ID
     * @param flags World server flags
     * @return Size of data written to buffer
     */
    std::int16_t AddWorldToList2(unsigned char* buffer, const char* name, 
                                 std::int32_t id, std::int16_t* flags);
    
    /**
     * @brief Generates checksum for outgoing packet
     * @param outapp Packet to generate checksum for
     */
    void GenerateChecksum(EQApplicationPacket* outapp);
    
    /**
     * @brief Main processing loop for the client
     * @return true if client is still active, false if should be removed
     */
    bool Process();
    
    /**
     * @brief Saves client error logs to database
     * @param app Packet containing error data
     * @param type Type of error log
     * @param version Client version
     */
    void SaveErrorsToDB(EQApplicationPacket* app, const char* type, std::int32_t version);
    
    // Character management methods
    /**
     * @brief Handles character creation approval from world server
     * @param server_id World server ID
     * @param char_id New character ID
     */
    void CharacterApproved(std::int32_t server_id, std::int32_t char_id);
    
    /**
     * @brief Handles character creation rejection
     * @param reason_number Rejection reason code
     */
    void CharacterRejected(std::int8_t reason_number);
    
    /**
     * @brief Processes login attempt
     * @param name Account name
     * @param pass Account password
     * @param seq Sequence number (default: 0)
     */
    void ProcessLogin(const char* name, const char* pass, int seq = 0);
    
    /**
     * @brief Queues a packet for sending to the client
     * @param app Packet to queue
     */
    void QueuePacket(EQ2Packet* app);
    
    /**
     * @brief Sends a fatal error to the client
     * @param response Error response code
     */
    void FatalError(std::int8_t response);
    
    /**
     * @brief Handles world server response for play request
     * @param worldid World server ID
     * @param response Response code
     * @param ip_address World server IP
     * @param port World server port
     * @param access_key Access key for world server
     */
    void WorldResponse(std::int32_t worldid, std::int8_t response, 
                      char* ip_address, std::int32_t port, std::int32_t access_key);
    
    /**
     * @brief Sends play failed message to client
     * @param response Failure reason code
     */
    void SendPlayFailed(std::int8_t response);
    
    /**
     * @brief Starts the disconnect timer
     */
    void StartDisconnectTimer();
    
    // Getter methods
    [[nodiscard]] EQStream* getConnection() { return eqnc_; }
    [[nodiscard]] LoginAccount* GetLoginAccount() { return login_account_.get(); }
    [[nodiscard]] std::int16_t GetVersion() const { return version_; }
    [[nodiscard]] char* GetKey() { return key_; }
    [[nodiscard]] std::int32_t GetIP() const { return ip_; }
    [[nodiscard]] std::int16_t GetPort() const { return port_; }
    [[nodiscard]] std::int32_t GetAccountID() const { return account_id_; }
    [[nodiscard]] const char* GetAccountName() const { return account_name_.c_str(); }
    [[nodiscard]] bool AwaitingCharCreationRequest() const { return createRequest_ != nullptr; }
    
    // Setter methods
    void SetLoginAccount(LoginAccount* in_account)
    {
        login_account_.reset(in_account);
        if (in_account)
        {
            account_id_ = in_account->getLoginAccountID();
        }
    }
    
    void SetKey(const char* in_key) 
    { 
        std::strncpy(key_, in_key, sizeof(key_) - 1);
        key_[sizeof(key_) - 1] = '\0';
    }
    
    void SetAccountName(const char* name) { account_name_ = name; }
    
    // Public data members (legacy compatibility)
    std::int8_t LoginKey[10]{};     // Login key for authentication
    std::int8_t ClientSession[25]{}; // Client session data
    
    // Timers
    std::unique_ptr<Timer> updatetimer;      // Timer for periodic updates
    std::unique_ptr<Timer> updatelisttimer;  // Timer for world list updates
    std::unique_ptr<Timer> disconnectTimer;  // Timer for disconnect timeout
    
    // Request tracking
    std::int16_t packettotal{0};            // Total packets processed
    std::int32_t requested_server_id{0};    // Requested world server ID
    std::int32_t request_num{0};            // Current request number
    
    // Delay queue for packet scheduling
    LinkedList<DelayQue*> delay_que;
    
private:
    // Character play state
    std::string pending_play_char_name_;     // Name of character pending play
    std::int32_t pending_play_char_id_{0};   // ID of character pending play
    
    // Update state
    std::int8_t update_position_{0};         // Current position in update sequence
    std::int16_t num_updates_{0};            // Number of updates sent
    std::vector<PacketStruct*>* update_packets_{nullptr}; // Update packet queue
    
    // Account information
    std::unique_ptr<LoginAccount> login_account_; // Login account data
    EQStream* eqnc_;                              // Network connection stream
    
    // Connection details
    std::int32_t ip_{0};                     // Client IP address
    std::int16_t port_{0};                   // Client port
    
    // Account data
    std::int32_t account_id_{0};             // Account ID in database
    std::string account_name_;               // Account name
    char key_[10]{};                         // Authentication key
    std::int8_t lsadmin_{0};                 // Login server admin level
    std::int16_t worldadmin_{0};             // World server admin level
    int lsstatus_{0};                        // Login server status
    
    // Client state flags
    bool kicked_{false};                     // Client has been kicked
    bool verified_{false};                   // Client has been verified
    bool start_{false};                      // Client has started
    bool needs_world_list_{true};            // Client needs world list
    bool sent_character_list_{false};        // Character list has been sent
    
    // Version and mode
    std::int16_t version_{0};                // Client version
    LoginMode login_mode_{LoginMode::None};  // Current login mode
    
    // Character creation
    char bannedreason_[30]{};                // Ban reason if applicable
    PacketStruct* createRequest_{nullptr};   // Pending character creation request
    std::unique_ptr<Timer> playWaitTimer_;   // Timer for play request timeout
};

/**
 * @brief Manages all connected clients
 * 
 * Thread-safe container for managing multiple client connections
 */
class ClientList
{
public:
    /**
     * @brief Default constructor
     */
    ClientList() = default;
    
    /**
     * @brief Destructor
     */
    ~ClientList() = default;
    
    // Disable copy operations
    ClientList(const ClientList&) = delete;
    ClientList& operator=(const ClientList&) = delete;
    
    /**
     * @brief Adds a client to the list
     * @param client Client to add
     */
    void Add(Client* client);
    
    /**
     * @brief Finds a client by IP and port
     * @param ip Client IP address
     * @param port Client port
     * @return Pointer to client if found, nullptr otherwise
     */
    [[nodiscard]] Client* Get(std::int32_t ip, std::int16_t port);
    
    /**
     * @brief Finds a client by login server account ID
     * @param lsaccountid Login server account ID
     * @return Pointer to client if found, nullptr otherwise
     */
    [[nodiscard]] Client* FindByLSID(std::int32_t lsaccountid);
    
    /**
     * @brief Finds clients waiting for character creation response
     */
    void FindByCreateRequest();
    
    /**
     * @brief Sends a packet to all connected clients
     * @param app Packet to send
     */
    void SendPacketToAllClients(EQ2Packet* app);
    
    /**
     * @brief Processes all clients in the list
     */
    void Process();
    
private:
    mutable std::mutex mutex_;               // Mutex for thread safety
    std::map<Client*, bool> client_list_;    // Map of clients to active status
};

/**
 * @brief Delayed packet queue entry
 * 
 * Holds a packet with an associated timer for delayed sending
 */
class DelayQue
{
public:
    /**
     * @brief Constructs a delayed queue entry
     * @param in_timer Timer for delay
     * @param in_packet Packet to delay
     */
    DelayQue(Timer* in_timer, EQApplicationPacket* in_packet)
        : timer(in_timer)
        , packet(in_packet)
    {
    }
    
    Timer* timer;                 // Timer controlling the delay
    EQApplicationPacket* packet;  // Packet to be sent after delay
};