#include "game/client/ClientGame.h"
#include "protocol/PacketDispatcher.h"
//#include "game/Team.h"
#include "game/client/Client.h"

#include <iostream>

namespace td {
namespace client {

ClientGame::ClientGame(Client* client) : protocol::PacketHandler(client->getConnexion().GetDispatcher()),
game::Game(&m_WorldClient), m_Client(client), m_Renderer(client->getRenderer()), m_WorldClient(this),
m_WorldRenderer(&m_WorldClient, this) {
    GetDispatcher()->RegisterHandler(protocol::PacketType::ConnectionInfo, this);
    GetDispatcher()->RegisterHandler(protocol::PacketType::PlayerJoin, this);
    GetDispatcher()->RegisterHandler(protocol::PacketType::PlayerList, this);
    GetDispatcher()->RegisterHandler(protocol::PacketType::PlayerLeave, this);
    GetDispatcher()->RegisterHandler(protocol::PacketType::UpdatePlayerTeam, this);
    GetDispatcher()->RegisterHandler(protocol::PacketType::UpdateLobbyTime, this);
    GetDispatcher()->RegisterHandler(protocol::PacketType::UpdateGameState, this);
    GetDispatcher()->RegisterHandler(protocol::PacketType::UpdateMoney, this);
    GetDispatcher()->RegisterHandler(protocol::PacketType::Disconnect, this);
    GetDispatcher()->RegisterHandler(protocol::PacketType::WorldData, this);
}

ClientGame::~ClientGame() {
    GetDispatcher()->UnregisterHandler(this);
}

void ClientGame::tick(std::uint64_t delta) {
    game::Game::tick(delta);
    m_WorldRenderer.update();
    if (m_GameState == game::GameState::Lobby && m_LobbyTime > 0) {
        m_LobbyTime -= delta;
    }
}

void ClientGame::HandlePacket(const protocol::PlayerJoinPacket* packet) {
    game::Player player(packet->getPlayerID());
    player.setName(packet->getPlayerName());

    m_Players.insert({ player.getID(), player });
}

void ClientGame::HandlePacket(const protocol::PlayerLeavePacket* packet) {
    game::Player* player = &m_Players[packet->getPlayerID()];
    if (player->getTeamColor() != game::TeamColor::None) {
        m_Teams[(std::size_t)player->getTeamColor()].removePlayer(player);
    }
    m_Players.erase(player->getID());
}

void ClientGame::HandlePacket(const protocol::PlayerListPacket* packet) {
    for (auto pair : packet->getPlayers()) {
        std::uint8_t playerID = pair.first;
        protocol::PlayerInfo playerInfo = pair.second;
        game::Player player(playerID);
        player.setName(playerInfo.name);
        player.setTeamColor(playerInfo.team);
        m_Players.insert({ playerID, player });
        if (player.getTeamColor() != game::TeamColor::None) {
            m_Teams[(std::size_t)player.getTeamColor()].addPlayer(&m_Players[playerID]);
        }
    }
    m_Player = &m_Players[m_ConnexionID];
}

void ClientGame::HandlePacket(const protocol::UpdatePlayerTeamPacket* packet) {
    game::Player* player = &m_Players[packet->getPlayerID()];
    if (player->getTeamColor() == game::TeamColor::None) { //join a team
        getTeam(packet->getSelectedTeam()).addPlayer(player);
    } else if (packet->getSelectedTeam() == game::TeamColor::None) { // leave a team
        getTeam(player->getTeamColor()).removePlayer(player);
    } else { // change team
        getTeam(player->getTeamColor()).removePlayer(player);
        getTeam(packet->getSelectedTeam()).addPlayer(player);
    }
    player->setTeamColor(packet->getSelectedTeam());
}

void ClientGame::HandlePacket(const protocol::UpdateGameStatePacket* packet) {
    setGameState(packet->getGameState());
}

void ClientGame::HandlePacket(const protocol::ConnexionInfoPacket* packet) {
    m_ConnexionID = packet->getConnectionID();
}

void ClientGame::HandlePacket(const protocol::UpdateLobbyTimePacket* packet) {
    m_LobbyTime = packet->getRemainingTime();
}

void ClientGame::HandlePacket(const protocol::UpdateMoneyPacket* packet) {
    m_Player->setGold(packet->getGold());
}

void ClientGame::HandlePacket(const protocol::DisconnectPacket* packet) {
    m_GameState = game::GameState::Disconnected;
}

void ClientGame::HandlePacket(const protocol::WorldDataPacket* packet) {
    m_WorldRenderer.loadModels();
    // set cam pos to player spawn
    const game::Spawn& spawn = m_World->getTeam(m_Player->getTeamColor()).getSpawn();
    m_WorldRenderer.setCamPos(spawn.getCenterX(), spawn.getCenterY());
}

void ClientGame::renderWorld() {
    if (m_GameState == game::GameState::Game || m_GameState == game::GameState::EndGame) {
        m_WorldRenderer.render();
    }
}

} // namespace client
} // namespace td
