//
// Created by m on 12/13/21.
//

#include "algorithmmanager.hpp"
#include "../engine/modules/logger.hpp"

#include <utility>
#include <fmt/format.h>

void AlgorithmManager::register_algorithm(std::string name, sol::function initialize, sol::function update,
                                          unsigned int updates_per_second) {
    unsigned int frames_per_update = target_fps / updates_per_second;
    DungeonAlgorithm algorithm = {
            frames_per_update,
            std::move(update),
            std::move(initialize),
            std::move(name)
    };
    algorithms.push_back(algorithm);

}

ranges::any_view<DungeonAlgorithm*, ranges::category::random_access | ranges::category::sized> AlgorithmManager::get_algorithms() {
    auto view = algorithms | ranges::view::transform([](auto& algo) {
        return &algo;
    });
    return view;
}

void AlgorithmManager::load_algorithm(DungeonAlgorithm *algorithm, int w, int h) {
    algorithm_done = false;
    ticks = 0;
    loaded_algorithm = algorithm;
    auto init_result = loaded_algorithm->initialize(w, h);
    if(!init_result.valid()) {
        sol::error err = init_result;
        Logger::log(LogLevel::ERROR, fmt::format("error while initializing lua: {}", err.what()));
    }
}

void AlgorithmManager::tick_algorithm() {
    if(this->loaded_algorithm == nullptr || algorithm_done) {
        return;
    }
    if(ticks >= this->loaded_algorithm->frames_per_update) {
        ticks = 0;
        auto update_result = this->loaded_algorithm->update();
        if(update_result.valid()) {
            algorithm_done = update_result;
            if(algorithm_done) {
                Logger::log(LogLevel::DEBUG, "Finished running algorithm");
            }
        }
        else {
            algorithm_done = true;
            sol::error err = update_result;
            Logger::log(LogLevel::ERROR, fmt::format("An error occurred while running update: {}", err.what()));
        }
    }
    ticks++;
}
