/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *

   "Compilatore di profili" per la gestione vendite.
   Considerando:
   - Profilo documento
   - Configurazione vendite per la ditta
   - Condizioni atmosferiche
   genera le maschere appropriate.
   Accetta sulla riga di comando il nome del profilo documento per cui deve generare 
   la maschera. Il nome del file .MSK viene dedotto dal profilo documento stesso.
   
   * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */

#ifndef __CHECKS_H
#include <checks.h>
#endif

#ifndef __URLDEFID_H
#include <urldefid.h>
#endif

#ifndef __FSTREAM_H
#include <fstream.h>
#endif

#ifndef __SCANNER_H
#include <scanner.h>
#endif

#ifndef __CONFIG_H
#include <config.h>
#endif

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

#ifndef __UTILITY_H
#include <utility.h>
#endif

#ifndef __PROGIND_H
#include <progind.h>
#endif

#ifndef __TABUTIL_H
#include <tabutil.h>
#endif

#ifndef __DEFMASK_H
#include <defmask.h>
#endif

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

#ifndef __VEUML_H
#include "veuml.h"
#endif

#ifndef __VEUML1_H
#include "veuml1.h"
#endif

#ifndef __LFFILES_H
#include "lffiles.h"
#endif

#ifndef __VEINI_H
#include "veini.h"
#endif

#ifndef __VE0100_H
#include "ve0100.h"
#endif


// Significato delle colonne nella tabella _tab0300b
#define B_NOME                                                                    0
#define B_MSKTYPE                                                                 1
#define B_MSKSIZE                                                                 2
#define B_DESCR                                                                   3
#define B_WIDTH                                                                   4

// Significato dei campi nel profilo del documento
#define P_STATO                                                                   0
#define P_PROMPT                                                                  1
#define P_HELP                                                                    2
#define P_WARNING                                                                 3


// Numero massimo di linee per pagina
#define MAX_LINES_PER_PAGE                                18
#define MASK_FIRST_LINE                                    5 

#define GROUPKEY(x)   format( "%5d", x )

class TMaschera : public TObject
{
private:
  
  // Linea corrente
  int _curline;

  // Pagina corrente
  int _curpage;
  
public:

  void advance_line( ){ _curline ++; };
  
  // Genera una serie di righe uguali per funzione ( DISPLAY, ecc. )
  void outline( const TString& s = "", const TString& prefix = "", const char sep = '~' );

  // Crea un controllo del tipo specificato
  void control( const int type, const int id = DLG_NULL, const int size = -1 );

  // CHECKTYPE
  void check( const int i );
  
  // PAGE
  void pagina( const int i, const TString& title = "Pagina " ){ _out << "PA \"" << title << " " << i << "\" 11 60 14\n"; };
  
  // PROMPT
  void prompt( const int x, const int y, const TString& s = "" ){ _out << "PR " << x << " " << y << " \"" << s << "\"\n"; };
  
  // BEGIN
  void begin( void ) { outline ( "BE" ); };

  // END
  void end( void ) { outline ( "EN" ); };

  // ENDMASK
  void endmask( void ) { outline ( "ENDMASK" ); };

  // FLAG
  void flag( const TString& s ) { _out << "FLAG \"" << s << "\"\n"; };

  // HELP
  void help( const TString& s ) { _out << "HE \"" << s << "\"\n"; };

  // WARNING
  void warning( const TString& s ) { _out << "WA \"" << s << "\"\n"; };

  // GROUP
  void group( const int g ) { _out << "GR " << g << "\n"; };

  // FIELD
  void field( const TString& s ) { outline( s, "FI "); };

  // ITEM
  void item( const TString& s );

  // MESSAGE
  void message( const TString& s ) { outline( s, "ME "); };

  // USE
  void use( const TString& s ){ outline( s, "US " ); };
  void use( const int file, const int key ){ _out << "US " << file << " KEY " << key << "\n"; };

  // DISPLAY
  void display( const TString& s ){ outline( s, "DI " ); };

  // INPUT
  void input( const TString& s ){ outline( s, "IN " ); };

  // OUTPUT
  void output( const TString& s ){ outline( s, "OU " ); };

  // Funzioni per la lettura/scrittura di CURPAGE
  int page( ) { return _curpage; };
  void page( const int i ){ _curpage = i; };
  
  // Funzioni per la lettura/scrittura di CURLINE
  int line( ) { return _curline; };
  void line( const int i ){ _curline = i; };

  // Costruttore, vuole il nome del file .INI
  TMaschera( const TFilename outfile );
  
  // Vero se la pagina è aperta
  bool _pageopen;
  
  // File .MSK di output
  ofstream _out;
  
};



class TMask_generator
{

private:

  // Identificatore corrente per lo sheet
  int _curid;

  // vero se il gruppo è aperto
  bool _groupopen;

  // nome del file di .INI di input
  TFilename _proname;
  
  // File .INI di riferimento per i campi
  TConfig _fieldsini;                   
  
  // File .INI di riferimento per i gruppi
  TConfig _groupsini;                   
  
  // File .INI di input
  TConfig* _pro;
  
  TConfig _ditta;

  // Tabelle per la generazione
  
  // Campi dei documenti
  TString_array _fields;
  
  // Campi dello sheet
  TString_array _tab0300b;
  
  TAssoc_array _gruppi;
  
  TMaschera *_m;

protected:

  // Carica un file di tabella in un TString array
  void carica_tabella( const TFilename& fn, TString_array& tabella );
  
  // Scrive sul file di output una serie di righe uguali per funzione ( DISPLAY, ecc. )
  void outline( const TString& s = "", const TString& prefix = "", const char sep = '~' );


  // Crea un controllo del tipo specificato
  void control( const int type, const int id = DLG_NULL, const int size = -1 );

  // Ritorna vero se una componente della maschera è presente
  int present( const int i, TToken_string line ){ const TString s = line.get( i ); return !(s.blank( )); };
  
  // Carica dati dei campi
  void carica_dati_campi( const TFilename& fn, TAssoc_array& a );

  // Funzioni di accesso alle variabili private
  // ------------------------------------------

  // Funzioni per la lettura/scrittura di ID. Si incrementa da solo.
  int id ( ) { return( _curid++ ); };
  void id( const int i ){ _curid = i; };
  
  
  // Funzioni di generazione ad alto livello
  // ---------------------------------------

  // Genera un campo a partire da una linea del file VE0300A.DAT
  void genera_campo( TToken_string _line );

  // Genera un campo dello sheet a partire da una linea del file VE0300B.DAT
  void genera_campo_sheet( TToken_string _line );

  
  // Genera la colonna dello sheet a partire da una linea del file VE0300B.DAT
  void genera_item_sheet( TToken_string _line );

public:
  
  
  TMaschera& maschera( ){ return *_m; }  
  
  // Costruttore, vuole il nome del file .INI
  TMask_generator( const TString& profilo );

  // Attiva la generazione della maschera
  void genera( const TString&  );
  
  // Genera l'intestazione di una pagina ( non la prima )
  void intestazione_pagina( );
  
  TConfig& profilo( ){ return *_pro; }
  TConfig& ditta( ){ return _ditta; }
  TConfig& groupsini( ){ return _groupsini; }
  TConfig& fieldsini( ){ return _fieldsini; }
  
  // Distruttore
  virtual ~TMask_generator( );

};


TMaschera::TMaschera( const TFilename outfile ) : _out( outfile )
{
  _curline = 1;
  _curpage = 1;
  _pageopen = FALSE;
  bool _groupopen = FALSE;
}                         

void TMaschera::outline( const TString& s, const TString& prefix, const char sep )
{ 
  if ( s.left( 1 ) == "@" )
    _out << "CO " << prefix << s.mid( 1 ) << "\n";
  else 
  { 
    TToken_string u( s, sep );
    for ( int i = 0; i < u.items( ); i ++ )
      _out << prefix << u.get( i ) << "\n";
  }
}

void TMaschera::check( const int i )
{ 
  switch( i ) 
  {
  case S_OBBLIGATORIO:
    outline ( "CH REQUIRED" );
    break;
  default:
    outline ( "CH NORMAL" );
    break;
  }
}

void TMaschera::control( const int type, const int id, const int size )
{ 
  switch ( type )
  { 
  case T_DATA: 
    _out << "DA"; 
    break;
  case T_STRINGA:
    _out << "ST";
    break;
  case T_CHECKBOX:
    _out << "BO";
    break;                 
  case T_NUMERO:
    _out << "NU";
    break;
  case T_LISTA:
    _out << "LI";
    break;
  case T_MEMO:
    _out << "ME";
    break;
  case T_CORNICE:
    _out << "GR";
    break;
  case T_BOTTONE:
    _out << "BU";
    break;    
  }
  _out << " " << id;
  if ( size > 0 )
  { 
    if ( size > 100 )
      _out << " " << ( int )( size / 100 ) << " " << ( size % 100 );
    else 
      _out << " " << size;
  }
  _out << "\n";
}

void TMaschera::item( const TString& str ) 
{ 
  int i, j;
  TToken_string u( str, '@' );
  TToken_string s;
  
  for( i = 0; i < u.items( ); i ++ )
  { 
    _out << "IT \"" << s[ 0 ] << "\"\n";
    for( j = 1; j < s.items( ); j ++ )
      _out << "ME " << s[ j ] << "\n";     
  }
}

class TField;

class TGruppo : public TObject
{
protected:
  
  int _id;
  int _height;
  bool _present;
  TArray _fields;
  TMask_generator * _mg;
  
public:

  TMaschera& maschera( ){ return generatore( ).maschera( ); }
  TMask_generator& generatore( ){ CHECK( _mg, "Generatore nullo!" ); return *_mg; }
  void generatore( TMask_generator& mg ){ _mg = &mg; }
  void id( int id ){ _id = id; }
  int id( ){ return _id; }
  void height( int h ){ _height = h; }
  int height( ){ return _height; }
  void present( bool p ){ _present = p; }
  bool present( ){ return _present; }
  TArray& campi( ){ return _fields; }
  
  TGruppo ( int i );
  TGruppo ( int i, int height );
  void genera( );
  void add( TField * field );
  
};

class TField : public TObject
{ 
protected:
  
  TString _nome;
  // Gruppo di appartenenza
  TGruppo *_grp;
  
  int _gruppo;  
  int _id;
  int _type;
  int _size;
  int _stato;
  int _x, _y;
  TString _prompt, _flag, _help, _warning, _fieldname;
  TToken_string _use, _input, _display, _output, _special, _items, _configs;
  
public:
  
  TField( TString& campo, TConfig& pro );
  TField( );
  
  void genera( );
  void configura( TConfig& pro );

  void x( int x ){ _x = x; }
  void y( int y ){ _y = y; }
  void gruppo( int gruppo ){ _gruppo = gruppo; }                                      
  void grp( TGruppo& grp ){ _grp = &grp; }
  void id( int id ){ _id = id; }
  void type( int type ){ _type = type; }
  void size( int size ){ _size = size; }
  void stato( int stato ){ _stato = stato; }
  int x( ){ return _x; }
  int y( ){ return _y; }                                
  int gruppo( ){ return _gruppo; }
  TGruppo& grp( ) const { return *_grp; }
  int id( ){ return _id; }
  int type( ){ return _type; }
  int size( ){ return _size; }
  int stato( ){ return _stato; }
  int stato_reale( );
  
  
  void nome( TString& nome ){ _nome = nome; }
  void prompt( TString& prompt ){ _prompt = prompt; }
  void flag( TString& flag ){ _flag = flag; }
  void help( TString& help ){ _help = help; }
  void warning( TString& warning ){ _warning = warning; }
  void fieldname( TString& fieldname ){ _fieldname = fieldname; }
  TString& nome( ){ return _nome; }
  TString& prompt( ){ return _prompt; }
  TString& flag( ){ return _flag; }
  TString& help( ){ return _help; }
  TString& warning( ){ return _warning; }
  TString& fieldname( ){ return _fieldname; }
  
  void use( TToken_string& use ){ _use = use; }
  void input( TToken_string& input ){ _input = input; }
  void display( TToken_string& display ){ _display = display; }
  void output( TToken_string& output ){ _output = output; }
  void special( TToken_string& special ){ _special = special; }
  void items( TToken_string& items ){ _items = items; }
  void configs( TToken_string& configs ){ _configs = configs; }
  TToken_string& use( ){ return _use; }
  TToken_string& input( ){ return _input; }
  TToken_string& display( ){ return _display; }
  TToken_string& output( ){ return _output; }
  TToken_string& special( ){ return _special; }
  TToken_string& items( ){ return _items; }
  TToken_string& configs( ){ return _configs; }  
  
  TConfig& profilo( ) { return grp( ).generatore( ).profilo( ); }
  TConfig& ditta( ) { return grp( ).generatore( ).ditta( ); }
  TConfig& groupsini( ) { return grp( ).generatore( ).groupsini( ); }
  TConfig& fieldsini( ) { return grp( ).generatore( ).fieldsini( ); }
  
};

void TGruppo::add( TField * field )
{ 
  _fields.add( field ); 
  field->grp( *this ); 
}

TGruppo::TGruppo( int i )
{
  TConfig pro( "ve0300c.ini" );
  _id = i;
  _height = pro.get_int( "HEIGHT", format( "%d", i ) );
}

TGruppo::TGruppo( int i, int height )
{
  _id = i;
  _height = height;
}

static void genera_campo( const TObject& campo )
{ 
  // Per evitare 3000 casts ...
  TField& cmp = ( TField& )campo;
  
  cmp.configura( cmp.grp( ).generatore( ).profilo( ) );
  cmp.genera( );  
}

static void verifica_campo( const TObject& campo )
{ 
  // Per evitare 3000 casts ...
  TField& cmp = ( TField& )campo;
  
  int stato  = cmp.stato_reale( );
  cmp.stato( stato );
  if( cmp.stato( ) != S_NASCOSTO )
    cmp.grp( ).present( TRUE );
}

static void genera_gruppo( const TObject& gruppo )
{ 
  // Per evitare 3000 casts ...
  TGruppo& grp = ( TGruppo& )gruppo;
  
  grp.present( FALSE );
  grp.campi( ).for_each( verifica_campo );
  if( grp.present( ) )
    grp.genera( );
}

void TGruppo::genera( )
{  
  // Se il gruppo non sta sulla pagina ...
  int oldlinea = maschera( ).line( );
  if (  oldlinea + _height > MAX_LINES_PER_PAGE )
  {
    // Passa alla pagina successiva
    maschera( ).end( );
    generatore( ).intestazione_pagina( );
    maschera( ).line( MASK_FIRST_LINE );
    oldlinea = MASK_FIRST_LINE;
  }
  _fields.for_each( genera_campo );
  maschera( ).line( oldlinea + _height );
}

TField::TField( TString& campo, TConfig& pro ) : _nome( campo )
{
  _nome.trim( );
  _fieldname =  pro.get( "FIELDNAME", campo );
  _x = _gruppo = pro.get_int( "X", campo );
  _y = _gruppo = pro.get_int( "Y", campo );
  _gruppo = pro.get_int( "GROUP", campo );
  CHECK( _gruppo != 0, "Gruppo uguale a 0" );
  _id = pro.get_int( "MSKID", campo );
  CHECK( _id != 0, "Campo con MSKID uguale a 0" );
  _type = pro.get_int( "TYPE", campo );
  _size = pro.get_int( "SIZE", campo );
  _prompt = pro.get( "PROMPT", campo );
  if( _prompt[ 0 ] == '\"' )
  {
    _prompt.ltrim( 1 );
    _prompt.rtrim( 1 );
  };
  _flag = pro.get( "FLAG", campo );
  _help = pro.get( "FLAG", campo );
  _warning = pro.get( "FLAG", campo );
  _use = pro.get( "USE", campo );
  _use.separator( '~' );
  _input = pro.get( "INPUT", campo );
  _input.separator( '~' );
  _display = pro.get( "DISPLAY", campo );
  _display.separator( '~' );
  _output = pro.get( "OUTPUT", campo );
  _output.separator( '~' );
  _special = pro.get( "SPECIAL", campo );
  _special.separator( '~' );
  _configs = pro.get( "CONFIGS", campo );
  _configs.separator( '~' );
  _items = "";
  int last = pro.get_int( "NITEMS", campo );
  for( int i = 1; i <= last; i ++ )
    _items.add( pro.get( "ITEM", campo, i ) );
};

TField::TField( ) 
{
  _nome = "";
  _fieldname = "";
  _x = 0;
  _y = 0;
  _gruppo = 0;
  _id = 0;
  _type = 0;
  _size = 0;
  _prompt = "";
  _flag = "";
  _help = "";
  _warning = "";
  _use = "";
  _use.separator( '~' );
  _input = "";
  _input.separator( '~' );
  _display = "";
  _display.separator( '~' );
  _output = "";
  _output.separator( '~' );
  _special = "";
  _special.separator( '~' );
  _configs = "";
  _configs.separator( '~' );
  _items = "";
  _items.separator( '@' );   
}

int TField::stato_reale( )
{
  // Strategia al 28/08/95
  // Se il campo, nel file .INI di definizione dei campi, è legato a qualche voce in
  // configurazione, legge il suo stato, altrimenti assume il massimo.
  // Va poi a verificare nel .INI da compilare, e verifica li' la impostazione.
  // Controlla poi la impostazione per il gruppo, come impostato nel file .INI
  // e nella sezione CONFIGS nel .INI di definizione dei gruppi.
  // Utilizza poi la minore delle due.
  TToken_string config( fieldsini( ).get( "CONFIGS", _nome ) );
  TString reale;
  int items = config.items( );
  int stato = S_OBBLIGATORIO;
  for ( int i = 0; i < items; i += 3 )
  { TString chiave = config.get( i );
    TString valore = config.get( i + 1 );
    TString reale = ditta( ).get( chiave, "ve" );
    int pos = chiave.find( '[' );
    if ( pos >= 0 )
    {
      TString index = chiave.sub( pos + 1, chiave.find( ']' ) );
      chiave = chiave.sub( 0, pos );
      reale = ditta( ).get( chiave, "ve", atoi( index ) );        
    }
    else
      reale = ditta( ).get( chiave, "ve" );
    if ( reale == valore )
      stato = atoi( ( char * ) config.get( i + 2 ) );
  };
  TToken_string riga_profilo( grp( ).generatore( ).profilo( ).get( _nome, "PROFILO" ) );
  int stato_profilo = riga_profilo.get_int( P_STATO );
  stato = ( stato < stato_profilo )? stato : stato_profilo;
  TString gruppo ( fieldsini( ).get( "GROUP", _nome ) );
  gruppo.trim( );
  config = ( groupsini( ).get( "CONFIGS", gruppo ) );
  items = config.items( );
  int stato_gruppo = S_OBBLIGATORIO;
  for ( i = 0; i < items; i += 3 )
  { TString chiave = config.get( i );
    TString valore = config.get( i + 1 );
    reale = ditta( ).get( chiave, "ve" );
    if ( reale == valore )
       stato_gruppo = atoi( ( char * ) config.get( i + 2 ) );        
  };
  stato = ( stato < stato_gruppo )? stato : stato_gruppo;
  int stato_gruppo_profilo = grp( ).generatore( ).profilo( ).get_int( gruppo, "PROFILOGRUPPO" );
  stato = ( stato < stato_gruppo_profilo )? stato : stato_gruppo_profilo;
  return stato;  
}

void TField::configura( TConfig& pro )
{
  TToken_string riga_profilo( pro.get( _nome, "PROFILO" ) );
  TString newprompt( riga_profilo.get( P_PROMPT ) ) ;
  TString newhelp( riga_profilo.get( P_HELP ) ) ;
  TString newwarning( riga_profilo.get( P_WARNING ) ) ;
  if ( ! newprompt.blank( ) )
    _prompt = newprompt;
  if ( ! newhelp.blank( ) )
    _help = newhelp;
  if ( ! newwarning.blank( ) )
    _warning = newwarning;
}

void TField::genera( )
{  
  TMaschera& m = grp( ).maschera( );
  TString s;
  
  if ( _stato == S_NASCOSTO )
    return;
  if ( _size != 0 )
    m.control( _type, _id, _size );
  else
    m.control( _type, _id );
  m.begin( );
  switch( _stato )
  { 
  case S_DISABILITATO :
    m.prompt( _x, _y + m.line( ), _prompt );
    s = "D";
    s << _flag;
    m.flag( s ) ;
    break;
  case S_NORMALE :
  case S_OBBLIGATORIO :
    m.prompt( _x, _y + m.line( ), _prompt );
    m.flag( _flag );
    break;
  }
  m.check( _stato );
  if ( !_fieldname.blank( ) ) m.field( _fieldname );
  if ( !_items.blank( ) ) m.item( _items );
  if ( !_use.blank( ) ) m.use( _use );
  if ( !_input.blank( ) ) m.input( _input );
  if ( !_display.blank( ) ) m.display( _display );
  if ( !_output.blank( ) ) m.output( _output );
  if ( !_help.blank( ) ) m.help( _help );
  if ( !_warning.blank( ) ) m.warning( _warning );
  if ( !_special.blank( ) ) m.outline( _special );                    
  m.end( );
  if ( ( m.line( ) > MAX_LINES_PER_PAGE ) && ( m._pageopen == TRUE)) 
  { 
    m.end( );
    m._pageopen = FALSE;
    m.line( MASK_FIRST_LINE );
  }
}    

// Definizione dei metodi di_generator
// -----------------------------------------

TMask_generator::TMask_generator( const TString& profilo ) : _fieldsini( "ve0300a.ini" ), _groupsini( "ve0300c.ini" ), _ditta( CONFIG_DITTA )
{  
  CHECK( fexist( "ve0300a.ini" ), "Il file ve0300a.ini non esiste!" );
  _fieldsini.list_paragraphs( _fields );
  CHECK( fexist( "ve0300b.dat" ), "Il file ve0300b.dat non esiste!" );
  carica_tabella( "ve0300b.dat", _tab0300b );
  carica_dati_campi( "ve0300a.ini", _gruppi );
  TString16 param( profilo );
  param.lower( );
  if( param == "-all" )
  {
    TRelation rel("%TIP");
    TCursor t( &rel );
    long count = t.items( );
    TProgind bar( count, "Generazione delle maschere", FALSE, TRUE);    
    for( t = 0; t.pos( ) < count; ++t )
    {
      genera( t.curr( ).get( "S4" ) );      
      bar.addstatus(1);
    }  
  }
  else genera( profilo );
}

TMask_generator::~TMask_generator( )
{ 
  
};

/*

   void TMask_generator::carica_ordine_gruppi( )
   {
   int last, i;
   last = _gruppi.items( );
   for( i = 1; i <= last; i ++ )
   {
   TString16 chiavepro, chiavegruppi;
   chiavepro.format( "%d", i );
   const int gruppo( _pro->get_int( chiavepro, "ORDINEGRUPPI" ) ); 
   chiavegruppo.format( "%5d", gruppo );
   ( ( TGruppo& )_gruppi[ chiavegruppo ] ).ordine( i );
   }
   last = _pro->get( "NGRUPPI", "ORDINEGRUPPI" );
   for( i = 1; i <= last; i ++ )
   {
   TString16 chiavepro, chiavegruppi;
   chiavepro.format( "%d", i );
   const int gruppo( _pro->get_int( chiavepro, "ORDINEGRUPPI" ) ); 
   chiavegruppo.format( "%5d", gruppo );
   ( ( TGruppo& )_gruppi[ chiavegruppo ] ).ordine( i );
   }
   }
   */

void TMask_generator::carica_dati_campi( const TFilename& fn, TAssoc_array& a )
{
  TScanner infile( fn );
  TString line;
  TField *campo = NULL;
  TGruppo *gruppo = NULL;
  TString16 curgruppo( "" );
  
  while ( infile.ok( ) )
  { 
    line = infile.line( );
    line.trim( );
    while ( ( line.empty( ) || line[ 0 ] == '#' || line.left( 2 ) == "//" ) && infile.ok( ) )
    { 
      line = infile.line( );
      line.trim( );
    }
    if( infile.ok( ) )
    { 
      if ( line[ 0 ] == '[' ) // Carica un un campo
      { 
        line.strip( "[]" );
        if ( campo ) 
        {
          TString16 fieldkey = GROUPKEY( campo->gruppo( ) );
          if ( fieldkey != curgruppo )
          {
            if ( gruppo )
            {
              a.add( curgruppo, gruppo );
            }
            TString16 taggruppo;
            taggruppo.format( "%d", campo->gruppo( ) );
            int hgruppo = _groupsini.get_int( "HEIGHT", taggruppo );
            gruppo = new TGruppo( campo->gruppo( ), hgruppo );
            gruppo->generatore( *this );
          }
          gruppo->add( campo );
          curgruppo = GROUPKEY( campo->gruppo( ) );
        }
        campo = new TField( );
        campo->nome( line );        
      }
      else
      { 
        int pos;
        if( ( pos = line.find( '=' ) ) == 0 )
          yesnofatal_box( "Linea errata nel file.INI: %s", (const char *) line ); 
        else
        {
          TString key = line.sub( 0, pos );
          key.trim( );
          TString value = line.mid( pos + 1 );
          value.trim( );
          if( key == "X" )
            campo->x( atoi( value ) ); 
          else if( key == "Y" ) 
            campo->y( atoi( value ) );
          else if( key == "GROUP" ) 
            campo->gruppo( atoi( value ) );
          else if( key == "FIELDNAME" ) 
            campo->fieldname( value );
          else if( key == "MSKID" ) 
            campo->id( atoi( value ) );
          else if( key == "TYPE" ) 
            campo->type( atoi( value ) );
          else if( key == "SIZE" ) 
            campo->size( atoi( value ) );
          else if( key == "PROMPT" ) 
          {  
            if( value[ 0 ] == '"' )
            { 
              value.rtrim( 1 );
              value.ltrim( 1 );
            };
            campo->prompt( value );
          }
          else if( key == "FLAG" ) 
            campo->flag( value );
          else if( key == "USE" ) 
            campo->use( (TToken_string & ) value );
          else if( key == "INPUT" ) 
            campo->input( (TToken_string & )value );
          else if( key == "DISPLAY" ) 
            campo->display( (TToken_string & )value );
          else if( key == "OUTPUT" ) 
            campo->output( (TToken_string & )value );
          else if( key == "WARNING" ) 
            campo->warning( value );
          else if( key == "HELP" ) 
            campo->help( value );
          else if( key == "SPECIAL" ) 
            campo->special( (TToken_string & )value );
          else if( key == "ITEMS" )
            campo->items( (TToken_string & )value );
          else if( key == "CONFIGS" ) 
            campo->configs( (TToken_string & )value );
          else yesnofatal_box( "Chiave non trovata(%s)", (const char *) key );
        }
      }
    }
  }
  if ( campo ) 
  {
    if( format( "%5d", campo->gruppo( ) ) != curgruppo )
    {
      if( gruppo )
      {
        a.add( curgruppo, gruppo );
      }
      TString16 taggruppo;
      taggruppo.format( "%d", campo->gruppo( ) );
      int hgruppo = _groupsini.get_int( "HEIGHT", taggruppo );
      gruppo = new TGruppo( campo->gruppo( ), hgruppo );
      gruppo->generatore( *this );
    }
    gruppo->add( campo );
    curgruppo = GROUPKEY( campo->gruppo( ) );
    a.add( curgruppo, gruppo );    
  }
}

void TMask_generator::carica_tabella( const TFilename& fn, TString_array& tabella )
{ 
  TScanner in( fn  );
  TToken_string line = in.line( );
  while( line.not_empty( ) )   
  { // Attacca tutte le righe che finiscono con §
    while ( line.right( 1 ) == "§" )
    { line.rtrim( 1 );
      line << in.line( );
    };
    tabella.add( line );
    line = in.line( );     
  }
}

void TMask_generator::genera_campo_sheet( TToken_string _line )
{
  const int type( _line.get_int( B_MSKTYPE ) );
  _m->control( type, id( ), _line.get_int( B_MSKSIZE ) );
  _m->begin( );
  _m->prompt( 2, 2 );
  _m->field( _line.get( B_NOME ) );
  _m->end( );
}

void TMask_generator::genera_item_sheet( TToken_string _line )
{
  _m->_out << "IT \"" << _line.get( B_DESCR );
  _m->_out << "@" << _line.get_int( B_WIDTH ) << "\"\n";
}

void TMask_generator::intestazione_pagina( )
{
  _m->page( _m->page( ) + 1 );
  _m->pagina( _m->page( ), _pro->get( "TYPE", "MAIN" ) );
  
  _m->control( T_CORNICE, DLG_NULL, 7805 );
  _m->begin( );
  _m->prompt( 1, 0, "" );
  _m->end( );
  
  _m->control( T_STRINGA, DLG_NULL, 50 );
  _m->begin( );
  _m->prompt( 2, 1, "Tipo doc.  " );
  _m->flag( "DU" );
  _m->group( 5 );
  _m->end( );
  
  _m->control( T_NUMERO, DLG_NULL, 6 );
  _m->begin( );
  _m->prompt( 2, 2, "Doc.:  nr. " );
  _m->group( 6 );
  _m->flag( "D" );
  _m->end( );
  
  _m->control( T_DATA );
  _m->begin( );
  _m->prompt( 24, 2 );
  _m->group( 7 );
  _m->flag( "D" );
  _m->end( );
    
  _m->control( T_STRINGA, DLG_NULL, 50 );
  _m->begin( );
  if ( _pro->get( "TIPOCF", "MAIN" ) == "C" )
    _m->prompt( 2, 3, "Cliente    " );
  else
    _m->prompt( 2, 3, "Fornitore  " );
  _m->flag( "D" );
  _m->group( 11 );
  _m->end( );
  
}

void TMask_generator::genera( const TString& profilo )
{  
  int i, last;
  TString temp_s;
  TFilename proname( profilo ),
  // All'inizio il gruppo è 'chiuso'
  _groupopen = FALSE;

  // Forza l'estensione al profilo
  proname.ext( "ini" );

  // Se il file di profilo non esiste, esci con un errore fatale
  if ( !fexist( proname ) )
    fatal_box( "Il file %s non esiste!", ( const  char * )proname );
  
  _pro = new TConfig( proname );
  
  TFilename _mskname( _pro->get( "MSKFILE", "MAIN") );
  _mskname.ext( "msk" );
  
  // Mi serve per ordinare i gruppi a seconda del documento
  TAssoc_array grouporder;
  
  _m = new TMaschera( _mskname );
  
  // Definizione della toolbar
  _m->outline( "TOOLBAR \"\" 0 20 0 2" );
  
  _m->control( T_BOTTONE, 18, 802 );
  _m->begin( );
  _m->prompt( -15, -1,  "~Registra" );
  _m->message( "EXIT,20082" );
  _m->end( );
  
  _m->control( T_BOTTONE, 17, 802 );
  _m->begin( );
  _m->prompt( -25, -1, "~Elimina" );
  _m->message( "EXIT,127" );
  _m->end( );
  
  _m->control( T_BOTTONE, 99, 802 );
  _m->begin( );
  _m->prompt( -35, -1, "E~labora" );
  _m->message( "EXIT,345" );
  _m->end( );
  
  _m->control( T_BOTTONE, 24, 802 );
  _m->begin( );
  _m->prompt( -45, -1 );
  _m->message( "EXIT,346" );
  _m->end( );
  
  _m->control( T_BOTTONE, 2, 802 );
  _m->begin( );
  _m->prompt( -55, -1 );
  _m->message( "EXIT,27" );
  _m->end( );
  
  // End della toolbar
  _m->end( );
  
  
  // Header della prima pagina
  
  _m->pagina( _m->page( ), _pro->get( "TYPE", "MAIN" ) );
  
  _m->control( T_CORNICE, DLG_NULL, 7806 );
  _m->begin( );
  _m->prompt( 1, 0, "" );
  _m->end( );
  
  _m->control( T_STRINGA, F_CODNUM, 4 );
  _m->begin( );
  _m->prompt( 2, 1, "Cod. num.  " );
  _m->field( "CODNUM" );
  _m->flag( "GDUZ" );
  _m->message( "CO,2@" );
  _m->end( );

  _m->control( T_STRINGA, F_DESNUM, 50 );
  _m->begin( );
  _m->prompt( 24, 1 );
  _m->flag( "GDU" );
  _m->message( "CO,3@" );
  _m->end( );
  
  _m->control( T_STRINGA, F_TIPODOC, 4 );
  _m->begin( );
  _m->field( "TIPODOC" );
  _m->prompt( 2, 2, "Tipo doc.  " );
  _m->flag( "GDU" );
  _m->message( "CO,4@" );
  _m->end( );
  
  _m->control( T_STRINGA, F_TIPOCF, 1 );
  _m->begin( );
  _m->prompt( 2, 2, "Tipo CF " );
  _m->field( "TIPOCF" );
  _m->flag( "H" );
  _m->end( );
  
  _m->control( T_STRINGA, F_PROVV, 1 );
  _m->begin( );
  _m->prompt( 2, 2, "<D><P>  " );
  _m->field( "PROVV" );
  _m->flag( "H" );
  _m->end( );
  
  _m->control( T_STRINGA, F_DESTIPODOC, 50 );
  _m->begin( );
  _m->prompt( 24, 2 );
  _m->flag( "GDU" );
  _m->message( "CO,5@" );
  _m->end( );
  
  _m->control( T_NUMERO, F_NDOC, 6 );
  _m->begin( );
  _m->prompt( 2, 3, "Doc.:  nr. " );
  _m->field( "NDOC" );
  _m->message( "CO,6@" );
  _m->flag( "GDR" );
  _m->end( );
  
  _m->control( T_DATA, F_DATADOC );
  _m->begin( );
  _m->prompt( 24, 3, "Data    " );
  _m->field( "DATADOC" );
  _m->message( "CO,7@" );
  _m->flag( "A" );
  _m->end( );
  
  _m->control( T_NUMERO, F_STATO, 1 );
  _m->begin( );
  _m->prompt( 50, 3, "Stato " );
  _m->field( "STATO" );
  _m->message( "CO,8@" );
  _m->flag( "D" );
  _m->end( );
  
  _m->control( T_STRINGA, F_ANNO, 4 );
  _m->begin( );
  _m->prompt( 66, 3, "Es. " );
  _m->field( "ANNO" );
  _m->flag( "GD" );
  _m->message( "CO,9@" );
  _m->end( );
  
  _m->control( T_STRINGA, F_CODCF, 6 );
  _m->begin( );
  if ( _pro->get( "TIPOCF", "MAIN" ) == "C" )
  {
    _m->prompt( 2, 4, "Cliente    " );
    _m->warning( "Cliente assente" );
    _m->help( "Codice del cliente del documento" );    
  }
  else
  {
    _m->prompt( 2, 4, "Fornitore  " );
    _m->warning( "Fornitore assente" );
    _m->help( "Codice del fornitore del documento" );
  }
  _m->flag( "R" );
  _m->field( "CODCF" );
  _m->group( 1 );
  _m->use( LF_CLIFO, 1 );
  temp_s.format( "TIPOCF \"C\"~CODCF %d", F_CODCF);  
  _m->input( temp_s );
  _m->display( "\"Codice\" CODCF~\"Ragione Sociale@50\" RAGSOC~\"Partita IVA@12\" PAIV~\"Sospeso\" SOSPESO" );
  temp_s.format( "%d CODCF~%d RAGSOC", F_CODCF, F_RAGSOC );
  _m->output( temp_s );
  _m->check( S_OBBLIGATORIO );
  _m->outline( "ADD RU cg0 -1 C" );
  _m->message( "CO,10@" );
  _m->end( );
  
  _m->control( T_STRINGA, F_RAGSOC, 50 );
  _m->begin( );
  if ( _pro->get( "TIPOCF", "MAIN" ) == "C" )
  {
    _m->warning( "Cliente assente" );
    _m->help( "Ragione sociale del cliente del documento" );    
  }
  else
  {
    _m->warning( "Fornitore assente" );
    _m->help( "Ragione sociale del fornitore del documento" );
  }
  _m->prompt( 24, 4, "" );
  _m->group( 1 );
  _m->use( LF_CLIFO, 2 );
  temp_s.format( "TIPOCF \"C\"~RAGSOC %d", F_RAGSOC );  
  _m->input( temp_s );
  _m->display( "\"Ragione Sociale@50\" RAGSOC~\"Partita IVA@12\" PAIV~\"Codice\" CODCF" );
  temp_s.format( "%d CODCF~%d RAGSOC", F_CODCF, F_RAGSOC );
  _m->output( temp_s );
  _m->check( S_OBBLIGATORIO );
  _m->outline( "ADD RU cg0 -1 C" );  
  _m->message( "CO,11@" );
  _m->end( );
  _m->line( MASK_FIRST_LINE + 1 );
  
  _m->_pageopen = TRUE;
  
  // Generazione
  
  // carica_ordine_gruppi( );  
  // Scorro l'array dei gruppi, e ne creo una copia ordinata
  // con solo i gruppi visibili
  
  TArray sortedgroups;
  TString16 chiave, chiavegruppo;
  last = _pro->get_int( "NGROUPS", "ORDINEGRUPPI" );
  for( i = 1; i <= last; i ++ )
  {
    chiave.format( "%d", i );
    chiavegruppo.GROUPKEY( _pro->get_int( chiave, "ORDINEGRUPPI" ) );
    if( _gruppi.is_key( chiavegruppo ) )
      sortedgroups.add( &_gruppi[ (chiavegruppo) ] );        
  }
  sortedgroups.for_each( genera_gruppo );
  if ( _m->_pageopen == TRUE ) 
  { 
    _m->end( );
    intestazione_pagina( );
  }
  
  /*
     
     last = _campi.items( );
     for( i = 0; i < last; i ++ )
     {  
     TString nome = (( TField & )_campi[ i ]).nome( );
     int stato = stato_del_campo( nome );
     if( stato != S_NASCOSTO )
     { 
     const int addgruppo = (( TField & )_campi[ i ]).gruppo( );
     const int ordine = 0; //ordine_del_gruppo( addgruppo );
     if( ordine )
     {
     TString16 chiave;
     chiave.format( "%5d", addgruppo );
     if ( !gruppi.is_key( chiave ) )
     gruppi.add( chiave, new TGruppo( addgruppo, _groupsini ) );
     }
     else
     {
     // Se il gruppo non compare nell'elenco dei gruppi, 
     stato = S_NASCOSTO;
     }    
     }
     (( TField & )_campi[ i ]).stato( stato );
     // (( TField & )_campi[ i ]).ordine( ordine );
     };

     _campi.sort( );  
     // Generazione vera e propria
     _m->_pageopen = TRUE;
     last = _campi.items( );
     int curgruppo = 0;
     for( i = 0; i < last; i ++ )
     { 
     if ( _m->_pageopen == FALSE)
     { 
     intestazione_pagina( );
     _m->_pageopen = TRUE;
     }
     TField& campo = ( TField& ) _campi[ i ];
     if ( campo.gruppo( ) != curgruppo )
     {
     TString16 chiave;
     chiave.format( "%5d", campo.gruppo( ) );
     if ( gruppi.is_key( chiave ) )
     {
     TGruppo& gruppo = ( TGruppo& ) gruppi[ chiave ];
     if ( _m->line( ) + gruppo.height( ) > MAX_LINES_PER_PAGE )
     {
     _m->end( );
     intestazione_pagina( );
     _m->line( MASK_FIRST_LINE ); 
     }
     else
     {
     TString16 vecchiachiave;
     vecchiachiave.format( "%5d", curgruppo );
     if ( gruppi.is_key( vecchiachiave ) )
     {
     _m->line( _m->line( ) + ( ( TGruppo& ) gruppi[ vecchiachiave ]).height( ) );
     }
     }    
     }
     curgruppo = campo.gruppo( );
     }
     campo.configura( *_pro );
     campo.genera( *_m );
     }
     if ( _m->_pageopen == TRUE ) _m->end( );
     intestazione_pagina( );
     
     */
  
  // Generazione del listbox per i tipi di riga da aggiungere nello sheet   
  _m->control ( T_LISTA, F_LBTIPORIGA, 30 );
  _m->begin( );      
  _m->prompt( 2, 6, "Tipo riga da aggiungere " );
  _m->end( );

  // Generazione dello sheet : vedi il profilo
  TScanner sheet_in( "ve0300b.dat" );
  _m->_out << "SPREADSHEET " << F_SHEET << " 76 6 \n";
  // _m->_out << "SPREADSHEET " << F_SHEET << " 76 10 \n";
  _m->begin( );
  _m->prompt( 2, 7 );
  for( i = 0; i < _tab0300b.items( ); i ++ )
    genera_item_sheet( _tab0300b.row( i ) );
  _m->end( );
  
  // Campo memo per mostrare le righe
  _m->control( T_MEMO, F_MEMORIGA, 7604 );
  _m->begin( );      
  _m->prompt( 2, 14, " " );
  _m->field( "34->DESCEST" );
  _m->flag( "D" );
  _m->end( );
  
  _m->end( );
  
  // Generazione pagina dei piedi
  TToken_string s(_pro->get( "PROGPIEDE", "MAIN" ) );
  TTable tab( "PPD" );
  if ( s.not_empty( ) )
  { 
    intestazione_pagina( );
    TString16 codpiede( s.get( ) );
    int cp = 1;
    while ( !codpiede.empty( ) )
    { 
      tab.zero( );
      tab.put( "CODTAB", codpiede );
      if ( tab.read( ) == NOERR )
      {
        TString picture( tab.get( "S3" ) );
        _m->control( T_NUMERO, BASE_PIEDE + tab.get_int( "I0" ), 1805 );
        _m->begin( );
        TString header = tab.get( "S1" );
        header.trim( );
        while( header.len( ) < 40 - picture.len( ) ) 
          header << " ";
        _m->prompt( 2, 7 + cp, header );
        _m->flag( "D" );
        _m->end( );
        cp ++;
        if( cp + MASK_FIRST_LINE > MAX_LINES_PER_PAGE )
        {
          _m->end( );
          intestazione_pagina( );
          _m->line( MASK_FIRST_LINE );
          cp = 1;
        }
      }
      codpiede = s.get( );
    }
    _m->end( );    
  };
  _m->endmask( );
  
  // Stategia al 30/06/95
  // Creo una maschera con tutte le colonne, poi, a RUNTIME, 
  // dal motore, nascondo e adatto le colonne.
  
  // Generazione maschera dello sheet
  _m->_out << "PA \"Pagina 1\" 8 5 64 13\n";
  
  id( 101 );
  for( i = 0; i < _tab0300b.items( ); i ++ )
    genera_campo_sheet( _tab0300b.row( i ) );
  _m->end( );
  _m->end( );
  _m->endmask( );
  delete _m;
  delete _pro;
}


// Applicazione guscio

class TGenMask_application : public TApplication
{
  
protected:
  
  virtual bool create ( );
  virtual bool destroy( );
  // virtual bool menu( MENU_TAG );  // Controlla il menu
  
}; 


bool TGenMask_application::create( )

{
  TApplication::create( );
  TMask_generator a( argv( 2 ) );
  return FALSE;
}

bool TGenMask_application::destroy( )
{
  return TApplication::destroy() ;
}

int ve0300( int argc, char** argv )
{
  
  if ( argc < 3 )
    fatal_box( "Sintassi: VE0 -3 <Nome file di profilo>" );
  
  TGenMask_application a;
  a.run ( argc, argv, "Generazione in corso ..." );
  return 0;    
}
