//
// Created by sebastian on 02.08.26.
//

#include "PiecePresenter.h"


#include "LudoGameMode.h"
#include "core/animation/components/BlockingAnimationComponent.h"
#include "core/animation/components/TransformAnimationComponent.h"
#include "ecs/standardComponents/MeshComponent.h"
#include "ecs/standardComponents/PickableComponent.h"
#include "ecs/standardComponents/TransformComponent.h"
#include "gameMode/PiecePathBuilder.h"
#include "spdlog/spdlog.h"


void ludo::PiecePresenter::spawnPieces(const ludo::LudoGameState &state) {
    for (const auto& piece : state.getPieces()) {
        auto entity = m_registry.createEntity();
        const auto transform = engine::TransformComponent(
            m_layout.getWorldPosition(piece.color, piece.state, piece.position),
            {0,0,0},
            {0.334296f, 0.334296f, 0.334296f});
        m_registry.addComponent<engine::TransformComponent>(entity, transform);
        m_registry.addComponent<engine::MeshComponent>(entity, engine::MeshComponent(m_pieceModels[std::string(ludo::toString(piece.color))]));
        m_registry.addComponent<engine::PickableComponent>(entity, engine::PickableComponent());

        auto soundSource = engine::audio::SoundSource();
        soundSource.setBuffer(*m_pieceSounds["step"]);
        soundSource.setPosition(transform.m_position.x, transform.m_position.y, transform.m_position.z);
        soundSource.setGain(1.0f);
        soundSource.setLooping(false);
        m_registry.addComponent<engine::audio::SoundSource>(entity, std::move(soundSource));
        m_pieceEntities.push_back(entity);
    }
}

void ludo::PiecePresenter::onPieceMoved(const ludo::PieceMovedEvent &event, const ludo::LudoGameState &state) {
    const auto& piece = state.getPieces()[event.pieceIndex];
    auto target = m_layout.getWorldPosition(piece.color, piece.state, piece.position);
    auto entity = m_pieceEntities[event.pieceIndex];

    engine::animation::TransformAnimationComponent animationComponent;
    std::vector<PathNode> path = PiecePathBuilder::computePath(piece.color, event.startState, event.fromPosition, event.targetState, event.toPosition);
    std::vector<glm::vec3> pathPositions;
    for (const auto& node : path) {
        pathPositions.push_back(m_layout.getWorldPosition(piece.color, node.state, node.position));
    }

    std::vector<engine::animation::Keyframe<glm::vec3>> keyframes;
    std::vector<engine::animation::AnimationMarker> animationMarkers;
    float time = 0;
    constexpr int kHopSubdivisions = 8; // Punkte zwischen den beiden Endpunkten
    constexpr float kHopHeight = 0.3f;  // Sprunghöhe in Weltkoordinaten

    if (pathPositions.back() != target) {
        spdlog::warn("PiecePresenter: computePath-Ziel weicht von autoritativem Board-State ab (pieceIndex={})", event.pieceIndex);
    }

    for (int i=0; i + 1 <pathPositions.size(); ++i) {
        const auto& from = pathPositions[i];
        const auto& to = pathPositions[i + 1];

        keyframes.emplace_back(time, from);
        time += 0.5f / (kHopSubdivisions + 1);

        for (int s = 1; s < kHopSubdivisions; ++s) {
            float t = static_cast<float>(s) / (kHopSubdivisions + 1);
            glm::vec3 pos = glm::mix(from, to, t);
            pos.y += kHopHeight * (1.0f - (2.0f * t - 1.0f) * (2.0f * t - 1.0f)); // -x² Parabel, Scheitel bei t=0.5
            keyframes.emplace_back(time, pos);
            time += 0.5f / (kHopSubdivisions + 1);
        }
        keyframes.emplace_back(time, to);
        animationMarkers.emplace_back(time, [this, entity]() {
            spdlog::info("Should play step");
            auto animationMarkerEvent = engine::animation::AnimationMarkerEvent(entity, "step");
            this->m_animationEventBus.publish(animationMarkerEvent);
        });
        time += 0.5f / (kHopSubdivisions + 1);
    }

    engine::animation::AnimationTrack<glm::vec3> positionTrack;
    engine::animation::AnimationTrack<glm::vec3> rotationTrack;
    engine::animation::AnimationTrack<glm::vec3> scaleTrack;

    positionTrack.keys = keyframes;
    animationComponent.position = positionTrack;
    animationComponent.rotation = rotationTrack;
    animationComponent.scale = scaleTrack;
    animationComponent.loop = false;
    animationComponent.duration = time;
    animationComponent.onFinished = [this, entity]() {
        m_registry.removeComponent<engine::animation::BlockingAnimationComponent>(entity);
    };
    animationComponent.markers = animationMarkers;

    m_registry.addComponent<engine::animation::TransformAnimationComponent>(entity, animationComponent);
    m_registry.addComponent<engine::animation::BlockingAnimationComponent>(entity, engine::animation::BlockingAnimationComponent());
    // auto& transform = m_registry.getComponent<engine::TransformComponent>(entity);
    // transform.m_position = target;
}

std::optional<int> ludo::PiecePresenter::getPieceIndex(std::optional<engine::Entity>::value_type entity) {
    auto it = std::ranges::find(m_pieceEntities, entity);
    if (it == m_pieceEntities.end()) return std::nullopt;
    return static_cast<int>(std::distance(m_pieceEntities.begin(), it));
}


