/*
 * eta_progress_bar.hpp
 *
 * A custom ProgressBar class to display a vertical progress bar with time estimation
 *
 * Author: clemens@nevrome.de
 *
 */
#ifndef _RcppProgress_ETA_PROGRESS_BAR_HPP
#define _RcppProgress_ETA_PROGRESS_BAR_HPP

#include <R_ext/Print.h>
#include <ctime>
#include <stdio.h>
#include <sstream>
#include <string.h>

#include "progress_bar.hpp"

// for unices only
#if !defined(WIN32) && !defined(__WIN32) && !defined(__WIN32__)
#include <Rinterface.h>
#endif

class ETAProgressBar : public ProgressBar
{
public: // ====== LIFECYCLE =====
    /**
     * Main constructor
     */
    ETAProgressBar()
    {
        _max_ticks = 50;
        _finalized = false;
        _mode_flag = 1;
        _timer_flag = true;
    }

    ~ETAProgressBar()
    {
    }

public: // ===== main methods =====
    void display()
    {
        REprintf("0%%   10   20   30   40   50   60   70   80   90   100%%\n");
        REprintf("[----|----|----|----|----|----|----|----|----|----|  Remaining    (Elapsed)\n");
        flush_console();
    }

    // will finalize display if needed
    void update(float progress)
    {

        // stop if already finalized
        if (_finalized)
            return;

        if (_timer_flag)
        {
            _timer_flag = false;
            time(&start);
        }
        else
        {

            time(&end);

            double pas_time = std::difftime(end, start);
            double rem_time = (pas_time / progress) * (1 - progress);
            // int pas_time_simple = (int) pas_time;
            // int rem_time_simple = (int) rem_time;

            std::string rem = _time_to_string(rem_time);
            std::string pas = _time_to_string(pas_time);

            // create progress bar string
            std::string progress_bar_string = _current_ticks_display(progress);

            // merge progress bar and time string
            std::stringstream strs;
            strs << "|" << progress_bar_string << "| " << rem << " (" << pas << ")"
                 << "         ";
            std::string temp_str = strs.str();
            char const *char_type = temp_str.c_str();

            // print: remove old and replace with new
            REprintf("\r");
            REprintf("%s", char_type);

            if (progress == 1)
            {
                _finalize_display();
            }
        }
    }

    void end_display()
    {
        update(1);
    }

protected: // ==== other instance methods =====
    // convert double with seconds to time string
    std::string _time_to_string(double seconds)
    {

        int time = (int)seconds;

        int hour = 0;
        int min = 0;
        int sec = 0;

        hour = time / 3600;
        time = time % 3600;
        min = time / 60;
        time = time % 60;
        sec = time;

        std::stringstream sec_strs;
        std::stringstream min_strs;
        std::stringstream hour_strs;
        if(sec <= 9){
            sec_strs << "0" << sec;
        }else{
            sec_strs << sec;
        }
        if(min <= 9){
            min_strs << "0" << min;
        }else{
            min_strs << min;
        }            
        if(hour <= 9){
            hour_strs << "0" << hour;
        }else{
            hour_strs << hour;
        }           

        std::stringstream time_strs;
        time_strs << hour_strs.str() << "h" << min_strs.str() << "min" << sec_strs.str() << "s";
        std::string time_str = time_strs.str();

        return time_str;
    }

    // update the ticks display corresponding to progress
    std::string _current_ticks_display(float progress)
    {

        int nb_ticks = _compute_nb_ticks(progress);

        std::string cur_display = _construct_ticks_display_string(nb_ticks);

        return cur_display;
    }

    // construct progress bar display
    std::string _construct_ticks_display_string(int nb)
    {

        std::stringstream ticks_strs;
        for (int i = 0; i < (_max_ticks - 1); ++i)
        {
            if (i < nb)
            {
                ticks_strs << "*";
            }
            else
            {
                ticks_strs << " ";
            }
        }
        std::string tick_space_string = ticks_strs.str();

        return tick_space_string;
    }

    void _finalize_display()
    {
        if (_finalized)
            return;

        REprintf("\n");
        flush_console();
        _finalized = true;
    }

    int _compute_nb_ticks(float progress)
    {
        return int(progress * _max_ticks);
    }

    // N.B: does nothing on windows
    void flush_console()
    {
#if !defined(WIN32) && !defined(__WIN32) && !defined(__WIN32__)
        R_FlushConsole();
#endif
    }

private:            // ===== INSTANCE VARIABLES ====
    int _max_ticks; // the total number of ticks to print
    bool _finalized;
    int _mode_flag;
    bool _timer_flag;
    time_t start, end;
};

#endif
