/*
 * eta_progress_bar.hpp
 *
 * A custom ProgressBar class to display a 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;
      _timer_flag = true;
    }
    
    ~ETAProgressBar() {
    }
    
    public: // ===== main methods =====
      
      void display() {
        REprintf("     0%%   10   20   30   40   50   60   70   80   90   100%%\n");
        REprintf("     [----|----|----|----|----|----|----|----|----|----]\n");
        flush_console();
      }
      
      // update display
      void update(float progress) {
        
        // stop if already finalized
        if (_finalized) return;
        
        // start time measurement when update() is called the first time
        if (_timer_flag) {
          _timer_flag = false;
          // measure start time
          time(&start);
        } else {
          
          // measure current time
          time(&end);
          
          // calculate passed time and remaining time (in seconds)
          double pas_time = std::difftime(end, start);
          double rem_time = (pas_time / progress) * (1 - progress);
          
          // convert seconds to time string
          std::string time_string = _time_to_string(rem_time);
          
          // create progress bar string 
          std::string progress_bar_string = _current_ticks_display(progress);
          
          // ensure overwriting of old time info
          int empty_length = time_string.length();
          std::string empty_space = std::string(empty_length, ' ');
          
          // merge progress bar and time string
          std::stringstream strs;
          // strs << "     |" << progress_bar_string << "| " << time_string << empty_space;
          strs << "     |" << progress_bar_string << "| " << empty_space;
          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);
          
          // finalize display when ready
          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 time_strs;
        if (hour != 0) time_strs << hour << "h ";
        if (min != 0) time_strs << min << "min ";
        if (sec != 0) time_strs << sec << "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;
      }
      
      // finalize
      void _finalize_display() {
        if (_finalized) return;
        
        REprintf("\n");
        flush_console();
        _finalized = true;
      }
      
      // compute number of ticks according to progress
      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;
      bool _timer_flag;
      time_t start,end;
          
};

#endif
