#include "rDisplayUtils.h"
#ifndef R_NO_REMAP
#define R_NO_REMAP
#endif
#include <Rinternals.h>
#include <R.h>

//************************************
//************************************
//************************************

DisplayMessageEvaluationR::DisplayMessageEvaluationR(
                          std::uint_fast64_t& total_number_of_extension,
                          std::uint_fast64_t& extension_count,
                          std::uint_fast64_t output_every_sec) : DisplayMessage(output_every_sec), __extension_count(extension_count),
__total_number_of_extensions(total_number_of_extension)
{ }

//************************************
//************************************
//************************************

void DisplayMessageEvaluationR::Start() {
    __start_clock = std::chrono::high_resolution_clock::now();
    __last_output_clock = __start_clock;
}

//************************************
//************************************
//************************************

void DisplayMessageEvaluationR::Stop() {
    Output();
};

//************************************
//************************************
//************************************

void DisplayMessageEvaluationR::Display() {
    std::uint_fast64_t inteval_clock = (std::uint_fast64_t) (std::chrono::duration_cast<std::chrono::seconds>(std::chrono::high_resolution_clock::now() - __last_output_clock).count());
    if ((__last_output_clock == __start_clock && __total_number_of_extensions != std::numeric_limits<std::uint_fast64_t>::max()) || inteval_clock > __output_every_sec) {
        Output();
    }
}

//************************************
//************************************
//************************************

void DisplayMessageEvaluationR::Display(std::string s) {
    std::lock_guard<std::mutex> guard(output_mutex);
    Rprintf("%s\n", s.c_str());
}

//************************************
//************************************
//************************************

void DisplayMessageEvaluationR::Output() {
    std::uint_fast64_t seconds_clock = (std::uint_fast64_t) (std::chrono::duration_cast<std::chrono::seconds>(std::chrono::high_resolution_clock::now() - __start_clock).count());
    std::uint_fast64_t days_clock = seconds_clock / (24 * 3600);
    std::uint_fast64_t days_remainder_clock = seconds_clock % (24 * 3600);
    std::uint_fast64_t hours_clock = days_remainder_clock / 3600;
    std::uint_fast64_t hours_remainder_clock = hours_clock % 3600;
    std::uint_fast64_t minutes_clock = hours_remainder_clock / 60;
    
    std::string message = "In " + std::to_string(days_clock) + "d " + std::to_string(hours_clock) + "h " + std::to_string(minutes_clock) + "min";
    if (days_clock == 0) {
        message = "In " + std::to_string(hours_clock) + "h " + std::to_string(minutes_clock) + "min";
        if (hours_clock == 0) {
            message = "In " + std::to_string(minutes_clock) + "min";
        }
    }
    
    if (__total_number_of_extensions != std::numeric_limits<std::uint_fast64_t>::max()) {
        message += " " + std::to_string(__extension_count) + " of " + std::to_string(__total_number_of_extensions);
    } else {
        message += " " + std::to_string(__extension_count);
    }
    message += " linear extensions analized.";
    {
        std::lock_guard<std::mutex> guard(output_mutex);
        Rprintf("%s\n", message.c_str());
        //R_FlushConsole();
    }
    __last_output_clock = std::chrono::high_resolution_clock::now();
}

//************************************
//************************************
//************************************

void DisplayMessageLEGetR::Display() {
    if (__expected_output) {
        std::uint_fast64_t seconds_tick = (std::uint_fast64_t) (std::max((std::uint_fast64_t) ((((double) std::clock()) - ((double) __start_tick)) / CLOCKS_PER_SEC), (std::uint_fast64_t) 0));
        std::uint_fast64_t seconds_clock = (std::uint_fast64_t) (std::chrono::duration_cast<std::chrono::seconds>(std::chrono::high_resolution_clock::now() - __start_clock).count());
        if (seconds_clock > EXPECTED_TIME_SEC) {
            std::uint_fast64_t etimesec = (__total_number_of_extensions / std::max(__extension_count, (std::uint_fast64_t) 1)) * seconds_tick;
            std::uint_fast64_t days = etimesec / (24 * 3600);
            std::uint_fast64_t days_remainder = etimesec % (24 * 3600);
            std::uint_fast64_t hours = days_remainder / 3600;
            std::uint_fast64_t hours_remainder = hours % 3600;
            std::uint_fast64_t minutes = hours_remainder / 60;
            
            std::uint_fast64_t etimesec_wall = (__total_number_of_extensions / std::max(__extension_count, (std::uint_fast64_t) 1)) * seconds_clock;
            std::uint_fast64_t days_wall = etimesec_wall / (24 * 3600);
            std::uint_fast64_t days_remainder_wall = etimesec_wall % (24 * 3600);
            std::uint_fast64_t hours_wall = days_remainder_wall / 3600;
            std::uint_fast64_t hours_remainder_wall = hours_wall % 3600;
            std::uint_fast64_t minutes_wall = hours_remainder_wall / 60;
            
            std::string message = std::to_string(days) + "d " + std::to_string(hours) + "h " + std::to_string(minutes) + "min";
            if (days == 0) {
                message = std::to_string(hours) + "h " + std::to_string(minutes) + "min";
                if (hours == 0) {
                    message = std::to_string(minutes) + "min";
                }
            }
            message = "Expected cpu time: " + message;
            
            std::string message_wall = std::to_string(days_wall) + "d " + std::to_string(hours_wall) + "h " + std::to_string(minutes_wall) + "min";
            if (days_wall == 0) {
                message_wall = std::to_string(hours_wall) + "h " + std::to_string(minutes_wall) + "min";
                if (hours_wall == 0) {
                    message_wall = std::to_string(minutes_wall) + "min";
                }
            }
            message_wall = "Expected elapsed time: " + message_wall;
            {
                std::lock_guard<std::mutex> guard(output_mutex);
                Rprintf("%s\n", message.c_str());
                Rprintf("%s\n", message_wall.c_str());
                //R_FlushConsole();
            }
            
            __expected_output = false;
        }
    }
    std::uint_fast64_t clock_check = (std::uint_fast64_t) (std::chrono::duration_cast<std::chrono::seconds>(std::chrono::high_resolution_clock::now() - __last_output_clock).count());
    if (clock_check > __output_every_sec && __expected_output == false) {
        std::string message = std::to_string(__extension_count) + " out of " + std::to_string(__total_number_of_extensions) + " (" + std::to_string((std::uint_fast64_t) ((((double) __extension_count) / __total_number_of_extensions) * 100)) + "%) linear extentions generated.";
        if (__total_number_of_extensions == std::numeric_limits<std::uint_fast64_t>::max()) {
            message = std::to_string(__extension_count) + " linear extentions generated.";
        }
        {
            std::lock_guard<std::mutex> guard(output_mutex);
            Rprintf("%s\n", message.c_str());
            //R_FlushConsole();
        }
        __last_output_clock = std::chrono::high_resolution_clock::now();
    }
}
   
//************************************
//************************************
//************************************

void DisplayMessageLEGetR::Display(std::string s) {
    std::lock_guard<std::mutex> guard(output_mutex);
    Rprintf("%s\n", s.c_str());
}

//************************************
//************************************
//************************************

DisplayMessageDimensionalityReductionR::DisplayMessageDimensionalityReductionR(std::uint_fast64_t& le_elaborate,
                                                                               std::uint_fast64_t total_le,
                                                                               std::uint_fast64_t output_every_sec = 10) : DisplayMessage(output_every_sec),
__le_elaborate(le_elaborate), __total_le(total_le) {
    __started = false;
}

//************************************
//************************************
//************************************


void DisplayMessageDimensionalityReductionR::Display(std::string s) {
    std::lock_guard<std::mutex> guard(output_mutex);
    Rprintf("%s\n", s.c_str());
}

//************************************
//************************************
//************************************

void DisplayMessageDimensionalityReductionR::Display() {
    if (!__started) return;
    
    std::lock_guard<std::mutex> guard(output_mutex);
    std::uint_fast64_t clock_check = (std::uint_fast64_t) (std::chrono::duration_cast<std::chrono::seconds>(std::chrono::high_resolution_clock::now() - __last_output_clock).count());
    //if (__last_output_clock == __start_clock || clock_check > __output_every_sec) {
    if (clock_check > __output_every_sec) {
        Output();
    }
}



//************************************
//************************************
//************************************

void DisplayMessageDimensionalityReductionR::Output() {
    std::string message = std::to_string(__le_elaborate) + " out of " + std::to_string(__total_le) + " (" + std::to_string((std::uint_fast64_t) ((((double) __le_elaborate) / __total_le) * 100)) + "%) linear extentions used.";
    //std::lock_guard<std::mutex> guard(output_mutex);
    Rprintf("%s\n", message.c_str());
    //R_FlushConsole();
    __last_output_clock = std::chrono::high_resolution_clock::now();
}
