// trattasi di -*-c++-*-
#ifndef __PRINTAPP_H
#define __PRINTAPP_H

#ifndef __APPLICATION_H
#include <applicat.h>
#endif

#ifndef __PRINTER_H
#include <printer.h>
#endif

#ifndef __RELATION_H
#include <relation.h>
#endif

// compatibility 
#define TPrintapp TPrint_application


enum print_action { REPEAT_PAGE, NEXT_PAGE };

// user functions to pass field informations to setrow()
// no class or nice C++ interface since varargs is nasty

// FLD(Num.logico, Nome campo [, da [, a]])
const char*  FLD(int lognum, const char* f, int from = -1, int to = -1);
// FLD(Num. logico, Nome campo numerico, Picture string)
const char*  FLD(int lognum, const char*  f, const char* picture);
// FLD(Nome tabella, Nome campo numerico, Picture string)
const char*  FLD(const char* tabname, const char*  f, const char* picture);
// FLD(Num.logico, Nome campo [, da [, a]])
const char*  FLD(const char* tabname, const char* f, int from = -1, int to = -1);

struct link_item {
  int           _logicnum;
  link_item*    _son;
  link_item*    _brother;
  int           _cnt;

  link_item(int l)
  { _logicnum = l; _son = NULL; _brother = NULL; _cnt = 0; }
};

class TProgind;


class TPrint_application : public TApplication
{
  TArray      _rows;      // rows descriptor
  TArray      _cursors;   // cursor array
  TCursor*    _cur;       // current cursor
  TArray      _transtab;  // field translation table
  TArray      _header;    // header lines
  TArray      _footer;    // footer lines
  int         _currow;    // current print row
  TPrintstyle _curstyle;  // current print style
  bool        _auto_ff;   // automatic form feed after each page
  const char* _wmess;     // wait message for progind
  bool        _wbar;      // bar y/n for progind
  bool        _wcancel;   // cancel button enabled
  int         _wthr;      // minimum # of items to show print progind
  const char* _confpr;    // config filename for printer
  char        _fillchar;  // fill character for empty fields
  link_item*  _pr_tree;   // functions for autom. printing of relations
  int         _maxrow;    // reference to maxrow
  int         _cur_file;  
  bool        _print_defined; 
  bool        _force_progind;
  bool        _force_setpage;
  bool        _print_zero;
  TProgind*   _prind;
  const char* _picture;
  MENU_TAG    _last_choice;
  int         _ncopies;
  bool        _repeat_print;
  bool        _cancelled;
  
  // set the printer
  void  set_printer() { printer().set(); }
  // print a single record; does not advance cursor
  // returns failure or success
  bool print_one(int file);
  // to be documented but very fig
  bool print_tree(link_item* head);

  static void _pp_header(TPrinter& pr);
  static void _pp_footer(TPrinter& pr);
  static void _pp_link(int id, const char* s);

  link_item* _look_print_node(link_item* head, int logicnum);
  void       _reset_tree(link_item* head);
  virtual bool create();
  virtual bool destroy();


protected:

  // ****************************************************
  // ISTRUZIONI PER l'USO
  // ****************************************************
  // 
  // La Printapp, saggiamente, consente di operare su uno
  // o piu' cursori stampando automaticamente anche files
  // collegati. La sequenza delle operazioni e' la seguente:
  //
  // 1) Derivare una classe da TPrint_application
  // 2) Implementare user_create() e user_destroy();
  //    Nella user_create() si creino i 
  //    necessari cursori, e li si dia in pasto a Printapp
  //    usando add_cursor(). Si puo' fare add_cursor(new TCursor(...))
  //    dato che il cursore viene distrutto automaticamente.
  // 3) Per ciascun file del cursore che si desidera porre
  //    nell'albero di stampa, si faccia add_file(logicnum [,from]);
  //    [from] sara' il file a cui e' collegato nella relazione.
  //    add_file VA FATTA anche per il file principale, se no
  //    non stampera' nulla;
  // *********************************************************
  // FUNZIONI VIRTUALI OBBLIGATORIE
  // *********************************************************
  // 4) Si definiscono le necessarie funzioni virtuali: e'
  //    sicuramente necessaria la set_page(file) nella quale
  //    si metteranno (sotto if o switch) le istruzioni 
  //    set_row (vedi sotto) corrispondenti alla pagina
  //    logica relativa a ciascun record di ogni file da stampare.
  //    Va definita anche set_print() in cui si presentera' ;a
  //    maschera di scelta se necessaria o una box yes_no;
  //    Ritornando TRUE da set_print la stampa viene eseguita 
  //    automaticamente (in genere ritorna FALSE se l'utente
  //    annulla la stampa con ESC.)
  //    
  //    Alla set_page, come alle pre_ e post_ process, viene
  //    passato 0 se il cursore attuale e' nullo (vedi sotto).
  // *********************************************************
  // FUNZIONI VIRTUALI FACOLTATIVE
  // *********************************************************
  // 5) Le varie funzioni pre_ e post_ process _page e _print
  //    vengono chiamate prima e dopo la stampa di ogni record
  //    o gruppo di record relativo alla relazione immessa;
  //    ad esempio, postprocess_print() viene chiamata per il
  //    file principale una volta dopo l'intera stampa; per
  //    un file collegato nella relazione, e' chiamata tante
  //    volte quanti gruppi di almeno un record esistono per
  //    record del file genitore. Qui si possono modificare
  //    righe, calcolare totali etc. A queste funzioni 
  //    viene sempre passato il file (logicnum) in corso di stampa e
  //    un contatore che indica quante volte la stampa e' stata
  //    ripetuta. le pre_ ritornano TRUE o FALSE, nell'ultimo
  //    caso interrompono la stampa; le post_ ritornano
  //    NEXT_PAGE (comportamento normale) o REPEAT_PAGE
  //    (indovina cosa fa).
  // 6) set_print() viene chiamata dalla voce Selezione,
  //    unica del secondo menu. E' il posto dove mettere
  //    una buona maschera di selezione di cosa stampare.
  //    Alla fine, si esegua enable_print_menu() per
  //    abilitare la voce Stampa, inizialmente inattiva.
  // 7) cancel_hook() permette di intercettare la 
  //    cancellazione della stampa; ritornando TRUE
  //    la stampa viene effettivamente cancellata
  //    Tutti i parametri relativi al progress indicator
  //    vengono settati da funzioni apposite (vedi oltre)
  // ****************************************************
  // Molte altre minchiatine (form feed automatici, header,
  // footer etc) sono spiegate nel seguito
  // ****************************************************

  virtual bool user_create()  pure;
  virtual bool user_destroy() pure;

  // set print, bound to menu :Selezione:Stampa
  // chiamata automaticamente dopo user_create()
  virtual bool set_print(int i = 1)        { return FALSE; }

  // set_row functions MUST be called here in a switch
  // for each particular file being printed
  virtual void set_page(int file, int cnt) {}
  
  // called before processing each page
  // used to set print strings from tables or files
  // not included in relation
  // returning FALSE cancels page printing
  // counter is the current print page number 
  virtual bool preprocess_page(int file, int counter) 
  { return TRUE; }

  // same before each print request
  // e.g. to initialize counters
  // returning FALSE cancels print request or subtree
  virtual bool preprocess_print(int file, int counter) 
  { return TRUE; }

  // postprocessing; returning REPEAT_PAGE reprints the
  // whole page (after all sons are printed) or print
  // counter is the current page or print number
  virtual print_action postprocess_page(int file, int counter)
  { return NEXT_PAGE; }
  virtual print_action postprocess_print(int file, int counter)
  { return NEXT_PAGE; }
  // executed after all print job is completed
  virtual void postclose_print() {}

  // called when LINK button is pressed with active selection in
  // preview window
  virtual void process_link(int id, const char* text) {}


  // called when user cancels print; returning TRUE 
  // actually stops printing; not called if no cancel
  virtual bool cancel_hook() {return TRUE;}
  
  // bound to TApplication print menu
  // redefined ONLY for very special purposes
  virtual void print();
  
  // bound to <select> menu and automatically called after
  // user_create()
  void    do_print(int n);

public:

  // --------------------------------------------------------------
  // COME SETTARE LE RIGHE DI STAMPA
  // --------------------------------------------------------------
  // setrow() si usa come una printf per settare le righe di stampa
  // che vengono stampate da print()
  // I codici per gli argomenti variabili sono di 3 tipi:
  // @ si usa per stampare campi di database o informazioni di controllo
  //   posizione carrello e font
  //   ACHTUNG: i codici di formato sono diversi da quelli di printf e
  //   sono elencati sotto. Per i campi di database occorre che il codice
  //   sia accoppiato ad una delle funzioni FLD() passata come argomento;
  //   questa provoca la stampa di campi della relazione corrente,
  //   posizionata come e' stato deciso nell'inizializzazione
  // % si usa esattamente come in una printf con un plus: se il codice di 
  //   formato e' maiuscolo (es. S per stringa, D per intero) viene
  //   ignorato il carattere di riempimento eventualmente specificato
  //   con set_fillchar. Cio' vale anche per i codici @ (vedi)
  //   E' possibile usare due codici aggiuntivi: r(R) e t(T). A questi
  //   va fatto seguire un PUNTATORE a real o a TString. Il formato 
  //   viene interpretato con le stesse regole di %t in dsprintf per real 
  //   (come %d o %f) e di %s per TString. Il puntatore NON
  //   viene memorizzato; per questo occorre il codice # (sotto).
  // # si usa come % (stessi codici di printf) ma memorizza gli argomenti
  //   per riferimento: ovvero, ogni volta che la riga viene stampata
  //   viene stampato il contenuto in quel momento (che si puo' cambiare
  //   in una delle pre- o post- process). Cio' implica che:
  //     1) gli argomenti vanno passati per RIFERIMENTO (set_row(1,"#5d",&i))
  //     2) i puntatori devono rimanere validi e costanti tra la set_row e
  //        la fine della stampa
  //   Quindi, attenzione a %s con TString ridimensionate; si possono
  //   usare solo se predimensionate alla dimensione massima, ma e' meglio
  //   usare char* o il codice apposito. I codici #r e #t prendono puntatori a
  //   real e a TString, memorizzandoli. Non ci sono problemi con la resize.
  //   Comunque, il modo corretto di adoperare il codice # e'
  //   usarlo solo per stampare MEMBRI della classe derivata da TPrint_application
  // ----------------------------------------------
  // field codes (match one of FLD() functions)
  //   @@               -> @
  //   @[n[,{l|c|r}]s   -> STRING: n = pad, lcr = alignment
  //   @{[n[.d=0]]|[n[,{l|c|r}]]p}n 
  //                    -> NUMBER: n = digits, d = decimals
  //                               p = picture string (first matching arg)
  //   @[l]d            -> DATE:   l = full year
  //   @f               -> BOOL:   prints si/no
  //   @[n,{l|c|r}]t    -> Translated field (must set translation)
  //   
  // Tutti questi codici possono essere usati anche maiuscoli, il che inibisce
  // l'uso del carattere di riempimento (set_fillchar) per uno specifico campo.
  // ---------------------------------------------
  // Per tutti i codici che riguardano la stampa di real (@n, %r, #r)
  // se non vengono date ulteriori specifiche di formato viene usata
  // una picture che e' "" per default, ma puo' essere modificata con
  // set_real_picture(). Anche questo e' assai carino.
  // Normalmente un real uguale a zero viene stampato come stringa vuota
  // a meno che non si specifichi set_print_zero([TRUE]).
  // ---------------------------------------------
  // codici posizionamento e movimento carrello
  //   @<n>g  vai a posizione n
  //   @<n>j  salta di n posizioni (in orizzontale)
  // codici stile
  //   @b     bold
  //   @i     italic
  //   @u     underlined
  //   @r     reset to normal
  // ---------------------------------------------------
  // CODICI COLORE PER VISUALIZZAZIONE E COLLEGAMENTO
  // ---------------------------------------------------
  // Se si vuole che in visualizzazione il testo sia colorato
  // si usa il codice $[]; tra le quadre si scrive il colore
  // di foreground, opzionalmente seguito da una virgola e dal
  // colore di background (bianco per default). I colori si 
  // specificano con un singolo carattere come segue:
  //    n       nero
  //    g       verde
  //    b       blu
  //    c       cyan
  //    y       giallo
  //    v       magenta
  //    m       colore background maschere (azzurrotto)
  //    d       grigio scuro
  //    l       grigio chiaro
  //    k       grigio normale
  // ------------------------------------------------------
  // Se si fa enable_link(..) con un certo colore, tutto
  // cio; che e' scritto in quel colore diventa selezionabile
  // e alla sua selezione (premendo 'Collega') si puo' associare
  // un'azione in process_link. A quest'ultima viene passata
  // l'ID ritornata da enable_link() e il testo selezionato alla
  // pressione di Collega.  Vedere ba6 e stampare l'elenco (con 
  // Includi ditte abilitato) per un esempio.
  // --------------------------------------------------------  

  void reset_row(int r);

  // chiamare reset_print() durante la stampa forza la
  // rilettura di set_page() alla prossima volta
  void reset_print();

  void set_row(int r, const char* fmt, ...);

  // ---------------------------------------------
  // set translation values for field
  // called once for each translation: example
  // set_translation(12,"STATOCIV","1","Celibe")
  // provoca la stampa automatica di stringhe al
  // posto di determinati valori dei campi se e' dato
  // il codice @t
  // Il posto giusto per chiamarla: user_create()
  // ---------------------------------------------
  void set_translation(int lognum, const char* field, 
                       const char* from, const char* to);


  // --------------------------------------------------------    
  // hypertext interface for viswin 
  // --------------------------------------------------------
  // Quando si vogliono abilitare determinati colori come 
  // indicatori di legame ipertestuale, si faccia enable_link
  // nella create. L' ID ritornato viene passato a process_link
  // assieme al testo selezionato
  int  find_link(const char* descr) const;
  int  enable_link (const char* descr, char fg, char bg = 'w');
  void disable_link(char fg, char bg = 'w');  
  void disable_links() { printer().links().destroy(); }   
  // se si setta multiple a TRUE anziche' la descrizione del testo selezionato
  // viene passata a enable_link una tokenstring con tutti i 'bottoni' dello
  // stesso colore presenti sulla riga
  void set_multiple_link(bool on);

  // BACKGROUND PAINTING! Chefigata! poi vi spiego....
  void set_background(const char* bgdesc = NULL);

  
  // ---------------------------------------------
  // set/select cursor
  // ---------------------------------------------
  // selects i-th cursor
  // inutile se c'e' un cursore solo
  void select_cursor(int i);

  // return i-th cursor without making it current
  TCursor* get_cursor(int i);
  // returns maximum row defined
  int      get_maxrow()   { return _maxrow; }

  // adds cursor to class; return identifier 
  // cursor* can be NULL: no file is used but
  // print_one is called and iterations are performed
  // by pre_ and post_ process
  int      add_cursor(TCursor* c);

  // retrieve current cursor
  TCursor* current_cursor() { return _cur; }

  // ---------------------------------------------
  // set_auto_ff(TRUE) fa si' che dopo ogni pagina logica (relativa ad
  // un record) si stampi un form feed. (anche se il cursore e' nullo)
  void set_auto_ff( bool b = TRUE) 
  { _auto_ff = b; }

  // il carattere specificato con set_fillchar (default ' ') viene
  // usato per riempire davanti e dietro i campi in cui si e' specificata
  // una dimensione maggiore della lunghezza effettiva del contenuto,
  // (a meno che non si sia usato un codice di formato maiuscolo)
  void set_fillchar(char c) 
  { _fillchar = c; }

  // riempie di righe vuote la pagina corrente fino alla dimensione
  // della pagina
  void fill_page(int from = -1);
  
  // di solito basta e avanza quella di default
  virtual bool menu(MENU_TAG m);
  
  virtual word class_id() const { return CLASS_PRINT_APPLICATION; }

  // print menu is enabled when set_print returns TRUE
  void enable_print_menu();
  void disable_print_menu();
  void enable_setprint_menu(); 
  void disable_setprint_menu(); 
  
  // header/footer (printf, not set_row analogues)
  // only understand @-codes for setting font attributes,
  // date and page number
  // plus every printf %-code
  
  // con queste si possono ridefinire header e footer al 
  // verificarsi di condizioni durante la stampa
  virtual void preprocess_header() {}
  virtual void preprocess_footer() {}
  
  void set_header(int row, const char* fmt, ...);
  void set_footer(int row, const char* fmt, ...);
  void reset_header();
  void reset_footer();
  
  // vedi sopra per capire
  void reset_files();
  void add_file(int file, int from = 0);
  void add_file(const char* tab, int from = 0);

  // set default picture for reals
  void set_real_picture(const char* p) { _picture = p;    }
  void set_print_zero(bool b = TRUE)   { _print_zero = b; }
  
  // progress indicator control
  void         set_wait_message(const char* m) 
  { _wmess = m;   }
  void         set_wait_bar(bool m) 
    // default yes
  { _wbar = m;    }
  void         set_wait_cancel(bool m)
    // default yes
  { _wcancel = m; }
  void         set_wait_threshold(int m)
    // minimum number of print items to show progress indicator;
    // default 2
  { _wthr = m;    }

  // questa forza la progind anche se si stampa su video
  void force_progind(bool b = TRUE)
  { _force_progind = b; }

  // questa forza la rilettura delle setrow in set_page ad ogni
  // record stampato, in modo che i dati siano
  // sempre quelli del record corrente anche se si usano codici %
  // s'intende che rallenta un po' la stampa
  void force_setpage(bool b = TRUE)
  { _force_setpage = b; }
  
  void set_config_file(const char* s) 
  { _confpr = s; }
  word get_page_number()       
  { return printer().getcurrentpage(); }
  void set_page_number(word n) 
  { printer().setcurrentpage(n); }                                 
  
  // dirige la stampa sul file specificato, preservando gli attributi di formato  
  // se header == TRUE si stampano su file anche gli header    
  void        set_export_file(const char* name, bool header = TRUE) 
  { printer().set_export_file(name,header); }
  // infila un file di export fatto da un'altra printer (con formati e tutto, ignora
  // gli header supponendo che siano gia' presenti nel file)
  // se header == TRUE gli header stanno nel file e non vengono stampati 
  // se direct == FALSE le rigne vengono aggiunte con set_row da printapp, altrimenti
  // si usa printer::merge_export_file
  void        merge_export_file(const char* file, bool header = TRUE, bool direct = FALSE);

  
  void set_n_copies(int n)  { _ncopies = n;         }
  int  get_n_copies()       const { return _ncopies;      }
  void repeat_print()      { _repeat_print = TRUE; }
  
  bool is_cancelled() const { return _cancelled;    }
  
  
  TPrint_application();
  virtual ~TPrint_application();
};

// buon divertimento


#endif