#include <mask.h>
#include <applicat.h>
#include <colors.h>
#include <relation.h>
#include <printer.h>
#include <sheet.h>
#include <urldefid.h>
#include <utility.h>

TSheet::TSheet(short x, short y, short dx, short dy,
               const char* title, const char* head, byte buttons,
               long first, WINDOW parent)
: _curr(first), _last_update(-1), _checkable(FALSE),
  _check_enabled(TRUE), _buttonmask(buttons)
{
  const char* g;

  TToken_string new_head(512);
  TToken_string h(head);
  TString item(80);

  for(_columns = 0; (g = h.get()) != NULL; _columns++)
  {
    CHECK(_columns < MAX_COL, "To many columns in sheet ");
    item = g;

    const int et = item.find('@');
    if (et >= 0)
    {
      const char t = item.right(1)[0];
      _type[_columns] = isalpha(t) ? toupper(t) : '\0';
      const int l = atoi(item.mid(et+1));
      item.cut(et);
      if (_columns == 0 && et == 0 && l <= 1)
        _checkable = _size[0] = 1;
      else  
        _size[_columns] = l > 0 ? l : item.len();
    } else _size[_columns] = item.len();

    item.trim();
    new_head.add(item);
  }
  set_row(new_head, -1);

  for (int i = 0; i < MAX_BUT; i++) _button[i] = NULL_WIN;

  if (parent == NULL_WIN) parent = TASK_WIN;

  long flags = WSF_CLOSE | WSF_SIZE | WSF_VSCROLL | WSF_HSCROLL ;
  WIN_TYPE wt = W_DOC;

  if (parent != TASK_WIN)
  {
    wt = W_PLAIN;
    flags = WSF_VSCROLL;
  }

  if (dx == 0)                            // Calculates window width
  {                              
    RCT r; get_client_rect(parent, &r);
    dx = r.right/CHARX -6;                          

    const int larg = width();
    if (dx > larg) dx = larg;
  }  
  if (dy == 0) dy = 20;                   // Calculates window height

  create(x, y, dx, dy, title, flags, wt, parent);
  if (wt != W_PLAIN)    // If normal sheet add buttons
  {
    _visible_rows = rows()- head_on() - 3;

    add_button(DLG_SELECT, "Selezione" );
    if (buttons & 0x1) add_button(DLG_NEWREC, "Gestione");
    if (buttons & 0x2) add_button(DLG_NEWREC, "Nuovo");
    if (buttons & 0x4) add_button(DLG_DELREC, "Elimina");
    if (buttons & 0x8) add_button(DLG_QUIT,   "Fine");
    else               add_button(DLG_CANCEL, "Annulla");
  }
}

void TSheet::add_button(short id, const char* caption, KEY key)
{
#if XVT_OS == XVT_OS_WIN
  const int BUT_HEIGHT = 2;
#else
  const int BUT_HEIGHT = 1;
#endif

  WINDOW b = xvt_create_control(WC_PUSHBUTTON, 0, 0, 11, BUT_HEIGHT, caption, win(), 0, 0, id);
  for (int i = 0; i < MAX_BUT; i++)
    if (_button[i] == NULL_WIN)
    {
      _button[i] = b;
      _key[i] = key;
      break;
    }
}

void TSheet::repos_buttons() const
{
  for (int buttons = 0; _button[buttons] != NULL_WIN; buttons++)
    if (buttons == MAX_BUT-1) { buttons++; break; }
  if (buttons == 0) return;     
  
  RCT wr; get_client_rect(win(), &wr);
  RCT br; get_client_rect(_button[0], &br);

  int space = (wr.right - buttons * br.right) / (buttons+1);
  if (space < 0) space = 0;

  int x = space;
  const int y = wr.bottom - br.bottom-4;
  for (int b = 0; b < buttons; b++, x += br.right+space)
  {
    RCT r; set_rect(&r, x, y, x+br.right, y+br.bottom);
    move_window(_button[b], &r);
  }
}

void TSheet::open()
{
  set_scroll_max(width(), items());
  _last_update = -1;

  if (!is_visible(_curr)) set_first(_curr);

  if (buttons_on())
  { 
    // Abilita selezione se c'e' almeno un elemento
    xvt_enable_control(_button[0], items() > 0);     
    repos_buttons();
  }
  TScroll_window::open();
  force_update();
}


void TSheet::set_first(long n)
{
  update_thumb(-1, n);
}


void TSheet::set_row(const TToken_string& row, byte i)
{
  if (i > 64) i = 0; else i++;
  if (_page.objptr(i) == NULL)
    _page.add(row, i);
  else
    ((TToken_string&)_page[i]) = row;
}


void TSheet::build_page(long from)
{
  const long f = (from < 0) ? first() : from;

  if (_last_update != f)
  {
    int r = visible_rows();
    if (f+r > items())
      r = int(items()-f);
    if (r > 0)
      page_build(f, r);
    _last_update = f;
  }
}       


short TSheet::reserved_rows() const
{
  return buttons_on() ? 3 : 0;
}


void TSheet::handler(WINDOW win, EVENT* ep)
{
  switch(ep->type)
  {
  case E_FOCUS:
    invert_row(selected());
    break;
  case E_MOUSE_DOWN:
    if (ep->v.mouse.button)
    {
      dispatch_e_char(win, K_ESC);
      return;
    }
  case E_MOUSE_DBL:
  {
    const short y = ep->v.mouse.where.v / CHARY;
    const short f = head_on() ? 1 : 0;
    if (y >= f)
    {
      long vec = selected();
      long nuo = first()+y-f;
      select(nuo);
      if (ep->type == E_MOUSE_DBL)
        dispatch_e_char(win, K_ENTER);
      else      if (_checkable && _check_enabled && vec == nuo)
      {
        _checked.not(nuo);
        force_update();
      }
    }
    else
      dispatch_e_char(win, y ? K_UP : K_PREV);
  }
    break;
  case E_CONTROL:
    switch(ep->v.ctl.id)
    {
    case DLG_OK    :
    case DLG_SELECT:
      dispatch_e_char(win, K_ENTER); break;
    case DLG_CANCEL:
    case DLG_QUIT  :
      dispatch_e_char(win, K_ESC); break;
    case DLG_NEWREC:
      dispatch_e_char(win, K_INS); break;
    case DLG_DELREC:
      dispatch_e_char(win, K_DEL); break;
    default:
    {
      const WINDOW b = ep->v.ctl.ci.win;
      for (int i = 0; i < MAX_BUT; i++)
        if (_button[i] == b)
        {
          stop_run(_key[i]);
          break;
        }        
    }  
      break;
    }
    break;
  case E_SIZE:
  {
    _last_update = -1;
    _visible_rows = ep->v.size.height/CHARY - (head_on() ? 1 : 0);
    if (buttons_on())
    {
      _visible_rows -= reserved_rows();
      repos_buttons();
    }
    force_update();
  }
    break;
  default:
    break;
  }

  TScroll_window::handler(win, ep);
}


PNT TSheet::log2dev(long x, long y) const
{
  if (autoscrolling()) x -= origin().x;
  return TWindow::log2dev(x, y);
}

bool TSheet::on_key(KEY key)
{
  switch(key)
  {
  case K_ENTER:
    if (items() < 1) key = K_ESC;
  case K_ESC:
    stop_run(key);
  case K_DEL:
    if ((_buttonmask & 0x4) && items())
      stop_run(key);
    break;
  case K_INS:
    if (_buttonmask & 0x3)
      stop_run(K_INS);
    break;
  case K_LHOME:
    select(0);
    return TRUE;
  case K_LEND:
    select(items()-1);
    return TRUE;
  case K_PREV:
    select(selected()-visible_rows());
    return TRUE;
  case K_UP:
    select(selected()-1);
    return TRUE;
  case K_NEXT:
    select(selected()+visible_rows());
    return TRUE;
  case K_DOWN:
    select(selected()+1);
    return TRUE;
  case K_CTRL+'P':
    print();
    break;
  default:
    for (int i = 0; i < MAX_BUT; i++)
      if (_key[i] == key)
      {
        stop_run(key);
        break;
      }
    break;
  }

  if (_checkable && _check_enabled && items() > 0)
  {
    bool ok = TRUE;
    switch(key)
    {
    case K_SPACE: _checked.not(selected());     break;
                case K_F2   : uncheck(-1);      break;
                case K_F3   :   check(-1);      break;
                  default     : ok = FALSE;     break;
                }
    if (ok) force_update();
  }
  return TScroll_window::on_key(key);
}


void TSheet::select(long n)
{
  if (n < 0) n = 0; else
    if (n >= items()) n = items()-1;

  invert_row(_curr);
  _curr = n;
  
  if (is_visible(_curr))          
    invert_row(_curr);
  else
  {
    const long s = (_curr < first()) ? _curr : _curr-visible_rows()+1;
    set_first(s);
    force_update();
  }
}


void TSheet::check(long n, bool on)
{
  if (n < 0)
  {
    if (on)
    {
      _checked.set(items()-1);  // Force the size of Bit_array
      _checked.set();
    } 
    else
      _checked.reset();
  }     
  else 
    _checked.set(n, on);
}


// Converte il numero di riga assoluto nell'indice dell'array _page
int TSheet::row_to_page(long n) const
{
  long i = (n < 0) ? 0L : n-first()+1;
  
  if (i < 0 || i > visible_rows())
  {
    error_box("Line out of screen");
    i = 0L;
  }

  return (int)i;
}


// Converte il numero di riga assoluto nella coordinata y sulla finestra
int TSheet::row_to_win(long n) const
{
  int y = row_to_page(n);

  if (y < 1) y = 0; else
    if (!head_on()) y--;

  return y;
}


// Calcola la larghezza totale dello sheet virtuale
int TSheet::width() const
{
  int width = 0;
  for (int c = 0; c < _columns; c++)
    width += _size[c]+(c > 0);
  return width;
}

void TSheet::invert_row(long n)
{
  if (win() != NULL_WIN && is_visible(n))
  {
    const short y = row_to_win(n);
    autoscroll(FALSE);
    invert_bar(0,y,columns(),y+1);
    autoscroll(TRUE);
  }
}


bool TSheet::update_row(long n)
{
  const int i = row_to_page(n);
  if (i >= _page.items()) return FALSE;

  TToken_string& t = (TToken_string&)(_page[i]);
  t.restart();

  const short y = row_to_win(n);
  const char* s;

  int x = 0;
  for (int j = 0; (s = t.get()) != NULL; x += _size[j++]+1)
  {
    int x1 = x;
    if (n >= 0)
    {
      if (_checkable && j == 0)
        s = _checked[n] ? "X" : " ";
      
      switch (_type[j])
      {
      case 'R':
        x1 += _size[j]-strlen(s);        // Allinea a destra
        break;  
      case 'M': 
        s = itor(atoi(s));               // Numeri romani
        break;
      default:
        break;
      }    
    }  
    else
      x1 += (_size[j]-strlen(s)) >> 1;   // Centra le testate
    
    stringat(x1, y, s);
  }

  return TRUE;
}


TToken_string& TSheet::row(long n)
{
  if (n < 0) n = selected();
  if (!is_visible(n))
  {
    build_page(n);
    set_first(n);
  }
  short idx = row_to_page(n);
  return (TToken_string&)_page[idx];
}


void TSheet::update()
{
  if (_last_update < 0)
  {
    set_color(COLOR_BLACK, COLOR_WHITE);
    set_brush(COLOR_WHITE);
    set_pen(COLOR_BLACK);
    set_font(FF_FIXED);
    _visible_rows = rows() - reserved_rows() - head_on();
  }

  if (head_on())
  {               
    const int height = visible_rows()+1;
    const int width = columns()+1;
    short x = -1;
    for (int c = 0; c < _columns; c++)
    {
      x += _size[c] + 1;
      line(x,0,x,height);
    }
    autoscroll(FALSE);
    set_brush(MASK_BACK_COLOR);
    bar(0,0,width,1);
    if (buttons_on())
      bar(0, height, width, height+reserved_rows()+1);
    autoscroll(TRUE);
  }

  build_page();
  if (head_on()) update_row(-1);

  long last = first() + visible_rows();
  if (last > items()) last = items();
  for (long n = first(); n < last; n++) update_row(n);

  if (get_front_window() == win())
    invert_row(selected());
}

void TSheet::print()
{
  TPrinter& pr = main_app().printer();
  TPrintrow row;
  bool ok = pr.open();

  set_cursor(TASK_WIN, CURSOR_WAIT);
  int step = visible_rows();
  for (long n = 0L; ok && n < items(); n += step)
  {
    set_first(n);
    build_page();

    if ((n+step) > items()) step = int(items()-n);
    for (int i = 1; ok && i <= step; i++)
    {
      const char* s;

      TToken_string& t = (TToken_string&)_page[i];
      t.restart();

      row.reset();
      for (int j = 0, x = 0; (s = t.get()) != NULL; x += _size[j++]+1)
        row.put(s, x);

      ok = pr.print(row);
    }
  }
  set_cursor(TASK_WIN, CURSOR_ARROW);

  if (ok)
  {
    pr.formfeed();
    pr.close();
  }
  set_focus();
}

///////////////////////////////////////////////////////////
// TArray_sheet
///////////////////////////////////////////////////////////

TArray_sheet::TArray_sheet(short x, short y, short dx, short dy,
                           const char* caption, const char* head, byte buttons, WINDOW parent)
: TSheet(x, y, dx, dy, caption, head, buttons, 0L, parent)
{}

void TArray_sheet::page_build(long first, byte num)
{
  for (byte i = 0; i < num; i++)
    set_row(data(first+i), i);
}

long TArray_sheet::add(const TToken_string& s)
{
  const long n = _data.add(s, -1);
  set_scroll_max(-1, n);
  return n;
}

long TArray_sheet::add(TToken_string* s)
{
  const long n = _data.add(s, -1);
  set_scroll_max(-1, n);
  return n;
}

long TArray_sheet::insert(const TToken_string& s, long n)
{
  _data.insert(s, (int)n);
  set_scroll_max(-1, items());
  return n;
}


///////////////////////////////////////////////////////////
// TCursor_sheet
///////////////////////////////////////////////////////////

TCursor_sheet::TCursor_sheet(TCursor* cursor, const char* fields,
                             const char* title, const char* head, byte buttons)
: TSheet(-1,-1, 0, 0, title,head,buttons,cursor->pos()),
  _cursor(cursor), _records(cursor->items())
{
  TToken_string fldlst(fields);
  int campi = 0;
  for (const char* t = fldlst.get(0); t; t = fldlst.get(), campi++)
    if (*t > ' ' && *t != '"')
    {
      TFieldref fr(t, 0);
      TRecfield* rf = new TRecfield(_cursor->curr(fr.file()),
                                    fr.name(), fr.from(), fr.to() - 1);
      _fields.add(rf, campi);
    }
}


KEY TCursor_sheet::run()
{
  _records = _cursor->items();
  _cursor->freeze(TRUE);
  const KEY k = TSheet::run();
  _cursor->freeze(FALSE);
  return k;
}


void TCursor_sheet::page_build(long first, byte rows)
{
  TToken_string l(256);

  *_cursor = (TRecnotype)first;
  for (byte r = 0; r < rows; r++, ++(*_cursor))
  {
    l.cut(0);
    const int last = _fields.last();
    for (int i = 0; i <= last; i++)
    {
      TRecfield* rf = (TRecfield*)_fields.objptr(i);
      TFixed_string s(rf ? (const char*)*rf : "");
      l.add(s);
    }
    set_row(l, r);
  }
}

///////////////////////////////////////////////////////////
// TBrowse_sheet
///////////////////////////////////////////////////////////

TBrowse_sheet::TBrowse_sheet(TCursor* cursor, const char* fields,
                             const char* title, const char* head, byte buttons, 
                             TEdit_field* f, TToken_string* s)
: TCursor_sheet(cursor, fields, title, head, buttons), _field(f)
  
{
  if (field().browse()->inputs() == 1)
  {
#if XVT_OS == XVT_OS_WIN
    xvt_create_control(WC_EDIT, 1, -3, f->size()+1, 1, f->get(), win(), 
                       CTL_FLAG_DISABLED, 0L, 100);
#else
    xvt_create_control(WC_EDIT, 1, -3, f->size()+2, 1, f->get(), win(), 
                       CTL_FLAG_DISABLED, 0L, 100);
#endif
  }
  
  if (s && s->items() > 2)
  {
    int maxlen = 0;    
    SLIST lst = slist_new();
    for (const char* item = s->get(0); item; item = s->get())
    {
      item = s->get();
      const int len = strlen(item);
      if (len > maxlen) maxlen = len;
      slist_add(lst, (SLIST_ELT)NULL, (char*)item, 0L);
    }  
    
    WINDOW listbox = xvt_create_control(WC_LISTBUTTON, f->size()+3, -3, maxlen+3, 3, 
                                        "", win(), 0, 0L, 99);
    win_list_add(listbox, -1, (char*)lst);
    slist_dispose(lst);
    
    TString16 id; id << f->dlg();
    const int sel = s->get_pos(id) >> 1;
    win_list_set_sel(listbox, sel, TRUE);
  }
}


short TBrowse_sheet::reserved_rows() const
{
  return TSheet::reserved_rows() + 1;
}


void TBrowse_sheet::repos_buttons() const
{
  TSheet::repos_buttons();                    // Repos toolbar

  RCT wr; get_client_rect(win(), &wr);        // Get window size
  
  int left = CHARX;                           // left coord of next control to draw
  
  for (int id = 99; id <= 100; id++)
  {
    const WINDOW w = get_ctl_window(win(), id);
    if (w != NULL_WIN)
    {
      RCT r; get_client_rect(w, &r);
      r.left = left;
      r.top = wr.bottom - 4*CHARY + 4;
      r.right += r.left;
      r.bottom += r.top;
      move_window(w, &r);
      left = r.right+CHARX;                   // Increase left coord
    }
  }
}


bool TBrowse_sheet::on_key(KEY k)
{
  const WINDOW ctl = get_ctl_window(win(), 100);          
  const bool alnum = k >= ' ' && k < 256;

  if (ctl != NULL_WIN && (alnum || k == K_BACKSPACE || k == K_DEL))
  {
    const long oldsel = selected();
    const bool corre = field().mask().is_running();
    const WINDOW fldwin = field().win();

    TString80 old(corre ? field().get_window_data() : field().get_field_data());
    TString80 val(old.ltrim());

    if (alnum) 
    {
      if (val.len() >= field().size()) 
        val.cut(0);
      if (field()._flags.uppercase && isalpha(k))
        k = toupper(k);
      val << char(k);
    }  
    else val.rtrim(1);
    
    if (corre) set_title(fldwin, (char*)(const  char*)val);  
    else field().set_field_data(val);  

    ((TBrowse*)field().browse())->do_input(FALSE);
    _cursor->read();
    if (_cursor->file().bad())
    {
      beep();
      val = old;
      if (corre) set_title(fldwin, (char*)(const  char*)val);  
      else field().set_field_data(val);  
      *_cursor = oldsel;
    }
    else
      select(_cursor->pos());

    if (ctl != NULL_WIN) 
    {
      val = corre ? field().get_window_data() : field().get_field_data();
      set_title(ctl, (char*)(const char*)val);
    }
    return TRUE;
  }  
  else 
  {
    field().set("");
    if (ctl != NULL_WIN) set_title(ctl, "");
  }  

  return TSheet::on_key(k);
}


void TBrowse_sheet::handler(WINDOW win, EVENT* ep)
{
  if (ep->type == E_CONTROL && ep->v.ctl.id == 99)
  {
    const int what = win_list_get_sel_index(ep->v.ctl.ci.win);
    if (what >= 0)
      stop_run(K_CTRL + what);
    else  
      set_front_window(win);
    return;
  }
  TCursor_sheet::handler(win, ep);
}


KEY TBrowse_sheet::run()
{
  const bool spork = field().dirty();      // Store field status
  const TString80 old(field().get());
  
  const KEY key = TCursor_sheet::run();
  
  if (key == K_ESC || key == K_F9 || key == K_INS)                        
  {
    field().set(old);                      // Restore field status
    field().set_dirty(spork);
  }           
  
  return key;
}