#include <config.h>
#include <prefix.h>
#include <progind.h>
#include <tabutil.h>
#include <xvtility.h>
#include <defmask.h>
#include <image.h>
#include <utility.h> // file functions

#include "mrplib.h"

///////////////////////////////////////////////////////////
// TMRP_time
//////////////////////////////////////////////////////////
TAssoc_array TMRP_time::_frate_indovino;

TMRP_calendar& TMRP_time::get_calendar(const char* codimp,const char* codlin)
{
  TToken_string k;
  k.add(codimp);
  k.add(codlin);
  TMRP_calendar* cal = (TMRP_calendar*)_frate_indovino.objptr(k);
  if (cal == NULL)
  {
    if (_frate_indovino.items() > 16) 
      _frate_indovino.destroy(); // troppi calendari fanno male alla salute...
    
    cal = new TMRP_calendar(codlin, codimp);
    _frate_indovino.add(k, cal);
  }
  return *cal;
}

TMRP_calendar& TMRP_time::calendario()
{
  return TMRP_time::get_calendar(_imp, _lin);
}

int TMRP_time::compare(const TSortable& s) const
{
  const TMRP_time& t = (const TMRP_time&)s;
  int res;
  if (_date == t._date)
    res = _hour - t._hour;
  else
    res = _date > t._date ? +1 : -1;
  return res;
}

TMRP_time& TMRP_time::add_time(int days, long hours,bool macchina)
{
  TMRP_calendar& cal = calendario();
  if (days != 0)
  {
    cal.next_working_day(_date, days);
    _hour = 0;
  }
  while (hours != 0)
  {
    real junk;
    const int ore = macchina ? cal.add_oremacchina(junk, _date):cal.add_oreuomo(junk, _date);
    const long tot = _hour + hours;
    if (tot < 0 || tot > ore)
    {
      if (hours >= 0)
      {
        cal.next_working_day(_date);
        hours -= ore - _hour;
        _hour = 0;
      }
      else
      {
        cal.prev_working_day(_date);
        hours += _hour;
        _hour = (macchina ? cal.add_oremacchina(junk, _date) : cal.add_oreuomo(junk,_date));
      }
    }
    else
    {
      _hour = int(tot);
      hours = 0;
    }
  }
  return *this;
}

void TMRP_time::set(const TDate& d, int h, const char* imp, const char* lin)
{
  _date = d;
  _hour = h;
  _imp = imp;
  _lin = lin;
}

TMRP_time::TMRP_time() : _hour(0)
{ }

TMRP_time::TMRP_time(const TDate& d, int h, const char* imp, const char* lin)
: _date(d), _hour(h), _imp(imp), _lin(lin)
{ }

///////////////////////////////////////////////////////////
// TMRP_array
///////////////////////////////////////////////////////////

TSortable* TMRP_array::add_obj(const TToken_string& key)
{
  TSortable* obj = (TSortable*)_by_key.objptr(key);
  if (obj == NULL)
  {
    obj = new_obj(key);
    _by_key.add(key, obj);
    _by_idx.add(obj);
  }
  return obj;
}

void TMRP_array::add(TMRP_array &a, bool force)
{
  TString_array keys;
  a._by_key.get_keys(keys);
  for (int o=keys.items()-1; o >= 0; o--)
  {
    TString & key = keys.row(o);
    TSortable* obj = (TSortable*)a._by_key.objptr(key)->dup();
    TSortable* oldobj = (TSortable*)_by_key.objptr(key);
    if (oldobj == NULL || force)
    {
      _by_key.add(key, obj);
      _by_idx.add(obj);
    }
  }
}

TMRP_array & TMRP_array::operator=(const TMRP_array& a)
{
  _by_key=a._by_key;
  _by_idx=a._by_idx;
  return *this;
}

TMRP_array::TMRP_array(const TMRP_array& a)
{
  *this = TMRP_array::operator=(a);
}

// Ordina gli articoli secondo il noto algoritmo Fantini-Truffelli
// per array di oggetti su cui non e' definito un ordinamento completo
long TMRP_array::sort()
{
  const long last = items()-1;

  TProgind* pi = NULL;
  if (last >= 16)
    pi = new TProgind(last, "Ordinamento", FALSE, TRUE);
  else
    begin_wait();

  for (long i = 0; i < last; i++)
  {
    if (pi) pi->addstatus(1);
    TSortable* best = &find_obj(i);
    bool swapped = TRUE;
    while (swapped)
    {
      swapped = FALSE;
      for (long j = i+1; j <= last; j++)
      {
        TSortable& other = find_obj(j);
        if (other.compare(*best) > 0)
        {
//          swap(i, j);
//          best = &other;
          best = (TSortable*)_by_idx.remove(int(j), TRUE);
          _by_idx.TArray::insert(best, int(i));
          swapped = TRUE; 
        }
      }
    }
  }

  if (pi)
    delete pi;
  else
    end_wait();

  return last+1;
}

void TMRP_array::destroy()
{
  _by_key.destroy();
  _by_idx.destroy();
}

///////////////////////////////////////////////////////////
// TMRP_calendar
///////////////////////////////////////////////////////////

TString16 TMRP_calendar::_days;
TToken_string TMRP_calendar::_holidays;

void TMRP_calendar::init_default()
{
  TMRP_config cfg;
  // Inizializza i turni dei 5 giorni feriali a 1
  // Inizializza i turni di sabato e domenica a 0
  // Inizializza i turni dei giorni festivi   a 0
  _days = cfg.get("Turni", NULL, -1, "1111100011111000");
  // Inizializza la lista delle feste comandate tranne la Pasqua
  _holidays = cfg.get("Feste", NULL, -1, 
                      "01-01|06-01|25-04|01-05|15-08|01-11|08-12|25-12|26-12");
  TUnita_produttiva * up=NULL;
  if (_codlin.not_empty())
    up=new TLinea_prod(_codlin);
  else if (_codimp.not_empty())
    up=new TImpianto(_codimp);
  
  for (int t = 0; t < 8; t++)
  {
    if (up)
    { 
      // turni di linea
      _lungturno[t] = up->durata_turno(t);
      _inizturno[t] = up->inizio_turno(t);
      _persturno[t] = up->numpers_turno(t);
    } 
    else
    { 
      // turni di ditta
      _lungturno[t] = cfg.durata_turno(t);
      _inizturno[t] = cfg.inizio_turno(t);
      _persturno[t] = cfg.numpers_turno(t);
    } 
  }
}

bool TMRP_calendar::is_holiday(int g, int m) const
{
  if (_days.empty())
    ((TMRP_calendar*)this)->init_default();
  TString16 str; str.format("%02d-%02d", g, m);
  return _holidays.find(str) >= 0;
}

bool TMRP_calendar::is_holiday(const TDate& date) const
{
  return is_holiday(date.day(), date.month());
}

bool TMRP_calendar::is_red(const TDate& date) const
{
  if (date.wday() == 7)
    return TRUE;
  return is_holiday(date);
}

void TMRP_calendar::declare_holiday(int g, int m)
{
  if (!is_holiday(g, m))
  {
    TString16 str; str.format("%02d-%02d", g, m);
    _holidays.add(str);
  }
}

void TMRP_calendar::suppress_holiday(int g, int m)
{
  if (is_holiday(g, m))
  {
    TString16 str; str.format("%02d-%02d", g, m);
    const int pos = _holidays.find(str);
    const TString tail = _holidays.mid(pos+6, -1);
    _holidays.cut(pos);
    _holidays << tail;
  }
}

void TMRP_calendar::read_cal(const TString& key, char tipo)
{
  TTable cal(tipo == 'L' ? "CAL" : "CAI");
  TString16 codtab = tipo == 'L' ? _codlin : _codimp;
  codtab << key;
  cal.put("CODTAB", codtab);
  TString* s1 = new TString(62);
  if (cal.read() == NOERR)
    *s1 = cal.get("S1");
  const int l1 = s1->len();
  if (l1 < s1->size())
  {
    const TString spc(s1->size()-l1, ' ');
    *s1 << spc;
  }
  if (tipo == 'L')
    _exc_lin.add(key, s1);
  else
    _exc_imp.add(key, s1);
}

int TMRP_calendar::write_cal(char tipo) const
{
  int err = NOERR;
  TTable cal((tipo == 'L') ? "CAL" : "CAI");
  TAssoc_array& ass = tipo == 'L' ? ((TMRP_calendar*)this)->_exc_lin 
                                  : ((TMRP_calendar*)this)->_exc_imp;   
  TString codtab, s0;
  FOR_EACH_ASSOC_STRING(ass, hash, key, str)
  {
    codtab = tipo == 'L' ? _codlin : _codimp; 
    codtab.left_just(5); codtab << key;
    cal.zero();
    cal.put("CODTAB", codtab);
    const TFixed_string s1(str);
    if (!s1.blank())
    {
      const int anno = atoi(codtab.mid(5, 4));
      const int mese = atoi(codtab.mid(9, 2));
      if (tipo == 'L')
      {
        s0 = "Linea "; 
        s0 << _codlin;
      }
      else
      {
        s0 =  "Impianto "; 
        s0 << _codimp;
      }
      s0 << ' ' << anno << ' ' << itom(mese);
      cal.put("S0", s0);
      cal.put("S1", s1);
      err = cal.write();
      if (err != NOERR)
        err = cal.rewrite();
      if (err != NOERR)
      {
        error_box("Errore %d nell'aggiornamento del calendario %s", 
                  err, (const char*)codtab);
        break;
      }
    }
    else
      err = cal.remove();
  }
  return err;
}

int TMRP_calendar::remove_cal(const TString& code, char tipo) const
{
  TTable cal(tipo == 'L' ? "CAL" : "CAI");
  TString16 codtab = code; codtab.left_just(5);
  cal.put("CODTAB", codtab);
  for (int err = cal.read(_isgteq); err == NOERR; err = cal.next())
  {
    if (codtab == cal.get("CODTAB").left(5))
      cal.remove();
    else
      break;
  }
  return NOERR;
}

int TMRP_calendar::turni(const TDate& data, int& mini, int& maxi, bool as_is)
{
  const int year  = data.year();
  const int month = data.month();
  const int day   = data.day(); 
  TString16 str;

  mini = maxi = -1;
  if (_codlin.not_empty())
  {
    str.format("%04d%02d", year, month);
    if (_exc_lin.objptr(str) == NULL)
      read_cal(str, 'L');

    const TString& turn = (const TString&)_exc_lin[str];
    mini = turn[day-1] - '0';
    maxi = turn[day+30] - '0';
    if (as_is || (mini >= 0 && maxi >= 0))
      return 3;
  }

  if (_codimp.not_empty())
  {
    str.format("%04d%02d", year, month);
    if (_exc_imp.objptr(str) == NULL)
      read_cal(str, 'I');

    const TString& turn = (const TString&)_exc_imp[str];
    mini = turn[day-1] - '0';
    maxi = turn[day+30] - '0';

    if (as_is || (mini >= 0 && maxi >= 0))
      return 2;
  }

  if (_days.empty())
    init_default();

  // Controlla se e' festa
  for (str = _holidays.get(0); str.not_empty(); str = _holidays.get())
  {
    int g,m,a;
    str << '-' << year;
    if (sscanf(str, "%d-%d-%d", &g, &m, &a) == 3)
    {
      const TDate festa(g, m, a);
      if (data == festa)
      {
        if (mini < 0) mini = _days[7] - '0';
        if (maxi < 0) maxi = _days[15] - '0';
        break;
      }
    }
    if (mini >= 0 && maxi >= 0)
      return 1;
  }

  // Usa giorni default
  const int giorno = data.wday() - 1;
  if (mini < 0) mini = _days[giorno] - '0';
  if (maxi < 0) maxi = _days[giorno+8] - '0';
  return 0;
}

int TMRP_calendar::set_turni(const TDate& data, int mini, int maxi)
{
  const int year  = data.year();
  const int month = data.month();
  const int day   = data.day(); 
  TString16 str;

  if (_codlin.not_empty())
  {
    str.format("%04d%02d", year, month);
    if (_exc_lin.objptr(str) == NULL)
      read_cal(str, 'L');
    TString& turn = (TString&)_exc_lin[str];
   
    if (mini >= 0 && mini <= 9)
      turn[day-1] = '0' + mini;
    else
      turn[day-1] = ' ';
    if (maxi >= 0 && maxi <= 9)
      turn[day+30] = '0' + maxi;
    else
      turn[day+30] = ' ';
    return 3;
  }

  if (_codimp.not_empty())
  {
    str.format("%04d%02d", year, month);
    if (_exc_imp.objptr(str) == NULL)
      read_cal(str, 'I');
    TString& turn = (TString&)_exc_imp[str];
    if (mini >= 0 && mini <= 9)
      turn[day-1] = '0' + mini;
    else
      turn[day-1] = ' ';
    if (maxi >= 0 && maxi <= 9)
      turn[day+30] = '0' + maxi;
    else
      turn[day+30] = ' ';
    return 2;
  }

  // Controlla se e' festa
  for (str = _holidays.get(0); str.not_empty(); str = _holidays.get())
  {
    int g,m,a;
    str << '-' << year;
    if (sscanf(str, "%d-%d-%d", &g, &m, &a) == 3)
    {
      const TDate festa(g, m, a);
      if (data == festa)
      {
        if (mini >= 0 && mini <= 9)
          _days[7] = '0' + mini;
        else
          _days[7] = ' ';
        if (maxi >= 0 && maxi <= 9)
          _days[15] = '0' + maxi;
        else
          _days[15] = ' ';

        return 1;
      }
    }
  } 

  const int giorno = data.wday() - 1;
  
  if (mini >= 0 && mini <= 9)
    _days[giorno] = '0' + mini;
  else
    _days[giorno] = '0';
  
  if (maxi >= 0 && maxi <= 9)
    _days[giorno+8] = '0' + maxi;
  else
    _days[giorno+8] = '0';

  return 0;
}

int TMRP_calendar::add_oreuomo(real & var,const TDate& date, bool max)
{
  long minuti = 0L;
  const int tm = max ? turni_max(date) : turni_min(date);
  for (int t = tm-1; t >= 0; t--)
    minuti += _lungturno[t] * _persturno[t];
  const int ore = int(minuti / 60L);  
  var += ore;
  return ore;
}

int TMRP_calendar::add_oreuomo_max(real & var,const TDate& date)
{
  return add_oreuomo(var, date,TRUE);
}

int TMRP_calendar::add_oremacchina(real & var,const TDate& date, bool max)
{
  const int tm = max ? turni_max(date) : turni_min(date);
  real minuti(ZERO);
  
  for (int t = tm-1; t >= 0; t--)
  {
    int lungturno = _lungturno[t];
    const int start=_inizturno[t];
    const int end=_inizturno[t]+_lungturno[t]-1;
    for (int pt = t-1; lungturno>0 && pt >= 0; pt--)
    {
      int overlap ;
      if (_inizturno[pt]<start)
      {
        // OK 100%
        if (_inizturno[pt] + _lungturno[pt] > end)
          overlap = end + 1 - start ;
        else
          overlap = _inizturno[pt] + _lungturno[pt] -1 - start;
      }
      else
      {
        if (end > _inizturno[pt]+_lungturno[pt])
          overlap = _lungturno[pt];
        else
          overlap = end + 1 - _inizturno[pt] ;
      }
      if (overlap >= 0)
        lungturno -= overlap;
    }
    minuti += lungturno;
  }
  minuti /= 60.0;
  var +=(minuti);
  return int(minuti.integer());
}

int TMRP_calendar::add_oremacchina_max(real & var,const TDate& date)
{
  return add_oremacchina(var, date,TRUE);
}

int TMRP_calendar::write() const
{
  if (!_codlin.blank())
    return write_cal('L');

  if (!_codimp.blank())
    return write_cal('I');

  if (_days.not_empty())
  {
    TConfig cfg(CONFIG_DITTA, "mr");
    cfg.set("Turni", _days);
    cfg.set("Feste", _holidays);
  }
  
  return NOERR;
}

int TMRP_calendar::remove() const
{
  if (_codlin.not_empty())
    return remove_cal(_codlin, 'L');
  if (_codimp.not_empty())
    return remove_cal(_codimp, 'I');
  return NOERR;
}

void TMRP_calendar::set(const char* linea, const char* impianto)
{
  _exc_lin.destroy();
  _exc_imp.destroy();
   
  _codlin = linea;    _codlin.rpad(5);
  _codimp = impianto; _codimp.rpad(5);

  if (_codlin.not_empty())
  {
    TTable lnp("LNP");
    lnp.put("CODTAB", _codlin);
    if (lnp.read() == NOERR)
    {
      if (impianto == NULL)
        _codimp = lnp.get("S6");
    }
    else
      _codlin = "";
  }
  if (_codimp.not_empty())
  {
    TTable imp("IMP");
    imp.put("CODTAB", _codimp);
    if (imp.read() != NOERR)
      _codimp = "";
  }
}

char TMRP_calendar::tipo() const
{
  if (!_codlin.blank())
    return 'L';
  if (!_codimp.blank())
    return 'I';
  return 'S';
}

TDate& TMRP_calendar::next_working_day(TDate& work, int gap)
{
  const int delta = gap >= 0 ? +1 : -1;
  const int steps = gap >= 0 ? gap : -gap;
  for (int i = 0; i < steps; i++)
  {
    do work += delta;
    while (turni_min(work) <= 0);
  }
  return work;
}

TMRP_calendar::TMRP_calendar(const char* linea, const char* impianto)
{
  set(linea, impianto);
  init_default();
}

///////////////////////////////////////////////////////////
// TCalendar_win
///////////////////////////////////////////////////////////

class TCalendar_win : public TField_window
{
  int _anno;
  TMRP_calendar* _calendario;

protected:
  virtual void handler(WINDOW win, EVENT* ep);
  virtual void update();

public:
  void set_calendar(TMRP_calendar* cal, int year = 0);
  TMRP_calendar* get_calendar() { return _calendario; }

  TCalendar_win(int x, int y, int dx, int dy, 
                WINDOW parent, TWindowed_field* owner);
  virtual ~TCalendar_win() { }
};

void TCalendar_win::handler(WINDOW win, EVENT* ep)
{
  switch (ep->type)
  {
  case E_MOUSE_DOWN:
    if (_calendario) 
    {
      const PNT& where = ep->v.mouse.where;
      RCT rct; xvt_vobj_get_client_rect(win, &rct);
      const int month =  where.v * 13 / rct.bottom;
      if (month >= 1 && month <= 12)
      {
        const int day =  where.h * 33 / rct.right - 1;
        const int last = TDate::last_day(month, _anno);
        if (day >= 1 && day <= last)
        {
          const bool is_std=_calendario->tipo()=='S' ;
          TString calname;
          switch (_calendario->tipo())
          {
            case 'S':
              calname="Calendario standard";
              break;
            case 'I':
              calname="Calendario impianto ";
              calname << '"'<< _calendario->impianto() << '"';
              break;
            case 'L':
              calname="Calendario linea ";
              calname << '"'<< _calendario->linea() << '"';
              break;
          }
          TMask m(calname, 1, 40, 8);
          m.add_static(DLG_NULL, 0, "Turni del giorno ", 1, 1);
          m.add_date(101, 0, "", 1, 2, "D");
          m.add_string(102, 0, "", 19, 2, 9, "D");
          m.add_boolean(103, 0, "Festa", 31, 2, is_std ? "":"D");
          if (is_std)
          {
            m.add_list(104, 0, "Turni minimi  ", 1, 4, 8, "", "0|1|2|3|4|5|6|7|8", "Nessuno|1 turno|2 turni|3 turni|4 turni|5 turni|6 turni|7 turni|8 turni");
            m.add_list(105, 0, "Turni massimi ", 1, 5, 8, "", "0|1|2|3|4|5|6|7|8", "Nessuno|1 turno|2 turni|3 turni|4 turni|5 turni|6 turni|7 turni|8 turni");
          } else {
            m.add_list(104, 0, "Turni minimi  ", 1, 4, 8, "", " |0|1|2|3|4|5|6|7|8", "Standard|Nessuno|1 turno|2 turni|3 turni|4 turni|5 turni|6 turni|7 turni|8 turni");
            m.add_list(105, 0, "Turni massimi ", 1, 5, 8, "", " |0|1|2|3|4|5|6|7|8", "Standard|Nessuno|1 turno|2 turni|3 turni|4 turni|5 turni|6 turni|7 turni|8 turni");
          }
          m.add_number(106, 0, "= ", 31, 4, 1, "D");
          m.add_number(107, 0, "= ", 31, 5, 1, "D");
          m.add_button(DLG_OK, 0, "", -12, -1, 10, 2);
          m.add_button(DLG_CANCEL, 0, "", -22, -1, 10, 2);

          const TDate d(day, month, _anno);
          const bool festa = _calendario->is_holiday(d);
          m.set(101, d.string());
          m.set(102, itow(d.wday()));
          m.set(103, festa ? "X" : "");
          int mini, maxi; 
          _calendario->turni(d, mini, maxi, TRUE);
          m.set(104, mini);
          m.set(105, maxi);
          _calendario->turni(d, mini, maxi, FALSE);
          m.set(106, mini);
          m.set(107, maxi);
          if (m.run() == K_ENTER && m.dirty())
          {
            const char mi = m.get(104)[0];
            mini = (mi == ' ') ? -1 : mi-'0';
            const char ma = m.get(105)[0];
            maxi = (ma == ' ') ? -1 : ma-'0';
            _calendario->set_turni(d, mini, maxi);

            const bool fe = m.get_bool(103);
            if (fe != festa)
            {
              if (fe) 
                _calendario->declare_holiday(d.day(), d.month());
              else
                _calendario->suppress_holiday(d.day(), d.month());
            }
            owner().set_dirty();
            force_update();
          }
        }
      }
    }
    break;
  default:
    break;
  }

  TField_window::handler(win, ep);
}

void TCalendar_win::update()
{
  const TDate today(TODAY);
  const int this_year  = today.year();
  const int this_month = today.month();
  const int this_day   = today.day();

  RCT rct; xvt_vobj_get_client_rect(win(), &rct);

  TField_window::update();
  if (fexist("calend01.bmp"))
  {                     
    TFilename month; month.format("calend%02d.bmp", this_month); 
    TImage bmp(month);
    int x = (rct.right - bmp.width()) / 2;
    int y = (rct.bottom - bmp.height()) / 2;
    bmp.draw(win(), x, y);
  }

  _pixmap = TRUE;

  TString16 str;
  str << _anno;
  set_color(COLOR_BLACK, COLOR_WHITE);

  if (_anno == this_year)
    set_font(XVT_FFN_FIXED, XVT_FS_BOLD);
  stringat(1, 1, str);
  if (_anno == this_year)
    set_font();

  int i, j;
  for (i = 1; i <= 31; i++)
  {
    const int x = rct.right * (i+1) / 33;
    line(x, 0, x, rct.bottom);
    str.format("%2d", i);

    if (i == this_day)
      set_font(XVT_FFN_FIXED, XVT_FS_BOLD);
    stringat(x+2, 0, str);
    if (i == this_day)
      set_font();
  }
  for (j = 1; j <= 12; j++)
  {
    const int y = rct.bottom * j / 13;
    line(0, y, rct.right, y);
    str = itom(j); str.cut(3);

    if (j == this_month)
      set_font(XVT_FFN_FIXED, XVT_FS_BOLD);
    stringat(1, y, str);
    if (j == this_month)
      set_font();
  }

  TMRP_calendar* defcal = NULL;
  TMRP_calendar* cal = _calendario;
  if (cal == NULL)
  {
    defcal = new TMRP_calendar();
    cal = defcal;
  }

  for (j = 1; j <= 12; j++)
  {
    const int y = rct.bottom * j / 13;
    const last = TDate::last_day(j, _anno);
    for (i = 1; i <= last; i++)
    {
      const int x = rct.right * (i+1) / 33;
      const TDate data(i, j, _anno);
      int tmin, tmax;
      cal->turni(data, tmin, tmax);
      
      COLOR colore = COLOR_BLACK;
      if (cal->is_red(data))
        colore = COLOR_RED; 
      else
      {
        if (tmin != 0)
        {
          const TDate ieri = data-1L;
          if (cal->turni_min(ieri) == 0)
          {
            const TDate domani = data+1L;
            if (cal->turni_min(domani) == 0)
              colore = COLOR_MAGENTA; 
          }        
        }
        else
          colore = COLOR_BLUE; 
      }
      set_color(colore, COLOR_WHITE); 

      if (j == this_month && i == this_day)
        set_font(XVT_FFN_FIXED, XVT_FS_BOLD);
      
      str.format("%d", tmin);
      stringat(x+2, y, str);
      str.format("%2d", tmax);
      stringat(x+2, y+BASEY/2, str);

      if (j == this_month && i == this_day)
        set_font();
    }
  }

  if (defcal != NULL)
    delete defcal;

  _pixmap = FALSE;
}

void TCalendar_win::set_calendar(TMRP_calendar* cal, int year)
{
  _calendario = cal;
  if (year > 0)
    _anno = year;
  else
    _anno = TDate(TODAY).year();
}

TCalendar_win::TCalendar_win(int x, int y, int dx, int dy, 
               WINDOW parent, TWindowed_field* owner)
             : TField_window(x, y, dx, dy, parent, owner)
{
  xvt_sbar_set_range(win(), HSCROLL, 0, 0);
  xvt_sbar_set_range(win(), VSCROLL, 0, 0);
  set_calendar(NULL);
}

///////////////////////////////////////////////////////////
// TCalendar_field
///////////////////////////////////////////////////////////

TField_window* TCalendar_field::create_window(int x, int y, int dx, int dy, WINDOW parent)
{
  return new TCalendar_win(x, y, dx, dy, parent, this);
}           

void TCalendar_field::set_calendar(TMRP_calendar* cal, int year)
{
  TCalendar_win& cw = (TCalendar_win&)win();
  cw.set_calendar(cal, year);
  cw.force_update();
}

///////////////////////////////////////////////////////////
// TCalendar_mask
///////////////////////////////////////////////////////////

/*
void TCalendar_mask::make_profile_name(TFilename& f) const
{
  f =::firm2dir(-1);    // Directory dati
  f.add("config");      // Directory config
  f.add(user());      // Directory config
  if (!f.exist())
    make_dir(f);
  f.add(source_file()); // Nome Maschera
  f.ext("ini");         // Estensione
}

void TCalendar_mask::save_profile()
{
  TFilename prof; 
  make_profile_name(prof);

  ofstream ini(prof);
  for (int i = 0; i < fields(); i++)
  {
    TMask_field& f = fld(i);
    if (f.is_loadable())
    {
      if (f.is_sheet())
      {
        TSheet_field& s = (TSheet_field&)f;
        FOR_EACH_SHEET_ROW(s, r, row)
          ini << f.dlg() << '[' << r << "] = " << *row << endl;
      }
      else
      {
        ini << f.dlg() << " = " << f.get() << endl;
      } 
    }
  }
}

void TCalendar_mask::load_profile()
{
  TFilename prof; 
  make_profile_name(prof);
  if (prof.exist())
  {
    TScanner ini(prof);
    while (ini.good())
    {
      TString& line = ini.line();
      int id, idx;
      const int num = sscanf(line, "%d[%d] = ", &id, &idx);
      if (num > 0 && id2pos(id) >= 0)  // Riga valida
      {
        TMask_field& f = field(id);
        if (f.is_loadable())
        {
          const int eq = line.find('=');
          line.ltrim(eq+2);  // Considera solo il valore del campo
          if (num == 1)
            f.set(line); else
          if (num == 2 && f.is_sheet())
          {
            TSheet_field& sf = (TSheet_field&)f;
            if (idx == 0) sf.destroy(); // Cancella tutto se inserisci la prima riga
            sf.row(idx) = line;
          }
        }
      }
    }
  }
}
*/

TMask_field* TCalendar_mask::parse_field(TScanner& scanner)
{
  const TString& k = scanner.key();
  if (k == "CA")
    return new TCalendar_field(this);
  return TAutomask::parse_field(scanner);
}

void TCalendar_mask::update_calendar(short calendar, short year, short plant, short line)
{
  TCalendar_field& cf = (TCalendar_field&)field(calendar);
  TString16 linea = line > 0 ? get(line) : EMPTY_STRING;
  TString16 impianto = plant > 0 ? get(plant) : EMPTY_STRING;
  int anno = year >= 0 ? get_int(year) : 0;
  _calendar.set(linea, impianto);
  cf.set_calendar(&_calendar, anno);
}

TCalendar_mask::TCalendar_mask(const char* name, int num)
{
  read_mask(name, num, 0);
  set_handlers();
}


//***********************************
// 
//***********************************
static TMRP_config *_confditta=NULL;

TMRP_config & mrp_config()
{
  if (_confditta==NULL)
    _confditta= new TMRP_config();
  return *_confditta;
}


//***********************************
int TMRP_config::ora_inizio_turno(int i)
{
  return (int)inizio_turno(i) / 60; // numero di minuti 
}

int TMRP_config::min_inizio_turno(int i)
{
  return (int)inizio_turno(i) % 60; // numero di minuti 
}

int TMRP_config::inizio_turno(int i)
{
  return get_int("TINIZIO","mr", i);
}

int TMRP_config::ore_durata_turno(int i)
{
  return (int)durata_turno(i) / 60; 
}

int TMRP_config::min_durata_turno(int i)
{
  return (int)durata_turno(i) % 60; 
}

int TMRP_config::durata_turno(int i)
{
  return get_int("TDURATA", "mr", i);
}

int TMRP_config::numpers_turno(int t) 
{
  return get_int("NUMPERS", "mr", t);
}

void TMRP_config::set_inizio_turno(int i,int ore, int minuti)
{
  const int time = ore * 60 + minuti;
  set_inizio_turno(i,time);
}

void TMRP_config::set_durata_turno(int i,int ore, int minuti)
{
  const int time = ore * 60 + minuti;
  set_durata_turno(i,time);
}

void TMRP_config::set_inizio_turno(int i,int time)
{
  set("TINIZIO", time, "mr", TRUE, i);
}

void TMRP_config::set_durata_turno(int i,int time)
{
  set("TDURATA", time, "mr", TRUE, i);
}

//********
TImpianto  * get_impianto(const char * codice)
{
  static TImpianto *buffer=NULL;
  if (!buffer)
    buffer= new TImpianto();
  *buffer=cache().get("IMP",codice);
  return buffer;
}
TLinea_prod* get_linea(const char * codice)
{
  static TLinea_prod *buffer=NULL;
  if (!buffer)
    buffer= new TLinea_prod();
  *buffer=cache().get("LNP",codice);
  return buffer;
}


const char *TUnita_produttiva::erre1(int t)
{
  static char _erre[4] = "R0";
  _erre[1]='0'+char(t);
  CHECK(t>=0 &&  t<8, "Errore nel numero di turno");
  return _erre;
}
const char *TUnita_produttiva::erre2(int t)
{
  static char _erre[4] = "R10";
  _erre[2]='0'+char(t);
  CHECK(t>=0 &&  t<8, "Errore nel numero di turno");
  return _erre;
}
#define M_INIZIO 10000L
#define M_DURATA 1L
//#define M_PERSON 0.05
// inizio turno
int  TUnita_produttiva::inizio_turno(int t)
{
  real r=get_real(erre1(t));
  r=r/M_INIZIO;
  const int i=(int)r.round().integer();
  return i;
}
void TUnita_produttiva::set_inizio_turno(int t,int time)
{
  real r(time);
  r = r * M_INIZIO;
  r = r + long(TUnita_produttiva::durata_turno(t))*M_DURATA;
  put(erre1(t), r);
}
// durata turno
int  TUnita_produttiva::durata_turno(int t)
{
  real r=get_real(erre1(t));
  r=r-real(long(TUnita_produttiva::inizio_turno(t))*M_INIZIO);
  r=r/M_DURATA;
  const int i=(int)r.round().integer();
  return i;
}
void TUnita_produttiva::set_durata_turno(int t,int  time)
{
  real r(TUnita_produttiva::inizio_turno(t));
  r = r * M_INIZIO;
  r = r + long(time)*M_DURATA;
  put(erre1(t), r);
}
// persone per turno
int TUnita_produttiva::raw_numpers_turno(int t) 
{
  real r(get_real(erre2(t)));
  return (int)r.round().integer();
}

int TUnita_produttiva::numpers_turno(int t) 
{
  int val;
  if (TUnita_produttiva::personale_dedicato())
  {
    val=raw_numpers_turno(t);
    if (val==0)
      val=get_int("I1");
  }
  else
    val= mrp_config().numpers_turno(t);
  return val;
  /*int r=raw_numpers_turno(t);
  if (r==0)
    return numpers_turno();
  else
    return r;*/
}
void TUnita_produttiva::set_numpers_turno(int t, int  pers)
{
  real r(pers);
  put(erre2(t), r);
}


void TUnita_produttiva::set_inizio_turno(int t,int ore, int minuti)
{
  const int time=ore*60+minuti;
  set_inizio_turno(t,time);
}
void TUnita_produttiva::set_durata_turno(int t,int ore, int minuti)
{
  const int time=ore*60+minuti;
  set_durata_turno(t,time);
}

int TUnita_produttiva::ora_inizio_turno(int t)
{
  return inizio_turno(t)/60;
}
int TUnita_produttiva::min_inizio_turno(int t)
{
  return inizio_turno(t)%60;
}
int TUnita_produttiva::ore_durata_turno(int t)
{
  return durata_turno(t)/60;
}
int TUnita_produttiva::min_durata_turno(int t)
{
  return durata_turno(t)%60;
}

const char * TUnita_produttiva::codmag() const 
{
  TString16 s(codmagdep());
  return s.sub(0,3);
}

const char * TUnita_produttiva::coddep() const 
{
  TString16 s(codmagdep());
  return s.sub(3,5);
}

TRectype & TUnita_produttiva::operator=(const TRectype & rec) 
{  
  TRectype ::operator=(rec);
  return *this ; 
}
// ***************************
// ****** TImpianto
TImpianto::TImpianto(const char* cod) 
  : TUnita_produttiva()
{
  settab("IMP");
  if (cod && *cod)
  {
    const TRectype & rec = cache().get("IMP", cod);
    *this = rec;
  }
}


// ***************************
// ****** TLinea_prod
TRectype & TImpianto::operator=(const TRectype & rec) 
{  
  TUnita_produttiva::operator=(rec);
  return *this ; 
}
TRectype & TLinea_prod::operator=(const TRectype & rec) 
{  
  TUnita_produttiva::operator=(rec);
  return *this ; 
}

const char * TLinea_prod::codimp() const 
{
  return get("S6");
}

TLinea_prod::TLinea_prod(const char* cod ) 
  : TUnita_produttiva()
{
  settab("LNP");
  if (cod && *cod)
  {
    const TRectype & rec = cache().get("LNP", cod);
    *this = rec;
  }
}

const char * TLinea_prod::codmagdep() const 
{
  const char * cod=TUnita_produttiva::codmagdep() ;
  if (*cod==0)
  {
    TImpianto* imp=((TLinea_prod *)this)->get_impianto();
    if (imp)
      return imp->codmagdep();
  }
  return cod;
}


const char * TLinea_prod::codmagdep_coll() const 
{
  const char * cod=TUnita_produttiva::codmagdep_coll() ;
  if (*cod==0)
  {
    TImpianto* imp=((TLinea_prod *)this)->get_impianto();
    if (imp)
      return imp->codmagdep_coll();
  }
  return cod;
}


int TLinea_prod::inizio_turno(int t)
{
  int val;
  if (TUnita_produttiva::num_turni()!=0)
    val=TUnita_produttiva::inizio_turno(t);
  else
  {
    TImpianto* imp=get_impianto();
    if (imp && imp->num_turni()!=0)
      val=imp->inizio_turno(t);
    else
      val=mrp_config().inizio_turno(t);
  }
  return val;
}
int TLinea_prod::durata_turno(int t)
{
  int val;
  if (TUnita_produttiva::num_turni()!=0)
    val=TUnita_produttiva::durata_turno(t);
  else
  {
    TImpianto* imp=get_impianto();
    if (imp && imp->num_turni()!=0)
      val=imp->durata_turno(t);
    else
      val=mrp_config().durata_turno(t);
  }
  return val;
}
int TLinea_prod::numpers_turno(int t)
{
  int val;
  if (TUnita_produttiva::num_turni()!=0)
    val=TUnita_produttiva::numpers_turno(t);
  else
  {
    TImpianto* imp=get_impianto();
    if (imp)
      val=imp->numpers_turno(t);
  }  
  return val;
}

int TLinea_prod::num_turni() 
{
  int val=TUnita_produttiva::num_turni();
  if (val==0)
  {
    TImpianto* imp=get_impianto();
    if (imp)
      val=imp->num_turni();
    if (val==0)
      val=mrp_config().num_turni();
  }
  return val;
}


TImpianto * TLinea_prod::get_impianto() const
{
	const char * imp = codimp();
  if (imp && *imp)
    return ::get_impianto(imp);
  else
    return NULL;
}

void find_price(const TString &tipocv, const TString &codcv, const TString &codcatven, 
  const char *tipocf, long codcf, 
  const char * codice, const real & qta, real & price)
{
  static TCondizione_vendita *_condv =NULL;

  bool retry=TRUE;
  if (_condv == NULL)
    _condv = new TCondizione_vendita(new TConfig (CONFIG_DITTA));

  switch (tipocv[0])
  {
    case 'C':
      if (codcf == 0L && _condv->config_ditta().get_bool("GESCONCC", "ve"))
        break; // necessario il codice del cli/for
    case 'O':
    case 'L':
      _condv -> put_condv(tipocv,codcv,codcatven,
            tipocf, codcf ? format("%ld",codcf) : "");
      if (_condv->ricerca(codice, qta))
      {
        price = _condv->get_prezzo();
        retry=FALSE;
      }
    break;
  }
  if (retry)
  {
    TToken_string key(codice);
    key.add(1);
    price = cache().get(LF_UMART,key).get_real(UMART_PREZZO);
  } 
}