#include <applicat.h>
#include <printer.h>                  
#include <printwin.h>                  
#include <xvtility.h>

HIDDEN int LEN_SPACES(WINDOW win, int x)
{
  HIDDEN long w = 0L;
  if (x < 0)
  {
    x = 80;
    w = 0L;
  }  
  if (w == 0L)
  {                                    
    TString256 spc; spc.fill('m', 132);
    w = xvt_dwin_get_text_width(win,(char*)(const char*)spc, 132);
  }
  const int k = int((w*x) / 132);
  
#ifdef DBG
  static bool error_on = TRUE;
  if (error_on)
  {
    TString256 spc; spc.fill('m', x);
    const int k1 = xvt_dwin_get_text_width(win,(char*)(const char*)spc,x);
    if (k != k1) 
      error_on = error_box("Maguire disagrees: %d != %d", k, k1);
  }  
#endif
  
  return k;
}

void TPrintwin::paint_background(long j)
{
  const bool isbackground = _bg->items() > 0 && printer().isgraphics();
  if (!isbackground) return;     
  
  int rw = (int)(j % _formlen);
  TString& rwd = (TString&)(*_bg)[rw];  
  int cnt = 0; char ch;  
  
  char curcol  = 'n';
  char curpen  = 'n';
  char curpat  = 'n';
  char curwid  = '1';

  unsigned int x1, y1, x2, y2, id;  
  PNT b, e;
  
  while (ch = rwd[cnt++])
  {           
    switch(ch)
    {
    case 'v':  // verticale intera
      x1  = (byte)rwd[cnt++]-1;
      b.h = e.h = LEN_SPACES(win(), x1)+LEN_SPACES(win(), 1)/2+_hofs;
      b.v = rw * _chary + _vofs; e.v = rw * _chary + _vofs;
      xvt_dwin_draw_set_pos(win(),b);
      xvt_dwin_draw_line(win(),e);
      break;   
    case 'o':  // verticale pezzo sopra
      x1  = (byte)rwd[cnt++]-1;
      b.h = e.h = LEN_SPACES(win(), x1)+LEN_SPACES(win(), 1)/2 + _hofs;
      b.v = rw * _chary + _vofs; e.v = rw * _chary - _chary/2 + _vofs; 
      xvt_dwin_draw_set_pos(win(),b);                 
      xvt_dwin_draw_line(win(),e); 
      break;   
    case 'u':  // verticale pezzo sotto
      x1 = (byte)rwd[cnt++]-1;
      b.h = e.h = LEN_SPACES(win(), x1)+LEN_SPACES(win(), 1)/2 + _hofs;
      b.v = rw*_chary + _chary/2 + _vofs; e.v = rw * _chary + _vofs; 
      xvt_dwin_draw_set_pos(win(),b);                          
      xvt_dwin_draw_line(win(),e); 
      break;   
    case 'h':  // orizzontale intera
      x1 = (byte)rwd[cnt++]-1;
      x2 = (byte)rwd[cnt++]-1;  
      b.v = e.v = rw*_chary + _chary/2 + _vofs;
      b.h = LEN_SPACES(win(), x1)+_hofs; e.h = LEN_SPACES(win(), x2)+_hofs;
      xvt_dwin_draw_set_pos(win(),b);
      xvt_dwin_draw_line(win(),e); 
      break;   
    case 'r':  // orizzontale scorciata agli estremi
      x1 = (byte)rwd[cnt++]-1;
      x2 = (byte)rwd[cnt++]-1;
      b.v = e.v = rw*_chary + _chary/2 + _vofs;
      b.h = LEN_SPACES(win(), x1)+LEN_SPACES(win(), 1)/2 + _hofs; 
      e.h = LEN_SPACES(win(), x2)+LEN_SPACES(win(), 1)/2+_hofs;
      xvt_dwin_draw_set_pos(win(),b);
      xvt_dwin_draw_line(win(),e); 
      break;       
    case 'i':
      id = (byte)(rwd[cnt++])-1;   // Numero immagine
      y1 = (byte)(rwd[cnt++])-1;   // Riga sorgente
      x1 = (byte)(rwd[cnt++])-1;   // Colonna destinazione
      x2 = (byte)(rwd[cnt++]);     // Larghezza destinazione (in caratteri)
      y2 = (byte)(rwd[cnt++]);     // Altezza destinazione (in caratteri)
      if (id >= 0)
      {     
        const short width = LEN_SPACES(win(), x2); // Larghezza in pixel
        
        TImage* i = (TImage*)_images.objptr(id);
        if (i == NULL)
        {
          const TString_array& a = printer().image_names();
          const TImage src(a.row(id));
          if (src.ok())
          {
            i = new TImage(src, width, _chary*y2);
            _images.add(i, id);
          }
        }  
        
        if (i && i->ok())
        {     
          RCT src; xvt_rect_set(&src, 0, int(_chary*y1), 
                                width, min(int(_chary*(y1+1)), i->height()));
          if (src.top < i->height())
          {
            PNT p;                                                              
            p.h = LEN_SPACES(win(), x1) + _hofs; 
            p.v = _chary*rw + _vofs;
            RCT dst = src; xvt_rect_set_pos(&dst, p);
            i->draw(win(), dst, src);
          }  
        }  
      }  
      break;
    case 'W':
      curwid = rwd[cnt++];
      set_pen(trans_color(curcol), curwid- '0', trans_brush(curpat),
              trans_pen(curpen));
      break;
    case 'P':
      curpen = rwd[cnt++];
      set_pen(trans_color(curcol), curwid- '0', trans_brush(curpat),
              trans_pen(curpen));
      break;
    case 'B':
      curpat = rwd[cnt++];
      set_pen(trans_color(curcol), curwid- '0', trans_brush(curpat),
              trans_pen(curpen));
      break;
    case 'C':
      curcol = rwd[cnt++];
      set_pen(trans_color(curcol), curwid- '0', trans_brush(curpat),
              trans_pen(curpen));
      break;
    default:
      break;
    }
  }                    
  // restore default pen
  set_pen(COLOR_BLACK);
}

void TPrintwin::paint_row(long j)
{
  const int row = (int)(j % _formlen);
  const int y = row*_chary + _chary - _descent + _vofs;
  
  if (_chary > 1)
  {  
    paint_background(j);
    
    _txt.read_line(j);
    
    int pos = 0;
    
    const char* cp;
    while((cp = _txt.piece()) != NULL) 
    {     
#if XVT_OS != XVT_OS_SCOUNIX         
      const int st = _txt.get_style();
      set_font(printer().fontname(), st, _char_size);                 
      
      const COLOR bg = trans_color(_txt.get_background());
      const COLOR fg = trans_color(_txt.get_foreground());
      set_color(fg, bg);
#else
      set_color(COLOR_BLACK, COLOR_WHITE);
#endif      
      
      const char* beg = cp;                    
      while (*beg)
      {                            
        for (; *beg == ' '; beg++)               // Salta spazi iniziali
          pos++;      
        
        if (*beg)
        {  
          int len = 0;
          for (const char * end = beg; *end && (*end != ' ' || *(end+1) != ' '); end++) 
            len++;                                 // Misura sringa da stampare
          xvt_dwin_draw_text(win(), _hofs + LEN_SPACES(win(), pos), y, (char*)beg, len);                           
          pos += len;                   
          beg = end;
        }
      }  
    }
  } else
  {                                           
    set_font(printer().fontname(), XVT_FS_NONE, _char_size);                 
    TString s(_txt.line(j));                
#if XVT_OS == XVT_OS_WIN
    if ((j % _realformlen) == 0 && (j != 0)) // Questa e' la patch per TTY.DRV.
      s.insert("\n");                        // Quando uscira' un nuovo driver, si dovra' controllare 
#endif                                       // se esiste ancora questo piccolo bug.
    xvt_dwin_draw_text(win(), 0, y, (char*)(const char*)s, -1);                           
  }        
}                   

// @mfunc Permette di stampare un rettangolo a video
//
// @rdesc Ritorna se e' riuscito a stampare in una unica pagina
bool TPrintwin::print_band(
  int page,     // @parm Numero della pagina in cui stampare
  RCT& r)       // @parm Parte di finestra da stampare

  // @comm Di solito viene disegnata l'intera pagina, ma la cosa dipende dal driver di stampa
{
  const long j   = ((long)page) * _formlen;
  const int rows = (r.bottom - r.top) / _chary;  
  const int top  = r.top / _chary;  
  
  for (int k = top; k < top+rows; k++) 
  {
    if ((j+k) < _txt.lines() && k < _formlen) 
      paint_row(j+k);
    else break;
  }
  return j+k < _txt.lines();
}

// @mfunc Inizia la stampa
//
// @rdesc Ritorna se viene interrotta la stampa:
//
// @flag TRUE | La stampa e' andata a buon fine
// @flag FALSE | La stampa e' stata interrotta
bool TPrintwin::do_print()

  // @comm Quando possibile parte un processo concorrente (dipende dal sistema operativo)

{                    
  int page = 0; 
  RCT* rct;
  bool ok = TRUE;          
  
#ifdef DBG
  const long size = xvt_dwin_get_font_size_mapped(win());
#endif   
  while (ok && !_aborted)
  {
    _aborted = !(bool)xvt_print_open_page(_printrcd);                  
    while (!_aborted && ok 
           && (rct = xvt_print_get_next_band()) != NULL)
    {
      set_font(XVT_FFN_SYSTEM, XVT_FS_NONE, _char_size);                 
      set_font(printer().fontname(), XVT_FS_NONE, _char_size);                 
#ifdef DBG
      long size1 = xvt_dwin_get_font_size_mapped(win());
      CHECK(size == size1, "Failed to set font. xvt bugs???");
#endif   
      ok = print_band(page, *rct); 
    }
    _aborted |= !(bool)xvt_print_close_page(_printrcd); 
    page++;
  }     
  return !_aborted;             
}                                         


TPrintwin::TPrintwin(TTextfile& txt) 
: _txt(txt), _inited(FALSE), _aborted(FALSE)
{
  TPrinter& p = printer();

#if XVT_OS != XVT_OS_SCOUNIX
  _printrcd = p.get_printrcd();
  
  WINDOW prwin = xvt_print_create_win(_printrcd, (char*)(const char*)main_app().title());
  if (prwin == NULL_WIN) 
  {
    _aborted = TRUE;
    return;
  }
  set_win(prwin);                                                                    
#endif  
  
  _char_size = p.get_char_size();   
  set_font(p.fontname(), XVT_FS_NONE, _char_size);           
  LEN_SPACES(win(), -1);          // force update 
  
#if XVT_OS != XVT_OS_SCOUNIX
  p.set_win_formlen(prwin);       // Calcola offset e altre misure pagina

  xvt_dwin_get_font_metrics(prwin, &_lead, &_ascent, &_descent);
  _bg        = &p.getbgdesc();
  _chary     = p.get_dots_per_line(); 
  _hofs      = p.get_horz_offset();
  _vofs      = p.get_vert_offset();
#endif

  _formlen   = p.formlen();
  
  if (p.is_generic())
  {
    long pw, ph, phr, pvr; // Printer width, height, horizontal and vertical resolution
    xvt_app_escape (XVT_ESC_GET_PRINTER_INFO, p.get_printrcd(), &ph, &pw, &pvr, &phr);
    if (pvr != 0)
      _realformlen = int(ph * p.get_lines_per_inch() / pvr);
    else
      _realformlen = 66;
  }
  else
    _realformlen = 66; // Anche se non e' importante settarlo in altri casi.
  _formwidth = p.formwidth();
  _inited = TRUE;
}


TPrintwin::~TPrintwin()
{
  if (_inited && win() != NULL_WIN) 
    xvt_vobj_destroy(win());
  set_win(NULL_WIN);
}


