#include "gamecontext.h"
#include "game/classicarrownote.h"
#include "game/holdmanager.h"
#include "graphics/animations/classicanimationscenario.h"
#include "graphics/classicgraphicsmanager.h"

GameContext::GameContext(const std::shared_ptr<HoldManager>& hold_manager,
                         const std::shared_ptr<ClassicGraphicsManager>& graphics_manager) :
    _hold_manager(hold_manager),
    _graphics_manager(graphics_manager)
{}

void GameContext::input(ClassicArrowNote *note, kku::GameEvent&& input) const
{
    auto grade = ClassicArrowNote::Grade::BAD;
    auto& elements = note->getElements();

    bool input_valid = std::any_of(elements.begin(), elements.end(),
                [input=input](auto& element)
                {
                    if (element.pressed)
                        return false;

                    const auto code = std::get<kku::SystemEvent::Key>(input.event.data).view;
                    auto key_iterator = std::find(element.keys.begin(), element.keys.end(), code);

                    bool found_key = key_iterator != element.keys.end();
                    if (found_key)
                    {
                        element.pressed = true;
                        element.pressed_as = code;
                    }

                    return found_key;
                });

    bool all_pressed = std::all_of(elements.begin(), elements.end(),
                                   [](const auto& element)
                                   {
                                       return element.pressed;
                                   });

    if (all_pressed)
    {
        grade = note->calculatePrecision(input.timestamp);
        if (note->isHold())
            _hold_manager->emplace(note);
    }

    if (all_pressed || !input_valid)
    {
        note->setState(ClassicArrowNote::State::DYING);
        for (auto& element : elements)
            element.animations[note->getState()]->launch(element.sprite, input.timestamp, note->getPerfectOffset());
    }

    std::cout << "User input: " << static_cast<int>(grade) << "\n";
}

void GameContext::update(ClassicArrowNote *note, const kku::microsec& music_offset) const
{
    auto& elements = note->getElements();

    switch (note->getState())
    {
    default: return;
        break;

    case ClassicArrowNote::State::INITIAL:
        note->setState(ClassicArrowNote::State::FLYING);
        _graphics_manager->update(music_offset, note);
        break;

    case ClassicArrowNote::State::FLYING:
        if (!note->isActive(music_offset) && music_offset > note->getPerfectOffset())
        {
            note->setState(ClassicArrowNote::State::DYING);
            for (auto& element : elements)
                element.animations[note->getState()]->launch(element.sprite, music_offset, note->getPerfectOffset());
        }
        break;

    case ClassicArrowNote::State::DYING:
        if (elements[0].animations[note->getState()]->isDone())
            note->setState(ClassicArrowNote::State::DEAD);
        break;
    }

    for (auto& element : elements)
        if (element.animations[note->getState()])
            element.animations[note->getState()]->update(music_offset);
}

std::shared_ptr<HoldManager> GameContext::getHoldManager() const
{
    return _hold_manager;
}

std::shared_ptr<ClassicGraphicsManager> GameContext::getGraphicsManager() const
{
    return _graphics_manager;
}
