//
// Created by jholder on 16.12.21.
//
#include "konvergenz_runner.h"
#include <fstream>
#include <iostream>
#include <array>
#include "force_lambdas.h"
#include <indicators/progress_bar.hpp>


void Konvergenz::konvergenz(Compute::Force force, Compute::Type type,
                            const Integratoren2d::integrator_t& integrator,
                            size_t every, std::ofstream& file) {
    Integratoren2d::force_t force_lambda;
    switch (force) {
        case Compute::Force::zero_F:
            force_lambda = zero_Force;
            break;
        case Compute::Force::const_F:
            force_lambda = const_Force;
            break;
        case Compute::Force::harmonic_F:
            force_lambda = harmonic_Force;
            break;
    }
    double delta_t = 1.0 / static_cast<double>(every);
    Rod2d rod(1.0);
    Simulation sim(delta_t, 1234);
    Compute com(rod, type, force, every, sim);

    while (true) {
        for (int i = 0; i < 500000; ++i) {
            integrator(rod, sim, force_lambda, zero_Torque);
            com.eval(rod);
        }
        std::cout << every << " " << com.getDifference() << " "
                  << com.getAgg().getSEM() << "                       \r";
        if (com.getDifference() >= com.getAgg().getSEM() * 20 and
            com.getAgg().getSEM() < 0.01) {
            std::cout << "\n";
            file << delta_t << " " << com.getDifference() << " "
                 << com.getAgg().getSEM() << '\n';
            file.flush();
            break;
        }
    }
}
void Konvergenz::runner(Compute::Force force, Compute::Type type,
                        const Integratoren2d::integrator_t& integrator,
                        std::string filename) {
    std::ofstream file;
    file.open("out/"+filename);
    for (size_t i = 1; i < 100; i *= 2) {
        konvergenz(force, type, integrator, i, file);
    }
}
const std::array<std::string,6> names = {"oaf","msd","empxx","empyy", "x","x_squared"};
const std::array<Compute::Type,6> types = { Compute::Type::oaf , Compute::Type::msd , Compute::Type::empxx , Compute::Type::empyy ,  Compute::Type::x , Compute::Type::x_squared };

void Normal::runner(Compute::Force force,
                    const Integratoren2d::integrator_t& integrator,
                    const std::string& filename, double length) {
    std::array<std::ofstream,6> files;
    size_t counter = 0;
    for (auto &file:files) {
        file.open("out/"+names[counter]+'_'+filename);
        counter++;
    }

    time(force, integrator, files, length);

}
void Normal::time(Compute::Force force,
                  const Integratoren2d::integrator_t& integrator,
                  std::array<std::ofstream,6>& files, double length) {
    Integratoren2d::force_t force_lambda;
    switch (force) {
        case Compute::Force::zero_F:
            force_lambda = zero_Force;
            break;
        case Compute::Force::const_F:
            force_lambda = const_Force;
            break;
        case Compute::Force::harmonic_F:
            force_lambda = harmonic_Force;
            break;
    }
    constexpr double delta_t = 0.05;
    constexpr size_t max_count = 21;
    Rod2d rod(length);
    const Rod2d start_rod(length);
    Simulation sim(delta_t, 1234);
    std::vector<Compute> computes;
    computes.reserve(100);
    for (size_t count = 1; count <= max_count; count++) {
        computes.emplace_back(rod, Compute::Type::oaf, force, count, sim);
        computes.emplace_back(rod, Compute::Type::msd, force, count, sim);
        computes.emplace_back(rod, Compute::Type::empxx, force, count, sim);
        computes.emplace_back(rod, Compute::Type::empyy, force, count, sim);
        computes.emplace_back(rod, Compute::Type::x, force, count, sim);
        computes.emplace_back(rod, Compute::Type::x_squared, force, count, sim);
    }


    using namespace indicators;
    ProgressBar bar{
        option::BarWidth{50},
        option::Start{"["},
        option::Fill{"="},
        option::Lead{">"},
        option::Remainder{" "},
        option::End{"]"},
        option::PostfixText{"Calculating"},
        option::ShowElapsedTime{true},
        option::ShowRemainingTime{true},
        option::ForegroundColor{Color::green},
        option::FontStyles{std::vector<FontStyle>{FontStyle::bold}}
    };
    constexpr size_t num_runs = 200000;
    for (size_t run_index = 0; run_index < num_runs; run_index++) {
        bar.set_progress(run_index *100/ num_runs);
        for (size_t iter = 0; iter < max_count*10; ++iter) {
            integrator(rod, sim, force_lambda, zero_Torque);
            for (auto& com : computes) {
                com.eval(rod);
            }
        }
        for (auto& com : computes) {
            com.reset(start_rod);
            rod = start_rod;

        }
    }
    size_t counter = 0;
    for (auto &file:files) {
        file<<"#time mean error target npoints\n";
        for(auto & com: computes){
            if (com.getType() == types[counter]) {
                file<<com.getTime()<<" "<<com.getAgg().getMean()<<" "<<com.getAgg().getSEM()<<" "<<com.getTarget()<<" "<<com.getAgg().getNumPoints()<<'\n';
            }
        }
        counter++;
    }

    bar.mark_as_completed();
}
