// ========================================================================== //
// Class inclusion
#include "Timer.hpp"

// Standard libraries
#include <cmath>
#include <iostream>
#include <string>
#include <vector>

// Parallelisation
#include <omp.h>
#if USE_MPI == 1
    #include <mpi.h>
#endif

// Macros
#ifndef MPI_VERSION
    #define MPI_VERSION 0
#endif

// ========================================================================== //
// Main codes

Timer::Timer() {
    labels.push_back("start");
    times.push_back(omp_get_wtime());
}
Timer::~Timer() {}

void Timer::add(std::string label, bool mpi_sync) {

#if MPI_VERSION > 0
    // Sync up when running on MPI
    if (mpi_sync)
        MPI_Barrier(MPI_COMM_WORLD);
#endif

    labels.push_back(label);
    times.push_back(omp_get_wtime());
}

void Timer::print(int precision) {

#if MPI_VERSION > 0
    int rank;
    MPI_Comm_rank(MPI_COMM_WORLD, &rank);
    if (rank != 0)
        return;
#endif

    // Determine the maximal string length
    int string_size = 1;
    for (auto label : labels)
        string_size = std::max(string_size, (int)label.size() + 1);

    // ====================================================================== //
    // Wall time

    // Total time spent in seconds
    int total_time = (int)(times.back() - times.front()) + 1, used = 0;

    // Compute number of each temporal unit
    int days = (total_time - used) / (60 * 60 * 24);
    used += days * 60 * 60 * 24;
    int hours = (total_time - used) / (60 * 60);
    used += hours * 60 * 60;
    int minutes = (total_time - used) / 60;
    used += minutes * 60;
    int seconds = (total_time - used);

    // Determine number of temporal units needed
    int level = 0;
    level += days > 0;
    level += hours > 0;
    level += minutes > 0;
    level += seconds > 0;

    // Print total walltime spent
    printf("\n%-*s: ", string_size + 2, "Wall-time");
    std::string time_spent;
    switch (level) {
        case 3: printf("%02dd:", days); level--;
        case 2: printf("%02dh:", hours); level--;
        case 1: printf("%02dm:", minutes); level--;
        case 0: printf("%02ds\n", seconds); level--;
    }

    // ====================================================================== //
    // List the time stamps with labels

    // Determine length and precision needed for times.
    int double_length =
        (int)log10(times.back() - times.front()) + 1 + precision + 1;

    // Individual time stamps
    for (size_t idx = 1; idx < times.size(); idx++) {
        double time = times[idx] - times[idx - 1];
        printf("  %-*s: %*.*f\n", string_size, labels[idx].c_str(),
               double_length, precision, time);
    }
    // Print the total time spent
    printf("  %-*s: %*.*f\n", string_size, "Total", double_length, precision,
           times.back() - times.front());
}

// End of File
// ========================================================================== //