#include "scheduling.hpp"
#include "omp.h"
#include <iostream>
#include <filesystem>

Scheduler::Scheduler(int numThreads, std::string outPutDir, bool recordEvolution) : numThreads(numThreads), outPutDir(outPutDir), recordEvolution(recordEvolution) {
    // Create output directory if it doesn't exist
    std::filesystem::create_directories(outPutDir);
}

void Scheduler::addRun(int size, double temperature, int cycles, bool randomInit, int N_duplicates) {
    std::uniform_int_distribution<> dist(0, 1000000);

    for (int i = 0; i < N_duplicates; ++i) {
        runQueue.push_back({size, temperature, cycles, randomInit, dist(seedGen), i});
    }
}

void Scheduler::executeRun(const RunConfig& config) {
    IsingModel model(config.size, config.temperature, config.randomInit, config.seed);
    model.run(config.cycles);

    double finalEnergy = model.calculateEnergy();
    double finalMagnetization = model.calculateMagnetization();

    std::string outputFileName = getOutputFileName(config, false);
    model.saveResults(outputFileName);
    if (recordEvolution) {
        std::string evolutionFileName = getOutputFileName(config, true);
        model.saveEvolution(evolutionFileName);
    }

    // Output results (could be saved to file or processed further)
    #pragma omp critical
    {
        std::cout << "Run (Size: " << config.size
                  << ", Temp: " << config.temperature
                  << ", Cycles: " << config.cycles
                  << ", Seed: " << config.seed << ") -> "
                  << "Final Energy: " << finalEnergy
                  << ", Final Magnetization: " << finalMagnetization << std::endl;
    }
}

std::string Scheduler::getOutputFileName(const RunConfig& config, bool isEvolution) const {
    return outPutDir + "/ising_L" + std::to_string(config.size) +
           (config.randomInit ? "_R" : "_O") +
           "_T" + std::to_string(config.temperature) +
           "_C" + std::to_string(config.cycles) +
           "_D" + std::to_string(config.duplicateID) + (isEvolution ? "_evolution.txt" : ".txt");
}

void Scheduler::start() {
    #pragma omp parallel for num_threads(numThreads)
    for (size_t i = 0; i < runQueue.size(); ++i) {
        executeRun(runQueue[i]);
    }
}