//
// Created by sebastian on 05.08.26.
//

#include "LudoGameController.h"

#include "core/animation/components/BlockingAnimationComponent.h"
#include "core/audio/ecs/BlockingSoundComponent.h"

std::unique_ptr<ludo::GameController::LudoGameController> ludo::GameController::LudoGameController::create(LudoGameControllerContext& context) {
    auto controller = std::make_unique<LudoGameController>(context);
    controller->m_piecePresenter.spawnPieces(context.gameMode->getState());
    std::vector<engine::PlayerID> playerOrder = controller->m_gameMode->getPlayerOrder();
    for (engine::PlayerID player : playerOrder) {
        if (player == context.localPlayer) continue;

        controller->m_aiControllers.emplace_back(LudoAiController(controller->m_gameMode, player));
    }
    return controller;
}

void ludo::GameController::LudoGameController::update(float deltaTime, const engine::Mouse &mouse, const engine::Keyboard &keyboard) {
    m_animationSystem.update(deltaTime);
    
    if (isBlocking()) return;
    
    processEvents();
    
    m_presentationGameState.commitGameState(
        m_gameMode->getCurrentPlayer(),
        m_gameMode->getColorOf(m_gameMode->getCurrentPlayer()),
        m_gameMode->getState().getDiceValue(),
        m_gameMode->getCurrentPlayer() == 0 //Todo: Replace through real m_localPlayer variable
    );
    
    for (auto& ai : m_aiControllers) {
        ai.update(deltaTime);
    }
    
    handleInput(mouse, keyboard);
}

void ludo::GameController::LudoGameController::handleInput(const engine::Mouse &mouse, const engine::Keyboard &keyboard) {
    m_highlightSystem.update(mouse, m_registry);
    
    auto entity = m_pickingSystem.update(mouse, m_registry);
    if (!entity) return;
    
    auto pieceIndex = m_piecePresenter.getPieceIndex(*entity);
    if (!pieceIndex) return;
    
    m_gameMode->sendCommand(MovePieceCommand(m_localPlayer, *pieceIndex));
}

void ludo::GameController::LudoGameController::processEvents() {
    for (const auto& event : m_gameMode->pollEvents()) {
        std::visit([this]<typename T>(const T& e) {
                if constexpr (std::is_same_v<T, ludo::PieceMovedEvent>) {
                    m_piecePresenter.onPieceMoved(e, m_gameMode->getState());
                }
                if constexpr (std::is_same_v<T, ludo::DiceRolledEvent>) {
                    // m_lastEventMessage = std::format("Wuerfel geworfen: {}", e.diceValue);
                }
                // DiceRolledEvent/TurnChangedEvent -> ImGui-State aktualisieren
        }, event);
    }
}

bool ludo::GameController::LudoGameController::isBlocking() const {
    const bool isSoundBlocking = m_registry.getPool<engine::audio::BlockingSoundComponent>().size() > 0;
    const bool isAnimationBlocking = m_registry.getPool<engine::animation::BlockingAnimationComponent>().size() > 0;

    return isSoundBlocking || isAnimationBlocking;
}

ludo::GameController::LudoGameController::LudoGameController(
    LudoGameControllerContext deps
)
    :
    m_gameMode(std::move(deps.gameMode)),
    m_registry(deps.registry),
    m_piecePresenter(
        deps.animationEventBus,
        m_registry,
        std::move(deps.pieceModels),
        std::move(deps.pieceSounds),
        std::move(deps.boardModel)
    ),
    m_presentationGameState(*m_gameMode, m_gameMode->getState()),
    m_animationSystem(m_registry),
    m_highlightSystem(deps.camera),
    m_pickingSystem(deps.camera),
    m_localPlayer(deps.localPlayer)
{
}

