#include <automask.h>
#include <colors.h>
#include <defmask.h>
#include <diction.h>
#include <printer.h>
#include <progind.h>
#include <reprint.h>
#include <statbar.h>

#include <bagn003.h>

static bool _print_aborted = false;

///////////////////////////////////////////////////////////
// Utility
///////////////////////////////////////////////////////////

void advanced_draw_justified_text(TWindow& win, const char* text, short x, short y, short dx)
{
  TString txt(text); txt.rtrim();
  int spaces = 0;
  for (int s = 0; txt[s]; s++)
    if (isspace(txt[s])) spaces++;
  const int tw = xvt_dwin_get_text_width(win.win(), txt, -1);
  if (tw < dx && spaces > 0)
  {
    txt << ' '; // Aggiunge spazio finale
    const double kspc = double(dx - tw) / spaces;
    int start = 0;
    double kx = x;
    for (int i = 0; txt[i]; i++)
    {
      if (isspace(txt[i]))
      {
        const bool last_word = txt[i+1] == '\0';
        const TString& parola = txt.sub(start, i + (last_word ? 0 : 1));
        const int lw = xvt_dwin_get_text_width(win.win(), parola, -1);
        if (last_word) // ultima parola
          kx = x+dx-lw;
        xvt_dwin_draw_text(win.win(), int(kx+0.5), y, parola, -1);
        kx += lw + kspc;
        start = i+1;
      }
    }
  }
  else
    xvt_dwin_draw_text(win.win(), x, y, text, -1);
}

void advanced_draw_text(TWindow& win, const char* text, const RCT& r, 
                        char halign, char valign)
{
  const short dx = r.right-r.left;
  const short dy = r.bottom-r.top;
  short x = r.left;
  short y = r.bottom;
  
  if (halign != 'L')
  {
    const int tw = xvt_dwin_get_text_width(win.win(), text, -1);
    switch (halign)
    {
    case 'C': x += (dx - tw)/2; break;
    case 'R': x = r.right-tw; break;
    default : break;
    }
  }

  // Text Height
  int leading, ascent, descent;
  xvt_dwin_get_font_metrics(win.win(), &leading, &ascent, &descent);
  switch (valign)
  {
  case 'C': y -= (dy - ascent)/2; break;
  case 'T': y = r.top + leading + ascent; break;
  default : y -= descent; break;
  }

  RCT orig; 

  xvt_dwin_get_clip(win.win(), &orig);
  const bool restore_clip = !xvt_rect_is_empty(&orig);
  if (restore_clip)
  {
	RCT clipper;
	xvt_rect_intersect(&clipper, &orig, (RCT*)&r);
	xvt_dwin_set_clip(win.win(), (RCT*)&clipper);
  }
  else
	xvt_dwin_set_clip(win.win(), (RCT*)&r);
  
  if (halign == 'J')
    advanced_draw_justified_text(win, text, x, y, dx);
  else
    xvt_dwin_draw_text(win.win(), x, y, text, -1);

  if (restore_clip)
    xvt_dwin_set_clip(win.win(), &orig);
  else
    xvt_dwin_set_clip(win.win(), NULL);
}

void advanced_draw_paragraph(TWindow& win, TString& para, const RCT& rct, 
                             char halign, char valign, int default_10row_height)
{
  const bool acapo = para.find('\n') >= 0;

  int leading, ascent, descent;
  xvt_dwin_get_font_metrics(win.win(), &leading, &ascent, &descent);
  
  int ky10 = (leading + ascent + descent) * 10;
  // Aggiusta l'altezza di una riga standard, se necessario
  if (ky10 < default_10row_height && ky10 > 80*default_10row_height/100)
    ky10 = default_10row_height;

  const int rct_height = rct.bottom - rct.top;
  int rows = (rct_height * 11) / ky10;   // Sto abbondante del 10%  (*11/10) altrimenti risulta spesso 0

  if (acapo || rows > 1) // Devo scrivere piu' righe?
  {
    const int kx10 = xvt_dwin_get_text_width(win.win(), "MMMMMMMMMM", 10); 
    const unsigned columns = (rct.right - rct.left) * 10 / kx10;

    TParagraph_string str(para, columns);
    if (str.items() < rows)
      rows = str.items();

    int ybase = rct.top;
    switch (valign)
    {
    case 'C': ybase += (rct_height - (rows * ky10) / 10) / 2; break;
    case 'B': ybase += rct_height - (rows * ky10) / 10; break;
    default : break;
    }
    
    for (int row = 0; row < rows; row++)
    {
      RCT rctline = rct;
      rctline.top = ybase + (ky10 * row) / 10;
      rctline.bottom = rctline.top + ky10 / 10;
      const char* line = str.get();
      if (halign == 'J' && (row == rows-1 || strlen(line) < columns/2))
        halign = 'L';
      advanced_draw_text(win, line, rctline, halign, 'T'); 
    }
  }
  else
    advanced_draw_text(win, para, rct, halign, valign); 
}

///////////////////////////////////////////////////////////
// TReport_link
///////////////////////////////////////////////////////////

void TReport_link::set(const char* field, const TString& var)
{
  _fields.add(field, var, true);
}

const TString& TReport_link::get(const char* field) const
{
  const TString* var = (const TString*)_fields.objptr(field);
  return var != NULL ? *var : (const TString&)EMPTY_STRING;
}

void TReport_link::add_rect(const RCT& rct)
{
  // Non memorizzo tutti i rettangoli del link, ma la loro unione
  if (xvt_rect_is_empty(&_rct))
    _rct = rct;
  else
  {
    if (rct.left < _rct.left) _rct.left = rct.left;
    if (rct.top < _rct.top) _rct.top = rct.top;
    if (rct.right > _rct.right) _rct.right = rct.right;
    if (rct.bottom > _rct.bottom) _rct.bottom = rct.bottom;
  }
}

int TReport_link::hit_test(const PNT& p) const
{
  if (p.v < _rct.top)
    return -1;
  if (p.v > _rct.bottom)
    return +1;
  if (p.h < _rct.left)
    return -1;
  if (p.h > _rct.right)
    return +1;
  return 0;
}

int TReport_link::compare(const TSortable& s) const
{
  const TReport_link& lnk = (const TReport_link&)s;
  int cmp = _rct.top - lnk._rct.top;
  if (cmp == 0)
    cmp = _rct.left - lnk._rct.left;
  return cmp;
}
  
TReport_link::TReport_link(const char* table)
            : _table(table)
{
  xvt_rect_set_empty(&_rct);
}

///////////////////////////////////////////////////////////
// TPrint_preview_window
///////////////////////////////////////////////////////////

class TPrint_preview_window : public TField_window
{
  TBook* _book;
  word _page;
  int _zoom;
  bool _grid;
  
  TAssoc_array _alinks;
  TPointer_array _plinks;

protected:
  void page_select();
  void popup_menu(EVENT* ep);
  const TReport_link* find_link(const PNT& pnt) const;

  virtual void handler(WINDOW win, EVENT* ep);
  virtual void update();
  virtual bool on_key(KEY k);

  void update_scroll_range();
  void do_scroll(int kx, int ky);

public:
  virtual PNT log2dev(long lx, long ly) const;
  TAssoc_array& alinks() { return _alinks; }
  TPointer_array& plinks() { return _plinks; }

  TPrint_preview_window(int x, int y, int dx, int dy, WINDOW parent, 
                        TWindowed_field* owner, TBook* book);
  virtual ~TPrint_preview_window();
};

PNT TPrint_preview_window::log2dev(long lx, long ly) const
{
  const TPoint res = _book->page_res();

  PNT pnt;
  pnt.h = short(lx * _zoom / res.x);
  pnt.v = short(ly * _zoom / res.y);

  const TPoint orig = origin();
  pnt.h -= short(orig.x);
  pnt.v -= short(orig.y);

  return pnt;
}

void TPrint_preview_window::update_scroll_range()
{
  const TPoint size = _book->page_size();
  const TPoint res = _book->page_res();

  PNT pnt;
  pnt.h = short(size.x * _zoom / res.x);
  pnt.v = short(size.y * _zoom / res.y);

  RCT rct; xvt_vobj_get_client_rect(win(), &rct);
  pnt.h -= rct.right; if (pnt.h < 0) pnt.h = 0;
  pnt.v -= rct.bottom; if (pnt.v < 0) pnt.v = 0;
 
  update_thumb(0, 0);
  set_scroll_max(pnt.h, pnt.v);
}

void TPrint_preview_window::update()
{
  clear(MASK_BACK_COLOR);
  RCT rct; xvt_vobj_get_client_rect(win(), &rct);

  const TPoint size = _book->page_size();
  const PNT pag = log2dev(size.x, size.y);
  if (pag.h < rct.right) rct.right = pag.h;
  if (pag.v < rct.bottom) rct.bottom = pag.v;
  hide_pen();
  set_brush(COLOR_WHITE);
  xvt_dwin_draw_rect(win(), &rct); // Disegna foglio bianco

  if (_grid)
  {
    const TPoint res = _book->page_res();
    const int lpi = _book->lpi();
    for (int i = 1; lpi > 0; i++)
    {
      set_pen(i%lpi ? MAKE_COLOR(232,232,255) : MAKE_COLOR(255,192,255));
      const short y = i * res.y / lpi;
      if (y > size.y)
        break;
      line(0, y, (short)size.x, y);
    }
    const int cpi = _book->cpi();
    for (int j = 1; cpi > 0; j++)
    {
      set_pen(j%10 ? MAKE_COLOR(232,232,255) : MAKE_COLOR(255,192,255));
      const short x = j * res.x / cpi;
      if (x > size.x)
        break;
      line(x, 0, x, (short)size.y);
    }
  }

  _book->print_page(*this, _page);

  TString80 str; str.format(FR("Pagina %u di %u"), _page, _book->pages());
  statbar_set_title(TASK_WIN, str);
}

#define POPUP_FIRST      20883
#define POPUP_PREV       20884
#define POPUP_NEXT       20885
#define POPUP_SEARCH     20886
#define POPUP_LAST       20887
#define POPUP_ZOOMIN     20888
#define POPUP_ZOOMOUT    20889
#define POPUP_GRID       20890

void TPrint_preview_window::popup_menu(EVENT* ep)
{
  MENU_ITEM menu[16];  // Stiamo larghi
  memset(menu, 0, sizeof(menu));
  menu[0].tag = POPUP_FIRST;   menu[0].text = (char*)TR("Prima");    menu[0].enabled = _page > 1;
  menu[1].tag = POPUP_PREV;    menu[1].text = (char*)TR("Indietro"); menu[1].enabled = _page > 1;
  menu[2].tag = POPUP_NEXT;    menu[2].text = (char*)TR("Avanti");   menu[2].enabled = _page < _book->pages();
  menu[3].tag = POPUP_LAST;    menu[3].text = (char*)TR("Ultima");   menu[3].enabled = _page < _book->pages();
  menu[4].tag = -1;            menu[4].separator = true;
  menu[5].tag = POPUP_ZOOMIN;  menu[5].text = (char*)TR("Zoom +");   menu[5].enabled = _zoom < 300;
  menu[6].tag = POPUP_ZOOMOUT; menu[6].text = (char*)TR("Zoom -");   menu[6].enabled = _zoom > 50;
  menu[7].tag = -1;            menu[7].separator = true;
  menu[8].tag = POPUP_GRID;    menu[8].text = (char*)TR("Griglia");  menu[8].enabled = true; 
                               menu[8].checkable = true;             menu[8].checked = _grid;

  const PNT& p = ep->v.mouse.where;
  xvt_menu_popup(menu, win(), p, XVT_POPUP_CENTER, 0);
}

void TPrint_preview_window::page_select()
{
  TMask m("Ricerca", 1, 28, 4);
  m.add_number(101, 0, "Pagina ", 1, 1, 4,  "U").check_type(CHECK_REQUIRED);
  m.add_button(DLG_OK, 0, "", -12, -1, 10, 2); 
  m.add_button(DLG_CANCEL, 0, "", -22, -1, 10, 2);
  if (m.run())
  {
    _page = m.get_int(101);
    if (_page < 1) _page = 1;
    if (_page > _book->pages()) _page = _book->pages();
  }
}

const TReport_link* TPrint_preview_window::find_link(const PNT& pnt) const
{
  int primo = 0, ultimo = _plinks.last();
  while (primo <= ultimo)
  {
    const int in_mezzo = (primo+ultimo)/2;
    const TReport_link* lnk = (const TReport_link*)_plinks.objptr(in_mezzo);
    const int cmp = lnk->hit_test(pnt);
    if (cmp == 0)
      return lnk;
    if (cmp < 0)
      ultimo = in_mezzo-1;
    else
      primo = in_mezzo+1;
  }
  return NULL;
}

void TPrint_preview_window::handler(WINDOW win, EVENT* ep)
{
  static PNT ptPan;
  
  switch (ep->type)
  {
  case E_MOUSE_MOVE:
    switch (ep->v.mouse.button)
    {
    case 2:
      {
        const PNT& ptCur = ep->v.mouse.where;
        TPoint orig = origin();
        orig.x += ptPan.h - ptCur.h;
        orig.y += ptPan.v - ptCur.v;
        update_thumb(orig.x, orig.y);
        force_update();
        ptPan = ptCur;
      }
      break;
    default:
      if (find_link(ep->v.mouse.where) != NULL)
        xvt_win_set_cursor(win, 8004);          // Ditino
      else
        xvt_win_set_cursor(win, CURSOR_ARROW);  // Freccia
      break;
    }
    break;
  case E_MOUSE_DOWN:
    switch (ep->v.mouse.button)
    {
    case 0:
      {
        const TReport_link* lnk = find_link(ep->v.mouse.where);
        if (lnk != NULL && _book != NULL) 
          _book->on_link(*lnk);
      }
      break;
    case 1:
      popup_menu(ep);
      break;
    case 2:
      ptPan = ep->v.mouse.where;
      break;
    default:
      break;
    }
    break;
  case E_COMMAND:
    {
      bool processed = true;
      switch(ep->v.cmd.tag)
      {
      case POPUP_FIRST  : 
        processed = _page > 1; 
        if (processed) _page = 1; 
        break;
      case POPUP_PREV   : 
        processed = _page > 1; 
        if (processed) _page--; 
        break;
      case POPUP_SEARCH : page_select(); break;
      case POPUP_NEXT   : 
        processed = _page < _book->pages(); 
        if (processed) _page++; 
        break;
      case POPUP_LAST   : 
        processed = _page < _book->pages(); 
        if (processed) _page = _book->pages(); 
        break;
      case POPUP_ZOOMIN : if (_zoom < 300) { _zoom += 10; update_scroll_range(); } break;
      case POPUP_ZOOMOUT: if (_zoom >  50) { _zoom -= 10; update_scroll_range(); } break;
      case POPUP_GRID   : _grid = !_grid; break;
      default:processed = false; break;
      }
      if (processed)
        force_update();
    }
    break;
  case E_HSCROLL:
  case E_VSCROLL:
    {
      int kx = 0, ky = 0;
      int& k = ep->type == E_HSCROLL ? kx : ky;
      switch(ep->v.scroll.what)
      {
      case SC_PAGE_UP  : k = -3; break;
      case SC_LINE_UP  : k = -1; break;
      case SC_PAGE_DOWN: k = +3; break;
      case SC_LINE_DOWN: k = +1; break;
      default          : TField_window::handler(win, ep); break;
      }
      if (k != 0)
        do_scroll(kx, ky); break;
    }
    break;
  default:
    TField_window::handler(win, ep);
    break;
  }
}

void TPrint_preview_window::do_scroll(int kx, int ky)
{
  const TPoint& r = range();
  TPoint orig = origin();
  orig.x += kx * r.x/8;
  orig.y += ky * r.y/6;
  if (orig.x < 0) orig.x = 0;
  if (orig.x > r.x) orig.x = r.x;
  if (orig.y < 0) orig.y = 0;
  if (orig.y > r.y) orig.y = r.y;
  if (orig != origin())
  {
    update_thumb(orig.x, orig.y);
    force_update();
  }
}

bool TPrint_preview_window::on_key(KEY k)
{
  bool ok = true;
  switch (k)
  {
  case '+'     : dispatch_e_menu(win(), POPUP_ZOOMIN); break; 
  case '-'     : dispatch_e_menu(win(), POPUP_ZOOMOUT); break;
  case K_HOME  :
  case K_LHOME : dispatch_e_menu(win(), POPUP_FIRST); break;
  case K_PREV  : dispatch_e_menu(win(), POPUP_PREV); break;
  case K_NEXT  : dispatch_e_menu(win(), POPUP_NEXT); break;
  case K_END:
  case K_LEND  : dispatch_e_menu(win(), POPUP_LAST); break;
  case K_LEFT  : do_scroll(-1, 0); break;
  case K_RIGHT : do_scroll(+1, 0); break;
  case K_UP    : do_scroll(0, -1); break;
  case K_DOWN  : do_scroll(0, +1); break;
  case 'G'     : 
  case 'g'     : dispatch_e_menu(win(), POPUP_GRID); break;
  default      : 
    if (k > K_CTRL)
      dispatch_e_char(parent(), k);  // Gestione acceleratori
    else
      ok = TField_window::on_key(k); 
    break;
  };
  return ok;
}

TPrint_preview_window::TPrint_preview_window(int x, int y, int dx, int dy, WINDOW parent, 
                                             TWindowed_field* owner, TBook* book)
: TField_window(x, y, dx, dy, parent, owner), _book(book), _page(1), _zoom(100), _grid(false)
{ 
  _pixmap = true;
  update_scroll_range();

  TConfig ini(CONFIG_GUI, "Preview");
  _grid = ini.get_bool("Grid");
}

TPrint_preview_window::~TPrint_preview_window()
{
  TConfig ini(CONFIG_GUI, "Preview");
  ini.set("Grid", _grid ? "X" : "");
}

///////////////////////////////////////////////////////////
// TPrint_preview_field
///////////////////////////////////////////////////////////

class TPrint_preview_field : public TWindowed_field
{
  TBook* _book;

protected:
  virtual TField_window* create_window(int x, int y, int dx, int dy, WINDOW parent);

public:
  TPrint_preview_field(TMask* m, TBook* book) : TWindowed_field(m), _book(book) { }
};

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

///////////////////////////////////////////////////////////
// TPreview_mask
///////////////////////////////////////////////////////////

class TPreview_mask : public TAutomask
{
  TBook* _book;
  TPrint_preview_field* _pvf;

protected:
  virtual bool on_key(KEY k);
  virtual bool on_field_event(TOperable_field& o, TField_event e, long jolly);
  virtual TMask_field* parse_field(TScanner& scanner);
  virtual void handler(WINDOW win, EVENT* ep);
  
public:
  void export_text();
  TPreview_mask(TBook* book);
};

bool TPreview_mask::on_key(KEY k)
{
  switch (k)
  {
  case '+'    :
  case '-'    :
  case K_HOME : 
  case K_LHOME: 
  case K_LEFT : 
  case K_RIGHT:
  case K_END  :
  case K_LEND :
  case K_PREV : 
  case K_NEXT : 
  case 'G'    :
  case 'g'    :
    dispatch_e_char(_pvf->win().win(), k);
    return true;
  default:
    break;
  }
  return TAutomask::on_key(k);
}

void TPreview_mask::handler(WINDOW win, EVENT* ep)
{
  // Riflessione eventi di scroll
  if (ep->type == E_HSCROLL || ep->type == E_VSCROLL) 
    ::dispatch_event(_pvf->win().win(), *ep, false);

  TAutomask::handler(win, ep);
}

void TPreview_mask::export_text()
{
  TFilename n; n.temp(NULL, "txt");
  if (_book->export_text(n))
    xvt_sys_goto_url(n, "open");
}

bool TPreview_mask::on_field_event(TOperable_field& o, TField_event e, long jolly)
{
  if (e == fe_button)
  {
    switch (o.dlg()) 
    {
    case DLG_FIRSTREC: dispatch_e_menu(_pvf->win().win(), POPUP_FIRST); break;
    case DLG_PREVREC : dispatch_e_menu(_pvf->win().win(), POPUP_PREV);  break;
    case DLG_FINDREC : dispatch_e_menu(_pvf->win().win(), POPUP_SEARCH);break;
    case DLG_NEXTREC : dispatch_e_menu(_pvf->win().win(), POPUP_NEXT);  break;
    case DLG_LASTREC : dispatch_e_menu(_pvf->win().win(), POPUP_LAST);  break;
    case DLG_EDIT    : export_text(); break;
    default: break;
    }
  }
  return true;
}

TMask_field* TPreview_mask::parse_field(TScanner& scanner)
{
  if (scanner.token().starts_with("PR"))
  {
    _pvf = new TPrint_preview_field(this, _book);
    return _pvf;
  }
  return TAutomask::parse_field(scanner);
}

TPreview_mask::TPreview_mask(TBook* book) : _book(book)
{
  read_mask("bagn008", 0, -1); 
  set_handlers();

  int pos = id2pos(DLG_FINDREC);
  if (pos >= 0 && id2pos(DLG_FIRSTREC) >= 0) //se e' un bottone pentapartito...
	{
		TButton_field& f_find = (TButton_field &)fld(pos);
		RCT rct_base;	f_find.get_rect(rct_base);
    const int bwidth = (rct_base.right - rct_base.left);
    const int bheight = (rct_base.bottom - rct_base.top);
    if (bwidth > 3*bheight/2) // Controllo se ho gia' ridimensionato i bottoni in precedenza
    {
		  int bx = bwidth / 3;
		  int by = bheight / 2;

      RCT r = rct_base; r.left += bx-2;  r.right -= bx-2;
			f_find.set_rect(r); // Ridimensiona il bottone centrale di ricerca
			
      bx += 5; by += 3;   // Aggiusta dimensioni bottoni sussidiari

		  pos = id2pos(DLG_FIRSTREC);
		  if (pos >= 0)
		  {	
			  r = rct_base; r.top = r.bottom - by;  r.right = r.left + bx;
			  fld(pos).set_rect(r);
		  }
		  pos = id2pos(DLG_PREVREC);
		  if (pos >= 0) 
		  {
			  r = rct_base; r.bottom = r.top + by;  r.right = r.left + bx;
			  fld(pos).set_rect(r);
      }
		  pos = id2pos(DLG_NEXTREC);
		  if (pos >= 0) 
		  {
			  r = rct_base; r.bottom = r.top + by;  r.left = r.right - bx;
			  fld(pos).set_rect(r);
		  }
		  pos = id2pos(DLG_LASTREC);
		  if (pos >= 0) 
		  {
			  r = rct_base; r.top = r.bottom - by;  r.left = r.right - bx;
			  fld(pos).set_rect(r);
		  }
    }
	}
}

///////////////////////////////////////////////////////////
// TWindow_printer
///////////////////////////////////////////////////////////

class TWindow_printer : public TWindow
{
public:
  TWindow_printer(PRINT_RCD* rcd);
  ~TWindow_printer();
};

TWindow_printer::TWindow_printer(PRINT_RCD* rcd)
{
  WINDOW prwin = xvt_print_create_win(rcd, (char*)(const char*)"Printing");
  set_win(prwin);
  _pixmap = true;
}

TWindow_printer::~TWindow_printer()
{
  if (win() != NULL_WIN)
  {
    xvt_vobj_destroy(win());
    set_win(NULL_WIN);
  }
}

///////////////////////////////////////////////////////////
// Writing a page
///////////////////////////////////////////////////////////

TPoint TBook::page_size() const
{
  return TPoint(_pw, _ph);
}

TPoint TBook::page_res() const
{
  return TPoint(_phr, _pvr);
}

bool TBook::open_page()
{
  if (_out == NULL)
    _out = new ofstream(_file);

  _page++;
  _index.add_long(_out->tellp(), page()); // Scrive alla posizione 1 l'inizio di pagina 1
 
  *_out << "<page number=" << page() << '>' << endl;

  memset(&_tools, 0, sizeof(_tools));
  _font.create("", 12, XVT_FA_ALL);
  _horizontal_alignment = 'L';
  _vertical_alignment = 'T';
  _rect.set(-1,-1,0,0);

  _print_aborted = false;
  _page_is_open = true;
  return _page_is_open;
}

bool TBook::close_page()
{
  if (!_page_is_open)
    return false;

  *_out << "</page number=" << page() << '>' << endl;

  _pages++;

  _page_is_open = false;
  return true;
}

void TBook::set_font(const TReport_font& f)
{
  if (f != _font)
  {
    _font = f;
    *_out << "<font"
          << " name=\"" << f.name() << '"'
          << " size=" << f.size()
          << " style=" << f.style() 
          << " />" << endl;
  }
}

void TBook::define_frame(const TRectangle& rect)
{
  if (rect != _rect)
  {
    _rect = rect;
    *_out << "<frame"
          << " x=" << _rect.x << " y=" << _rect.y
          << " dx=" << _rect.width() << " dy=" << _rect.height()
          << " />" << endl;
  }
}

void TBook::set_clip(long top, long bottom)
{
  if (bottom >= top)
  {
    const TRectangle rect(0, top, logical_page_width(), bottom-top);
    define_frame(rect);
    *_out << "<clip>" << endl;
  }
  else
    *_out << "</clip>" << endl;
}

void TBook::draw_text(const TRectangle& r, const char* txt)
{
  const TFixed_string str(txt);
  if (!str.blank())
  {
    define_frame(r);
    *_out << "<text>" << endl << str << endl << "</text>" << endl;
  }
}

void TBook::draw_book_pages(const TRectangle& r)
{
  define_frame(r);
  *_out << "<pages/>" << endl;
}

void TBook::set_pen(COLOR color, int width, PEN_STYLE style)
{
  if (_tools.pen.color != color || _tools.pen.width != width || _tools.pen.style != style)
  {
    _tools.pen.color = color;
    _tools.pen.width = width;
    _tools.pen.style = style;
    *_out << "<pen color=" << color 
          << " width=" << width 
          << " style=" << style << " />" << endl;
  }
}

void TBook::set_brush(COLOR color, PAT_STYLE pattern)
{
  if (_tools.brush.color != color || _tools.brush.pat != pattern)
  {
    _tools.brush.color = color;
    _tools.brush.pat = pattern;
    *_out << "<brush color=" << color << " pattern=" << pattern << " />" << endl;
  }
}

void TBook::set_text_color(COLOR fore, COLOR back, bool opaque)
{
  if (_tools.fore_color != fore || _tools.back_color != back || _tools.opaque_text != short(opaque))
  {
    _tools.fore_color = fore;
    _tools.back_color = back;
    _tools.opaque_text = opaque;
    *_out << "<text_color fore=" << fore 
          << " back=" << back 
          << " opaque=" << opaque << " />" << endl;
  }
}

void TBook::set_text_align(char halign, char valign)
{
  if (_horizontal_alignment != halign || _vertical_alignment != valign)
  {
    _horizontal_alignment = halign;
    _vertical_alignment = valign;
    *_out << "<text_align horizontal=" << halign << " vertical=" << valign << " />" << endl;
  }
}
 
void TBook::draw_rectangle(const TRectangle& r)
{
  define_frame(r);
  *_out << "<rectangle/>" << endl;
}

void TBook::draw_round_rectangle(const TRectangle& r, int radius)
{
  define_frame(r);
  *_out << "<rounded_rectangle radius=" << radius << " />" << endl;
}

void TBook::draw_ellipse(const TRectangle& r)
{
  define_frame(r);
  *_out << "<ellipse/>" << endl;
}

void TBook::draw_line(const TRectangle& r)
{
  define_frame(r);
  *_out << "<line/>" << endl;
}

void TBook::draw_link(const TRectangle& rect, const char* text, const char* link)
{
  define_frame(rect);
  *_out << "<a href=\"" << link << "\" >" << text << "</a>" << endl;
}

void TBook::draw_image(const TRectangle& rect, const char* name)
{
  define_frame(rect);
  *_out << "<image src=\"" << name << "\" />" << endl;
}

///////////////////////////////////////////////////////////
// Reading a page
///////////////////////////////////////////////////////////

bool get_xml_string(const TString& line, const char* attr, TString& value)
{
  TString80 str; str << ' ' << attr << '=';
  const int pos = line.find(str);
  if (pos >= 0)
  {
    const int apicia = pos + strlen(attr) + 2;
    if (line[apicia] == '"')
    {
      const int apicic = line.find('"', apicia+1);
      if (apicic > apicia)
      {
        value = line.sub(apicia+1, apicic);
        return true;
      }
    }
    else
    {
      const int space = line.find(' ', apicia);
      if (space > 0)
      {
        value = line.sub(apicia, space);
        return true;
      }
    }
  }
  return false;
}

int get_xml_int(const TString& line, const char* attr, int def)
{
  TString16 str;
  if (get_xml_string(line, attr, str))
    def = atoi(str);
  return def;
}

bool TBook::print_page(TWindow& win, size_t page)
{
  if (page <= 0 || page > pages()) 
    return false;

  const bool preview = win.win() != 883;
  RCT visible;
  if (preview)
  {
    xvt_vobj_get_client_rect(win.win(), &visible);
    TPrint_preview_window& pw = (TPrint_preview_window&)win;
    pw.alinks().destroy();
  }
 
  const int buffer_size = 1024;
  TString str(buffer_size);
  char* buffer = str.get_buffer();

  TString stringona; // Testo completo di un campo

  // Calcolo altezza riga standard
  const TRectangle rect_riga(0,0,1000,1000);
  RCT rct_riga; win.log2dev(rect_riga, rct_riga);
  const int default_10row_height = rct_riga.bottom - rct_riga.top;

  const streampos pos = _index.get_long(page);
  ifstream ifs(_file);
  ifs.seekg(pos);

  _rect.set(-1,-1,0,0); // Reset frame
  RCT rct = { 0,0,0,0 };

  _horizontal_alignment = 'L'; // Reset text alignment
  _vertical_alignment = 'T';
  
  while (!ifs.eof())
  {
    ifs.getline(buffer, str.size());
    if (str.starts_with("</page"))
      break;

    if (str.starts_with("<a "))
    {
      if (preview && rct.bottom >= visible.top && rct.top <= visible.bottom)
      {
        TString link;
        if (get_xml_string(str, "href", link))
        {
          TPrint_preview_window& pw = (TPrint_preview_window&)win;
          TAssoc_array& links = pw.alinks();

          TToken_string tok(link, '.');
          TString80 table, field, key;
          tok.get(0, table); tok.get(1, field);

          key << table << ',' << _rect.y;
          TReport_link* rl = (TReport_link*)links.objptr(key);
          if (rl == NULL)
          {
            rl = new TReport_link(table);
            links.add(key, rl);
          }

          const int inizio = str.find('>')+1;
          const int fine = str.rfind('<');
          const TString& stringona = str.sub(inizio, fine);
          rl->set(field, stringona);
          rl->add_rect(rct);

          if (!stringona.blank() && rct.right > rct.left) // Possono esserci campi chiave nascosti
          {
            WINDOW w = win.win();
            DRAW_CTOOLS dct;
            xvt_dwin_get_draw_ctools(w, &dct);

            XVT_FNTID oldfont = xvt_dwin_get_font(w);
            XVT_FNTID newfont = xvt_font_create();
            xvt_font_copy(newfont, oldfont, XVT_FA_ALL);
            xvt_font_set_style(newfont, xvt_font_get_style(oldfont) | XVT_FS_UNDERLINE);
            xvt_dwin_set_font(w, newfont);
            win.set_color(COLOR_BLUE, dct.back_color);
            advanced_draw_text(win, stringona, rct, _horizontal_alignment, _vertical_alignment);
            win.set_color(dct.fore_color, dct.back_color);
            xvt_dwin_set_font(w, oldfont);         
          }
        }
      }
      continue;
    }
    if (str.starts_with("<brush "))
    {
      COLOR col = COLOR_BLACK; 
      PAT_STYLE pat = PAT_SOLID;
      sscanf(str, "<brush color=%u pattern=%u />", &col, &pat);
      if (pat <= PAT_HOLLOW)
        win.hide_brush();
      else
        win.set_brush(col, pat);
      continue;
    }
    if (str.starts_with("<clip"))
    {
      xvt_dwin_set_clip(win.win(), &rct);
    } else
    if (str.starts_with("</clip"))
    {
      xvt_dwin_set_clip(win.win(), NULL);
    } else
    if (str == "<ellipse/>")
    {
      xvt_dwin_draw_oval(win.win(), &rct);
      continue;
    }
    if (str.starts_with("<frame "))
    {
      long x, y, dx, dy;
      sscanf(str, "<frame x=%ld y=%ld dx=%ld dy=%ld />", &x, &y, &dx, &dy);
      _rect.set(x, y, dx, dy);
      win.log2dev(_rect, rct);
      continue;
    }
    if (str.starts_with("<font "))
    {
      TString name = XVT_FFN_COURIER; get_xml_string(str, "name", name);
      const int size = get_xml_int(str, "size", 12);
      const XVT_FONT_STYLE_MASK style = get_xml_int(str, "style", 0);
      TReport_font font; font.create(name, size, style);
      if (win.win() == 883)
        xvt_dwin_set_font(win.win(), font.get_xvt_font(win));
      else
        xvt_dwin_set_font(win.win(), font.get_preview_font(win, page_res()));
      continue;
    }
    if (str.starts_with("<image "))
    {
      TString name; get_xml_string(str, "src", name);
      TImage* img = _images.image(name);
      if (img != NULL)
        img->draw(win.win(), rct);
      continue;
    }
    if (str == "<line/>")
    {
      PNT fr = { rct.top, rct.left };
      PNT to = { rct.bottom, rct.right };
      xvt_dwin_draw_set_pos(win.win(), fr);
      xvt_dwin_draw_line(win.win(), to);
      continue;
    }
    if (str == "<pages/>")
    {
      TString8 str; str.format("%u", pages());
      advanced_draw_text(win, str, rct, _horizontal_alignment, _vertical_alignment);
      continue;
    }
    if (str.starts_with("<pen "))
    {
      COLOR col = COLOR_BLACK; 
      int width = 0;
      PEN_STYLE style = P_SOLID;
      sscanf(str, "<pen color=%u width=%d style=%u />", &col, &width, &style);
      if (width < 0)
        win.hide_pen();
      else
      {
        if (win.win() == 883)
          win.set_pen(col, width * _phr / 72); // Converte width in 72' di pollice
        else
          win.set_pen(col, width);
      }
      continue;
    }
    if (str == "<rectangle/>")
    {
      xvt_dwin_draw_rect(win.win(), &rct);
      continue;
    }
    if (str.starts_with("<rounded_rectangle"))
    {
      const int radius = get_xml_int(str, "radius", 0);
      
      const TRectangle rr(0,0,radius,radius);
      RCT re; win.log2dev(rr, re);
      const int rad = (re.right-re.left) * lpi() / (cpi() * 2);
      xvt_dwin_draw_roundrect(win.win(), &rct, rad, rad);
      continue;
    }
    if (str == "<text>")
    {
      stringona.cut(0);
      while (!ifs.eof())
      {
        ifs.getline(buffer, str.size());
        if (str == "</text>")
          break;
        if (stringona.not_empty())
          stringona << '\n';
        stringona << str;
      }
      advanced_draw_paragraph(win, stringona, rct, 
                              _horizontal_alignment, _vertical_alignment,
                              default_10row_height);
      continue;
    }
    if (str.starts_with("<text_align "))
    {
      sscanf(str, "<text_align horizontal=%c vertical=%c />", 
             &_horizontal_alignment, &_vertical_alignment);
      continue;
    }
    if (str.starts_with("<text_color"))
    {
      COLOR fore, back;
      int opaque; 
      sscanf(str, "<text_color fore=%u back=%u opaque=%d />", &fore, &back, &opaque);
      win.set_color(fore, back);
      win.set_opaque_text(opaque != 0);
      continue;
    }
  }

  if (preview)
  {
    TPrint_preview_window& pw = (TPrint_preview_window&)win;
    TAssoc_array& alinks = pw.alinks();
    TPointer_array& plinks = pw.plinks();   
    plinks.destroy();
    if (alinks.items() > 0)
    {
      FOR_EACH_ASSOC_OBJECT(alinks, h, key, l)
        plinks.add((TReport_link*)l);
      plinks.sort();
    }
  }

  return true;
}

bool TBook::export_text(const TFilename& fname) const
{
  TString str(1024);
  char* buffer = str.get_buffer();

  ifstream ifs(_file);
  ofstream ofs(fname);

  TString_array page;
  int row, col, wid, hei;

  const TPoint res = page_res();

  while (!ifs.eof())
  {
    ifs.getline(buffer, str.size());

    if (str.starts_with("<frame "))
    {
      long x, y, dx, dy;
      sscanf(str, "<frame x=%ld y=%ld dx=%ld dy=%ld />", &x, &y, &dx, &dy);
      row = y * lpi() / res.y;
      col = x * cpi() / res.x;
      wid = dx * cpi() / res.x;
      hei = dy * lpi() / res.y;
    } else
    if (str == "<line/>")
    {
      if (hei == 0 && wid > 0)
      {
        if (page.objptr(row) == NULL)
          page.add("", row);
        TString& line = page.row(row);
        for (int i = 0; i < wid; i++)
        {
          const int j = i+col;
          if (j >= line.len())
            line.overwrite("-", j);
          else
          {
            if (line[j] == '|')
              line[j] = '+';
            else
            {
              if (line[j] == ' ')
                line[j] = '-';
            }
          }
        }
      } else
      if (hei > 0 && wid == 0)
      {
        for (int i = row; i < row+hei; i++)
        {
          if (page.objptr(i) == NULL)
            page.add("", i);
          TString& line = page.row(i);
          if (line.len() <= col)
            line.overwrite("|", col);
          else
          {
            if (line[col] == '-')
              line[col] = '+';
            else
            {
              if (line[col] == ' ')
                line[col] = '|';
            }
          }
        }
      }
    }
    if (str.starts_with("<page "))
    {
      page.destroy();
    } else
    if (str.starts_with("</page "))
    {
      const int last_row = page.last();
      for (int i = 0; i <= last_row; i++)
      {
        const TString* line = (const TString*)page.objptr(i);
        if (line != NULL)
          ofs << *line;
        ofs << endl;
      }
    } else
    if (str == "<pages/>")
    {
      TString8 str; str.format("%u", pages());
      TString& line = page.row(row);
      switch (_horizontal_alignment)
      {
      case 'C': line.overwrite(str, col+(wid-str.len())/2); break;
      case 'R': line.overwrite(str, col+wid-str.len()); break;
      default : line.overwrite(str, col); break;
      }      
    } else
    if (str == "<text>")
    {
      TString stringona;
      while (!ifs.eof())
      {
        ifs.getline(buffer, str.size());
        if (str == "</text>")
          break;
        stringona << str << '\n';
      }
      stringona.rtrim();
      TParagraph_string para(stringona, wid);
      for (int i = 0; i < hei && i < para.items(); i++)
      {
        const int j = row+i;
        if (page.objptr(j) == NULL)
          page.add("", j);
        str = para.get();
        TString& line = page.row(j);
        switch (_horizontal_alignment)
        {
        case 'C': line.overwrite(str, col+(wid-str.len())/2); break;
        case 'R': line.overwrite(str, col+wid-str.len()); break;
        default : line.overwrite(str, col); break;
        }      
      }
    } else
    if (str.starts_with("<text_align "))
    {
      sscanf(str, "<text_align horizontal=%c vertical=%c />", 
             &_horizontal_alignment, &_vertical_alignment);
      continue;
    }
  }

  return true;
}

bool TBook::init()
{
  _rcd = printer().get_printrcd();
  if (!xvt_print_is_valid(_rcd))
    return error_box(TR("Stampante non valida"));

  xvt_app_escape (XVT_ESC_GET_PRINTER_INFO, _rcd, &_ph, &_pw, &_pvr, &_phr);

  if (_pw <= 0 || _ph <= 0)
    return error_box(TR("Dimensioni pagina NULLE"));

  bool ok = true;
  if (_pvr < 96 || _phr < 96) // Risoluzione di Acrobat Writer
  {
    ok = yesno_box(TR("Stampante obsoleta o non adeguatamente configurata:\n"
                      "Risoluzione %ldx%ld. Continuare ugualmente?"), _phr, _pvr);
  }
  return ok;
}

bool TBook::main_loop()
{
  _print_aborted = true;
  if (!init())
    return false;

  TWindow_printer win(_rcd);
  if (!win.ok())
    return false;
  
  if (_pageto < _pagefrom)
    _pageto = pages();

  _print_aborted = false;
  for (size_t c = 0; c < _copies && !_print_aborted; c++)
  {
    for (size_t page = _pagefrom; page <= _pageto; page++)
    {
      if (xvt_print_open_page(_rcd))
      {
        print_page(win, page);
        xvt_print_close_page(_rcd);                  
      }
      else
      {
        _print_aborted = true;
        break;
      }
    }
  }

  return !_print_aborted;
}

static BOOLEAN main_loop_callback(long jolly)
{
  TBook* pp = (TBook*)jolly;
  return pp->main_loop();
}

bool TBook::print(size_t pagefrom, size_t pageto, size_t copies)
{
  if (pages() <= 0)
    return false;

  if (pagefrom == 0)
  {
    TPrinter& p = printer();
    TMask msk("bagn003");
    msk.set(F_PRINTER, p.printername());
    msk.set(F_FORM, p.get_form_name());
    msk.set(F_FONT, p.fontname());
    msk.set(F_SIZE, p.get_char_size());
    
    msk.set(F_ISGRAPHICS, p.isgraphics() ? "X" : "");
    msk.set(F_FROMPAGE, 1);
    msk.set(F_TOPAGE, pages());
    msk.set(F_COPIES, 1);
    if (msk.run() == K_ENTER)
    {
      _copies = msk.get_int(F_COPIES);
      _pagefrom = msk.get_int(F_FROMPAGE);
      _pageto = msk.get_int(F_TOPAGE);
    }
    else
      return false;
  }
  else
  {
    _pagefrom = pagefrom;
    _pageto = pageto;
    _copies = copies;
  }

  xvt_print_start_thread(main_loop_callback, (long)this);
  return true;
}

bool TBook::preview()
{
  TPreview_mask msk(this);
  const KEY k = msk.run();
  if (k != K_QUIT)
    print();
  return true;
}

bool TBook::print_or_preview()
{
  bool ok = true;
  switch (printer().printtype())
  {
  case screenvis: 
    ok = preview(); 
    break;
  case exportprinter:
    ok = export_text(printer().get_export_file());
    break;
  default:
    ok = print();
    break;
  }
  return ok;
}

TBook::TBook(const char* name) 
     : _out(NULL), _is_temporary(false),
       _pw(0), _ph(0), _pvr(0), _phr(0), 
       _pages(0), _page(0)
{
  _file = name;
  if (_file.blank())
  {
    _file.temp("rep");
    _is_temporary = true;
  }
}

TBook::~TBook()
{
  if (_out != NULL)
  {
    _out->close();
    delete _out;
    if (_is_temporary)
      xvt_fsys_removefile(_file);
  }
}

///////////////////////////////////////////////////////////
// TReport_book
///////////////////////////////////////////////////////////

// Converte da coordinate logiche (1/100 caratteri) a coordinate fisiche 
TPoint TReport_book::log2dev(const TPoint& ptlog) const
{
  TPoint ptdev;
  ptdev.x = (ptlog.x * _phr) / (100 * cpi());
  ptdev.y = (ptlog.y * _pvr) / (100 * lpi());
  return ptdev;
}

void TReport_book::define_frame(const TRectangle& r)
{
  TPoint ptlog = r; ptlog += _delta;
  TPoint szlog = r.size();
  const TRectangle rect(log2dev(ptlog), log2dev(szlog));
  TBook::define_frame(rect);
}

long TReport_book::print_section(char type, int level)
{
  long h = 0;
  TReport_section* rs = _report->find_section(type, level);
  if (rs != NULL)
    h = print_section(*rs);
  return h;
}

bool TReport_book::open_page()
{
  if (!TBook::open_page())
    return false;

  _report->set_page(++_rep_page, page());

  _page_break_allowed = false;
  _delta.reset();

  TReport_section* page_background = _report->find_section('B', 0);
  if (page_background != NULL)
  {
    _delta = page_background->pos();
    print_section(*page_background);
    _delta.reset();
  }

  if (_rep_page == 1)
    _delta.y += print_section('H', 1);

  TReport_section* page_head = _report->find_section('H', 0);
  if (page_head != NULL && (_rep_page > 1 || !page_head->hidden_if_needed()))
  {
    _delta += page_head->pos();
    _delta.y += print_section(*page_head);
    _delta.x = 0;
  }

  return true;
}

bool TReport_book::close_page()
{
  if (_page_is_open)
  {
    TReport_section* page_foot = _report->find_section('F', 0);
    if (page_foot != NULL && (!_is_last_page || !page_foot->hidden_if_needed()))
    {
      _delta.x = page_foot->pos().x;
      _delta.y = _logical_foot_pos;
      print_section(*page_foot);
    }
  }
  return TBook::close_page();
}

void TReport_book::reprint_group_headers(const TReport_section& rs)
{
  const int max_group = _report->find_max_level('H');
  for (int level = max_group; level >= 2; level--)
  {
    TReport_section& rs = _report->section('H', level);
    if (rs.repeat_on_page())
    {
      const long height = rs.compute_size().y; // Compute size after the initilization script!
      if (height > 0)
      {
        rs.print(*this);
        _delta.y += height;
      }
    }
  }

  if (rs.level() > 10) // E' una sottosezione
  {
    TPointer_array headers; // Elenco degli header di sottosezione che devo stampare
    for (int level = rs.level(); level > 10; level /= 10)
    {
      TReport_section& rs = _report->section('H', level);
      if (rs.repeat_on_page())
      {
        const long height = rs.compute_size().y; // Compute size after the initilization script!
        if (height > 0)
          headers.add(rs);
      }
    }
    for (int i = headers.last(); i >= 0; i--)  // Stampo in ordine livello
    {
      TReport_section& rs = (TReport_section&)headers[i];
      const long height = rs.compute_size().y;
      rs.print(*this);
      _delta.y += height;
    }
  }
}

long TReport_book::print_section(TReport_section& rs)
{
  if (_print_aborted)
    return 0;

  rs.load_fields();

  // Non sono sicuro se vada prima di load_fields o dopo execute_prescript
  if (rs.condition().not_empty())
  {
    TVariant var;
    _report->evaluate(rs.condition(), var, _nullfld);
    if (!var.as_bool())
      return 0;
  }

  if (!rs.execute_prescript())
  {
    _print_aborted = true;
    return 0;
  }

  const long height = rs.compute_size().y; // Compute size after the initilization script!

  if (height > 0) // Has some visible fields
  {
    bool page_break = _page_break_allowed && rs.page_break();
    long reprint_from = 0;
    if (!page_break)
    {
      long h = height;
      if (rs.keep_with_next())
        h += rs.report().section('B', 1).compute_size().y;
      const long space_left = _logical_foot_pos - _delta.y;
      page_break = h > space_left;

      // Controllo se la sezione puo' essere stampata su due pagine
      if (page_break && space_left >= 100 && rs.can_be_broken() )
      {
        reprint_from = space_left / 100 * 100;
        rs.print_clipped(*this, 0, reprint_from);
      }
    }
    if (page_break && rs.level() > 0) // Avoid recursion
    {
      close_page();
      open_page();
      if (rs.type() == 'B')
        reprint_group_headers(rs);
    }
    if (_page_is_open)
    {
      if (reprint_from > 0)
      {
        _delta.y -= reprint_from;
        rs.print_clipped(*this, reprint_from, height);
      }
      else
        rs.print(*this);
    }

    if (rs.level() > 0) // Ho stampato qualcosa che non sia lo sfondo!
      _page_break_allowed = true;
  }

  if (!rs.execute_postscript())
    _print_aborted = true;

  return height;
}

bool TReport_book::init(TReport& rep)
{
  if (!TBook::init())
    return false;

  _report = &rep;
  if (_report->use_printer_font())
    _report->load_printer_font();

  const TPoint siz = page_size();
  const TPoint res = page_res();

  const double pollici_pagina_y = (double)siz.y / (double)res.y;
  const double righe_pagina = pollici_pagina_y * lpi();
  _logical_page_height = long(righe_pagina*100.0);

  const double pollici_pagina_x = (double)siz.x / (double)res.x;
  const double colonne_pagina = pollici_pagina_x * cpi();
  _logical_page_width = long(colonne_pagina*100.0);
  
  const TReport_section& footer = _report->section('F',0);
  _logical_foot_pos = footer.pos().y;
  if (_logical_foot_pos <= 0)
  {
    const long logical_footer_height = footer.compute_size().y;
    _logical_foot_pos = _logical_page_height - logical_footer_height;  
  }

  return true;
}

void TReport_book::print_subsections(int father)
{
  for (int i = 1; i <= 9; i++)
  {
    const int level = father*10+i;
    TReport_section* rs = _report->find_section('B', level);
    if (rs == NULL)
      break;

    TRecordset* rex = rs->recordset();
    if (rex != NULL)
    {
      rex->requery();
      if (rex->items() > 0)
      {
        _delta.y += print_section('H', level);
        for (bool ok = rex->move_to(0); ok && !_print_aborted; ok = rex->move_next())
        {
          _delta.y += print_section('B', level);
          print_subsections(level);
        }
        _delta.y += print_section('F', level);
      }
    }
  }
}

bool TReport_book::add(TReport& rep, bool progind)
{
  if (!init(rep))
    return false;

  if (!_report->execute_prescript())
    return false;

  TRecordset* rex = _report->recordset();
  if (rex == NULL)
    return true;

  rex->requery();
  if (rex->items() <= 0)
    return true;

  TString msg = TR("Elaborazione report");
  msg << ' ' << _report->filename();
  
  TProgind* pi = NULL;
  if (progind)
    pi = new TProgind(rex->items(), msg, true, true);

  TString_array oldgroup, newgroup;
  const int max_group = _report->find_max_level('H');
  if (max_group >= 2)
  {
    for (int g = 2; g <= max_group; g++)
      oldgroup.add(EMPTY_STRING, g);
  }
  const int max_body = _report->find_max_level('B');
  int last_body_height = 0;
  
  _rep_page = 0; // Azzera numero di pagina relativo
  _is_last_page = false;
	
	bool ok = rex->move_to(0);
	
	open_page();
  for (; ok && !_print_aborted; ok = rex->move_next())
  {
    if (max_group >= 2) // Gestione raggruppamenti
    {
      int first_changed = 0;
      TVariant var;
      for (int g = 2; g <= max_group; g++)
      {
        const TString& expr = _report->section('H', g).grouped_by();
        _report->evaluate(expr, var, _alfafld);
        const TString& grp = var.as_string();
        newgroup.add(grp, g);
        if (newgroup.row(g) != oldgroup.row(g) || rex->current_row() == 0)
        {
          if (first_changed == 0)
            first_changed = g;
        }
      }
      if (first_changed)
      {
        oldgroup = newgroup;

        if (_delta.x > 0) // Devo tornare a capo!
          _delta.y += last_body_height;
        _delta.x = 0;
        
        if (rex->current_row() > 0)
        {
          for (int g = max_group; g >= first_changed ; g--)
            _delta.y += print_section('F', g);
        }
        for (int g = first_changed; g <= max_group; g++)
          _delta.y += print_section('H', g);
      }
    }
    // Stampa di tutti i body
    for (int b = 1; b <= max_body; b++)
    {
      const int dy = print_section('B', b);
      if (dy > 0) // Ho stampato qualcosa
      {
        // Cerco di vedere se e' possibile la stampa etichette
        int column_delta = 0;
        const int dx = _report->section('B', b).size().x;
        // Se dx > 0 ho una sezione a dimensione fissa
        if (dx > 0 && _delta.x+2*dx <= _logical_page_width)
        {
          column_delta = dx;
          last_body_height = dy;
        }
      
        if (column_delta > 0)
          _delta.x += column_delta;
        else
        {
          _delta.x = 0;
          _delta.y += dy;
          last_body_height = 0; // Non servirebbe strettamente
        }  

        // Stampa eventuali sottosezioni
        print_subsections(b);
      }
    }

    if (pi != NULL)
    {
      pi->addstatus(1);
      if (pi->iscancelled())
        _print_aborted = true;
    }

  }
  if (!_print_aborted)
  {
    // Devo stampare tutte le code degli eventuali raggrupamenti
    for (int g = max_group; g >= 2 ; g--)
      _delta.y += print_section('F', g);

    TReport_section* fl = _report->find_section('F',1);
    if (fl != NULL)          // Gestione footer last (se esite)
    {
      const int fy = fl->pos().y;
      if (fy > 0)            // Ha una coordinata y imposta
      {
        if (fy < _delta.y)   // Sono gia' andato oltre quindi salto pagina
        {
          close_page();
          open_page();
        }
        _delta.x = 0;
        _delta.y = fy;

        // Azzero temporaneamente le dimensioni del footer per evitare salti pagina
        const int lfp = _logical_foot_pos;
        _logical_foot_pos = _logical_page_height; 
        print_section(*fl);
        _logical_foot_pos = lfp;
      }
      else
        print_section(*fl);  // Stampa normale
    }

    _is_last_page = true;
    close_page();

    _report->execute_postscript();
  }

  if (pi != NULL)
    delete pi;

  return !_print_aborted;
}

int TReport_book::lpi() const
{
  if (_report != NULL)
    return _report->print_lpi();
  return TBook::lpi();
}

int TReport_book::cpi() const
{
  if (_report != NULL)
    return _report->print_cpi();
  return TBook::cpi();
}

bool TReport_book::print(size_t pagefrom, size_t pageto, size_t copies)
{
  if (pages() <= 0)
    return false;

  if (pagefrom <= 0)
  {
    TPrinter& p = printer();
    TMask msk("bagn003");
    msk.set(F_PRINTER, p.printername());
    if (_report != NULL)
    {
      msk.set(F_FORM, _report->filename());
      msk.set(F_FONT, _report->print_font().name());
      msk.set(F_SIZE, _report->print_font().size());
    }
    msk.set(F_ISGRAPHICS, p.isgraphics() ? "X" : "");
    msk.set(F_FROMPAGE, 1);
    msk.set(F_TOPAGE, page());
    msk.set(F_COPIES, 1);
    if (msk.run() == K_ENTER)
    {
      copies = msk.get_int(F_COPIES);
      pagefrom = msk.get_int(F_FROMPAGE);
      pageto = msk.get_int(F_TOPAGE);
    }
    else
      return false;
  }
  
  return TBook::print(pagefrom, pageto, copies);
}

bool TReport_book::on_link(const TReport_link& lnk)
{
  bool ok = false;
  if (_report != NULL)
    ok = _report->on_link(lnk);
  return ok;
}

TReport_book::TReport_book(const char* name) 
            : TBook(name), _report(NULL)
{
}