/* 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.
 */
#include <iostream.h>
#include <iomanip.h>
#include <fstream.h>
#include <strstream.h>

/* 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...
public:
  line (TPtr &l, istream &in);   // Costruttore che legge dallo stream ed inserisce in lista
};

char line::inbuf [MAXLEN];

line::line (TPtr &l, istream &in) : list<line> (l)
{
  colori = 0;                               // Lista inizialmente vuota
  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
  istrstream istr(inbuf);                   // Crea un istrstream con la linea letta
  while (istr)                              // Loop fino a che ho caratteri nell'istrstream
  {
    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);
    }
  }
}

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;
  }
  while (in)            // Finche' lo stream non e' in errore (EOF)
    new line (l,in);    // Costruisce una nuova 'line' estraendola dal file

  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;
}