#include "xvt.h"
#include "xinclude.h"

#include <colors.h>
#include <diction.h>
#include <progind.h>  
#include <controls.h>
#include <reprint.h>
#include <urldefid.h>

int TIndwin::_indwin_count = 0;

word TIndwin::measure_text(TToken_string& s, word& maxlen) const
{
  word lines = 0;
  FOR_EACH_TOKEN(s, t)
  {
    const word l = strlen(t);
    if (l > maxlen) maxlen = l;
    lines++;
  }
  return lines;
}

// Certified 70%
TIndwin::TIndwin(long tot, const char* txt, bool cancel,  bool bar, int div)
       : _gauge(NULL_WIN), _can_cancel(cancel), _bar_top(0),
         _flags(0x0), _max(tot), _status(0L)
{
  TToken_string testo(txt, '\n');
  word maxlen = div;
  const word lines = measure_text(testo, maxlen);

  const int hor = min(maxlen+3, 78);
  const int ver = lines+3 + (bar ? 2 : 0);
  const int y = _indwin_count == 0 ? 3 : 12;

  long flags = WSF_INVISIBLE;
  if (_can_cancel)
    flags |= WSF_CLOSE;
  create(-1, y, hor, ver, TR("Elaborazione in corso"), flags, WD_MODAL);

  _bar_top = (lines+1)*ROWY;

  set_text(testo);

  if (bar)
  {
    XVT_COLOR_COMPONENT xcc[4]; 
    memset(xcc, 0, sizeof(xcc));
	  xcc[0].type = XVT_COLOR_BACKGROUND;
	  xcc[0].color = MASK_BACK_COLOR;
	  xcc[1].type = XVT_COLOR_BLEND;
	  xcc[1].color = BTN_LIGHT_COLOR;
	  xcc[2].type = XVT_COLOR_BORDER;
	  xcc[2].color = BTN_DARK_COLOR;

    WIN_DEF wd; memset(&wd, 0, sizeof(wd));
    wd.wtype = WC_HGAUGE;
    get_bar_rct(wd.rct);
    wd.ctlcolors = xcc;
    _gauge = xvt_ctl_create_def(&wd, win(), tot);
  }
   
  open_modal();

  _indwin_count++;
  setmax(tot);
}

// @doc EXTERNAL

// @mfunc Setta il testo della finestra
void TIndwin::set_text(
  const char* t) // @parm Testo della finestra

  // @comm Si puo' chiamare questa funzione per cambiare il testo, ma
  //       le dimensioni della finestra sono calcolate sul primo testo
  //       passato, quindi occorre dimensionare correttamente il primo passato
  //       (es. inserire degli spazi) quando se ne prevede uno piu' lungo.
{
  _text.destroy();
  TToken_string txt(t, '\n');
  FOR_EACH_TOKEN(txt, tok)
    _text.add(tok);

  RCT r; get_txt_rct(r);
  xvt_dwin_invalidate_rect(win(), &r);
}

void TIndwin::setmax(long m)
{
  _max = m <= 0 ? 1 : m;
  if (_gauge)
    xvt_sbar_set_range(_gauge, HVGAUGE, 0, _max);
  _start_time = clock();
}

TIndwin::~TIndwin()
{     
  if (is_open())
    close_modal(); 
  _indwin_count--;
}

bool TIndwin::can_be_closed() const
{
  const bool ok = (_flags & IND_FINISHED) || (_flags & IND_CANCELLED);
  if (!ok) error_box(TR("Attendere la fine dell'operazione prima di chiudere l'applicazione"));
  return ok;
}

KEY TIndwin::check_stop()
{
  KEY k = 0;
  if ((_flags & IND_FINISHED) || (_flags & IND_CANCELLED))
  {        
    k = (_flags & IND_FINISHED) ? K_ENTER : K_ESC;
    if (is_running())
      stop_run(k);
  }  
  return k;
}

void TIndwin::get_bar_rct(RCT& r) const
{
  xvt_vobj_get_client_rect(win(), &r);
  r.left += CHARX; 
  r.right -= CHARX;
  r.top = _bar_top; 
  r.bottom = r.top + ROWY;
}

void TIndwin::get_txt_rct(RCT& r) const
{
  get_bar_rct(r);
  r.bottom = r.top - ROWY/2;
  r.top = ROWY/2;
}

void TIndwin::sec2str(unsigned long ss, TString& str) const
{
  const unsigned long hh = ss / 3600;
  ss -= hh*3600;
  const unsigned long mm = ss / 60;
  ss -= mm *60;
  str.format("%02ld:%02ld:%02ld", hh, mm, ss);
}

void TIndwin::update_bar()
{
  if (_status >= _max)
  {
    _status = _max;
    _flags |= IND_FINISHED;
  }

  // Percentuale raggiunta finora
  const double prc = (double)_status / (double)_max;
  if (prc > 0)
  {
    const unsigned long elapsed_time = (clock() - _start_time)/CLOCKS_PER_SEC;
    const unsigned long total_time = (unsigned long)(elapsed_time / prc + 0.5);  
    TString16 str_ela, str_res, str_tot;
    sec2str(elapsed_time, str_ela);
    sec2str(total_time - elapsed_time, str_res);
    sec2str(total_time, str_tot);

    TString80 n; 
    n.format("%d%% - %s %s - %s %s - %s %s", int(prc*100.0+0.5),
             TR("Trascorso"), str_ela.get_buffer(), 
						 TR("Stimato"),   str_tot.get_buffer(),
             TR("Residuo"),   str_res.get_buffer()
             );
    RCT b; get_bar_rct(b);
    b.top = b.bottom+2; b.bottom = b.top + CHARY;

    WINDOW w = win();
    CBRUSH brush; brush.pat = PAT_SOLID; brush.color = MASK_BACK_COLOR;
    xvt_dwin_set_cbrush(w, &brush);
    xvt_dwin_set_std_cpen(w, TL_PEN_HOLLOW);
    xvt_dwin_draw_rect(w, &b);
    xvt_dwin_set_clip(w, NULL);
    set_color(NORMAL_COLOR, MASK_BACK_COLOR);
    set_opaque_text(TRUE);
    set_font();
    xvt_dwin_draw_text(w, b.left, b.bottom-1, n, -1);
  }
  check_stop();
}

void TIndwin::update_txt()
{
  RCT r; get_txt_rct(r);

  WINDOW w = win();
  CBRUSH brush; brush.pat = PAT_SOLID; brush.color = MASK_BACK_COLOR;
  xvt_dwin_set_cbrush(w, &brush);
  xvt_dwin_set_std_cpen(w, TL_PEN_HOLLOW);
  xvt_dwin_draw_rect(w, &r);
  xvt_dwin_set_clip(w, NULL);
  set_color(NORMAL_COLOR, MASK_BACK_COLOR);
  set_opaque_text(TRUE);
  set_font();
  advanced_draw_paragraph(w, _text, r, 'L', 'C', CHARY);
}

void TIndwin::update()
{
  clear(MASK_BACK_COLOR);

  if (_gauge != NULL_WIN) 
    update_bar();

  if (!_text.empty())
    update_txt();
} 

bool TIndwin::setstatus(long l)
{
  if (l < 0)
  {
    NFCHECK("Negative progind status");
    l = 0;
  }

  _status = l > _max ? _max : l;

  if (_gauge != NULL_WIN)
    xvt_sbar_set_pos(_gauge, HVGAUGE, _status);

  return !iscancelled();
}

bool TIndwin::on_key(KEY k)
{
  if (k == K_ESC && _can_cancel) 
  {
    _flags |= IND_CANCELLED;
    check_stop();
  }  
  return true; // Ignora tutti gli altri tasti, senza chiamare TWindow::on_key(k)
}

bool TIndwin::stop_run(KEY k)
{
  if (k == K_ESC)
  {
    if (_can_cancel)
      _flags |= IND_CANCELLED;
    else 
      return false;
  }
  return TWindow::stop_run(k);
}


// TProgind --------------------------------------------------------------

TProgind::TProgind(long max, const char* txt, bool cancel, bool bar, int div) 
        : TIndwin(max, txt, cancel, bar, div), _next_update(0)
{}

bool TProgind::setstatus(long l)
{ 
  const bool ok = TIndwin::setstatus(l);
  
  if (ok && clock() > _next_update)
  {
    update(); 
    do_events();
    _next_update = clock()+CLOCKS_PER_SEC; // Prossimo aggiornamento tra un secondo 
  }

  return ok;
}

// TTimerind ------------------------------------------------------------

long TTimerind::handler(WINDOW w, EVENT* e)
{
  switch(e->type)
  {
  case E_CREATE:
  case E_UPDATE:
    if (_timer_id == 0L && _interval > 0)
      _timer_id = xvt_timer_create(w, _interval);
    break;
  case E_TIMER:
    if (e->v.timer.id == _timer_id)
    {
      _status += _interval;
      force_update();
    }
    break;
  default:
    break;
  }
  return TIndwin::handler(w,e);
}

TTimerind::TTimerind(long msec, const char* txt, bool cancel, bool bar, int div, int i) 
         : TIndwin(msec, txt, cancel, bar, div), _timer_id(0), _interval(i)
{ }

TTimerind::~TTimerind()
{ 
  if (_timer_id)
    xvt_timer_destroy(_timer_id); 
}

// C-style binding
// uses static pointer for single instance of TIndwin

static TIndwin* __indwin__p = NULL;

void progind_create(long m, const char* t, bool b, bool c, int n)
{ 
  CHECK(__indwin__p == NULL, "Cannot have more than one progress indicator");
  __indwin__p = new TProgind(m,t,b,c,n);
}

bool progind_set_status(long l)
{
  return ((TProgind*)__indwin__p)->setstatus(l);
}

void progind_cancel()
{
  __indwin__p->cancel();
}

bool  progind_iscancelled()
{
  return __indwin__p->iscancelled();
}

bool  progind_isfinished()
{
  return __indwin__p->isfinished();
}

void progind_destroy()
{
  delete __indwin__p;
  __indwin__p = NULL;
}

void timerind_create(long l, const char* title, bool bar, bool cancel,
                     int divisions, int interval)
{
  CHECK(__indwin__p == NULL, "Cannot have more than one progress indicator");
  __indwin__p = new TTimerind(l,title,bar,cancel,divisions,interval);
}

void timerind_cancel()
{
  __indwin__p->cancel();
}

bool  timerind_iscancelled()
{
  return __indwin__p->iscancelled();
}

bool  timerind_isfinished()
{
  return __indwin__p->isfinished();
}

void timerind_destroy()
{
  delete __indwin__p;
  __indwin__p = NULL;
}