/* Programma html.cc:
 * Scrivere un programma C++ che, utilizzando le classi stream, legga un file di testo in HTML e: 
 *              Definisca una classe template 'lista' 
 *              Costruisca una classe 'linea' derivata da 'lista' che immagazzinerà una linea di 
 *                      testo del file (le linee non supereranno
 *              mai i 300 caratteri) 
 *              Inserisca in una lista le linee lette 
 *              Definisca una classe 'colore' che ha tre attributi interi chiamati R, G e B, derivata da lista 
 *              La classe 'linea' conterrà un puntatore alla classe 'colore', per gestire una lista di 'colori'. 
 *              Il costruttore della classe 'linea' eseguirà una scansione della linea stessa, alla ricerca di 
 *                      stringhe del tipo '#RRGGBB'
 *              che descrivono un colore. Ogni volta che individuerà una tale stringa, la analizzerà, interpretando 
 *                      ogni coppia di lettere
 *              (RR, GG e BB) come coppie di cifre esadecimali. 
 *              Per ogni stringa '#RRGGBB' costuirà una nuova istanza della classe 'colore' che conterrà i valori 
 *                      dei tre colori base
 *              estratti dal testo, inserendoli nella propria lista di colori. 
 *              Per finire verrà stampato il testo HRML, riga per riga, stampando dopo ogni riga l'elenco dei colori 
 *                      utilizzati in quella
 *              riga, in decimale, allineati ordinatamente.
 * Modificare il programma in modo che: 
 *              Il programma si fermi se incontra il colore di valore R=255 G=0 B=0. 
 *              Il costruttore di 'line' non individui direttamente i colori nella linea, ma richiami un metodo 
 *                 creacolore che ricevera' come parametro l'istrstream contenente la linea letta e creerà il 'colore' 
 *                 se ne trova uno 
 *              Il costruttore userà un loop infinito per leggere i colori, la terminazione avverrà tramite un'eccezione. 
 *              Il metodo creacolore potrà lanciare due eccezioni di tipo diverso: 
 *                 1.se l'istrstream va' in errore (se finisce la stringa) 
 *                 2.se viene letto il colore R=255 G=0 B=0 (valore 0xff0000) 
 *              Il costruttore di 'line' lancerà un'eccezione se il file di ingresso và in errore (EOF) 
 *              La funzione principale esegua un loop infinito per creare le istanze di 'line', ma 
 *                 1.Gestirà l'eccezione inviata dal costruttore di 'line' per 'EOF', terminando regolarmente 
 *                 2.Gestirà l'eccezione inviata da creacolore stampando un opportuno messaggio e terminando ancora regolarmente 
 */
#include <iostream.h>
#include <iomanip.h>
#include <fstream.h>
#include <strstream.h>

/* Classi per le eccezioni (costituiscono il tipo dell'eccezione)
 */
class lineEndException {  // Eccezione per 'fine linea'
};

class colorException {    // Eccezione per 'colore rosso'
};

class fileEndException {  // Eccezione per 'fine file'
};


/* Classe template list
 * Per consentire di gestire elementi di tipi diversi (derivati), con gli
 * opportuni tipi di ritorno e per i parametri, deve essere una classe
 * template.
 */
template <class T> class list {
  list *next;  // Puntatore al prossimo della catena
protected :
  // Definisco un tipo puntatore a classe finale, per costruire
  // variabili passate per riferimento (un puntatore non va bene)
  typedef T *TPtr;
  // Funzione che inserisce il nuovo elemento in coda alla lista
  void append (list *newEl) { if (next) next -> append (newEl); else next = newEl; };
  // Funzione virtuale pura che stampera' l'elemento
  virtual void print () = 0;
public :
  // Costruttore: si aggiunge in coda ad una lista 'base'
  list (TPtr &base) { next = 0; if (base) ((list*)base) -> append (this); else base = (T*) this;};
  // Funzione pubblica che stampa l'intera lista, se chiamata sulla base
  void printall () { print (); if (next) next -> printall ();};
  // Distruttore virtuale - distrugge tutti gli elementi della lista
  virtual ~list () { if (next) delete next; };
};

class colore : public list<colore> {
  int r,g,b; // I valori dei colori primari di questo colore
  // Metodo di stampa, stampa le componenti del colore
  virtual void print () { cout  <<  "Colore R=" << setw (3) << setfill ('0') << 
    r << " G="<< setw (3) << setfill ('0') << g << " B="<< setw (3) << setfill ('0') << b << endl; };
public:
  // Costruttore: Ininzializza la superclasse ed i colori primari
  colore (TPtr &l, int R, int G, int B) : list<colore> (l) { r=R; g=G; b=B; };
};

class line : public list<line> {
  static const int MAXLEN = 300; // Costante: dimensione del buffer
  static char inbuf [MAXLEN];    // Buffer statico per la lettura delle linee
  char *content;                 // Puntatore alla linea corrente
  colore *colori;                // Lista dei colori della linea
  virtual void print ();         // Funzione di stampa, ridefinisce la virtuale pura...
  // Metodo 'cercacolore' che lancia un'eccezione...
  void cercacolore (colore **colori, istrstream &istr);
public:
  line (TPtr &l);                // Costruttore che legge dallo stream ed inserisce in lista
  void build (istream &in);
};

char line::inbuf [MAXLEN];

/* Metodo che analizza l'istrstream passato come aprametro per individuare
 * un 'colore' (stringa '#RRGGBB'). Lancia un'eccezione 'lineEndException'
 * se lo stram finisce (fine linea) ed una 'colorException' se incontra il
 * colore rosso (#FF0000).
 */
void line::cercacolore (colore **colori, istrstream &istr)
{
colorException c;                         // Eccezione 'colore'
lineEndException e;                       // Eccezione 'fine linea'

  istr.ignore (MAXLEN,'#');               // Scarta caratteri fino al prossimo '#'
  if (istr)                               // Se con questo non ha esaurito la stringa
  {
  char colorbuf [7];                      // Buffer per i sei caratteri da leggere (+ null)
    istr.width (7);                       // indica al nostro strstream quanto e' lunga la nostra stringa
    istr >> colorbuf;                     // Estrae da istr 6 caratteri + 1 terminatore (visto che non ho spazi)
    istrstream icol (colorbuf);           // Creo un'altro istrstream per convertire il dato
    int col = -1;                         // Numero che conterra' i colori (tutti insieme)
    icol >> hex >> col;                   // Estraggo il numero HEX a 6 cifre dalla nostra stringa
    if (col >= 0)                         // Se ha letto veramente il dato (non era errato)
      // Creo un nuovo 'colore' spezzando il numero letto tramite rotazioni e maschere
      new colore (*colori, (col >> 16) & 0xff, (col >> 8) & 0xff, col & 0xff);
    if (col == 0xff0000)                  // Se riconosce il colore rosso (#ff0000)
      throw (c);                          // Lancia l'eccezione 'colorException'
  }
  else                                    // Se 'istr' e' in errore (fine linea)
    throw (e);                            // Lancia l'eccezione 'lineEndException'
}

/* Metodo che 'riempie' la struttura 'line' leggendo i dati dal file.
 * Occorre un metodo diverso dal costruttore, perche' se il costruttore viene
 * interrotto da un'eccezione, la struttura non viene creata.
 * Legge una linea dal file e la immagazzina, quindi costruisce
 * un istrstream sulla linea e lo alanizza con cercacolore
 */
void line::build (istream &in)
{
  in.getline (inbuf, MAXLEN);               // Legge la linea dallo stream
  content = new char [strlen (inbuf) + 1];  // Alloca lo spazio per la linea
  if (content)                              // Se l'allocazione ha avuto successo
    strcpy (content, inbuf);                // Immagazzina la linea letta

  if (!in)                                  // Se e' finito il file 
  {
  fileEndException e;                       // (variabile per lanciare l'eccezione)
    throw (e);                              // Lancia l'eccezione 'fileEndException'
  }

  istrstream istr(inbuf);                   // Crea un istrstream con la linea letta

  try {                                     // --- try block ---
    while (1)                               // Loop infinito (interrotto dall'eccezione)
    {
      cercacolore (&colori, istr);          // Chiama la funzione che estrae un colore
    }
  } catch (lineEndException e) {            // Intercetta l'eccezione 'lineEndException'
    return;                                 // ma non l'eccezione 'colorException'
  }
}

/* Costruttore che inserisce l'elemento nella lista (tramite i costruttore di 'list')
 * ed azzera gli attributi */
line::line (TPtr &l) : list<line> (l)
{
  colori = 0;                               // Lista inizialmente vuota
  content = 0;
}

void line::print ()
{
  if (content)               // Se ha la linea
    cout << content << endl; // La stampa
  if (colori)                // Se ha la lista dei colori
    colori -> printall ();   // Stampa anche quella
}

int main (int argc, char **argv)
{
line *l = 0;
  if (argc != 2)
  {
    cerr << "Il programma va chiamato con un nome di file come parametro" << endl;
    return -1;
  }
  ifstream in (argv [1]); // Costruisce un nuovo ifstream a partire dal nome del file
  if (!in)                // Se e' in stato di errore, non c'era il file
  {
    cerr << "Il file " << argv [1] << " non esiste" << endl;
    return -1;
  }

  try {                                // --- Try block ---
    while (1)                          // loop infinito (verra' interrotto dall'eccezione)
    {
      (new line (l)) -> build (in);    // Costruisce una nuova 'line' e poi la inizializza con il contenuto del file
    }
  } catch (fileEndException e) {       // Eccezione di 'fine file' prodotta da 'build'
    cout << "File terminato normalmente" << endl;
  } catch (colorException e) {         // Eccezione di 'colore rosso' prodotta da 'cercacolore'
    cout << "Incontrato il colore per terminare" << endl;
  }

  if (l)                // Se ha creato la lista di linee
    l -> printall ();   // La stampa
  else
    cout << "Il file " << argv [1] << " era vuoto" << endl;

  in.close();           // Chiude il file (sarebbe chiuso comunque alla distruzione do 'in')
  return 0;
}