#ifndef displayMessage_hpp
#define displayMessage_hpp

#include <memory>
#include <string>
#include <map>
#include <chrono>
#include <fstream>
#include <thread>
#include <iostream>
#include <mutex>

#include "paramType.h"
#include "types.h"
#include "matrice.h"

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

class DisplayMessage {
private:
protected:
    std::uint_fast64_t __calls;
    std::mutex output_mutex;
    std::uint_fast64_t __output_every_sec;
public:
    DisplayMessage(std::uint_fast64_t output_every_sec = 10) : __output_every_sec(output_every_sec) {
        __calls = 0;
    }
    virtual ~DisplayMessage() {};
    virtual void Start() = 0;
    virtual void Stop() = 0;
    virtual void Display() = 0;
    virtual void Display(std::string) = 0;
    virtual std::string to_string() const {
        std::string result = "";
        result += "Calls: " + std::to_string(__calls);
        return result;
    }
    virtual std::uint_fast64_t OutputSeconds() const {
        return __output_every_sec;
    }
};

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

class DisplayMessageNull : public DisplayMessage {
public:
    DisplayMessageNull() : DisplayMessage() {};
    virtual void Display() {};
    virtual void Start() {};
    virtual void Stop() {};
    virtual void Display(std::string) {};

};

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

class DisplayMessageDimensionalityReductionCout : public DisplayMessage {
private:
    std::uint_fast64_t& __le_elaborate;
    std::uint_fast64_t __total_le;
    std::chrono::time_point<std::chrono::high_resolution_clock> __start_clock;
    std::chrono::time_point<std::chrono::high_resolution_clock> __last_output_clock;
    std::clock_t __start_tick;
public:
    DisplayMessageDimensionalityReductionCout(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) {

    }

    virtual void Display() {
        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) {
            Output();
        }

    };
    virtual void Start() {
        __start_tick = std::clock();
        __start_clock = std::chrono::high_resolution_clock::now();
        __last_output_clock = __start_clock;
    }
    virtual void Stop() {
        Output();
    };
    virtual void Display(std::string s) {
        // attenzione no thread safe
    };
private:
    void 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);
        std::cout << message << std::endl;
        __last_output_clock = std::chrono::high_resolution_clock::now();
    }
};


#endif /* displayMessage_hpp */
