/* albero.cc
 * Scrivere un programma che definisca un albero binario il cui nodo contiene, 
 * come dati, due interi. Il primo intero sarà la chiave, mentre il secondo 
 * indicherà l'ordine di creazione. Usando una variabile globale come 
 * puntatore alla radice dell'albero,
 *      scrivere: 
 *             Una funzione ricorsiva che inserisca un nodo nell'albero 
 *             Una funzione che crei (allochi) un nodo e lo inserisca 
 *                nell'albero, usando al bisogno la funzione ricorsiva di cui sopra. 
 *             Una funzione ricorsiva che 'traversi' l'albero, stampando il contenuto di ogni nodo, nell'ordine 
 *             Una funzione ricorsiva che liberi la memoria occupata dall'albero 
 *             Un programma principale che, utilizzando le funzioni scritte: 
 *                1.Crei un'albero di venti numeri casuali 
 *                2.Li stampi in maniera ordinata, secondo il valore casuale 
 *                3.Liberi la memoria occupata dall'alabero
 * Modificare il programma dell'albero binario assegnato in precedenza (soluzione precedente) in modo da: 
 *          1.creare una classe astratta 'albero' con un attributo statico per il puntoatore alla radice. Dovrà avere: 
 *                    Solo i puntatori 'destro' e sinistro', come attributi non staitic 
 *                    Un costruttore di default che inserisca il nuovo nodo creato nell'albero 
 *                    Un distruttore virtuale che distrugga l'intero albero 
 *                    Un metodo virtuale puro 'confronto' privato con un parametro di tipo void *, 
 *                       che confronta il nodo corrente con quello passato per parametro 
 *                    Un metodo virtuale puro 'stampa' privato che stampa il nodo corrente 
 *                    Un metodo pubblico 'traversa' che stampa, tramite il memtodo stampa l'intero albero 
 *                    Un metodo statico pubblico 'elimina' che esegua la 'delete' dell'intero albero 
 *          2.Una classe alberoIntero che derivi in modo privato da albero, la quale: 
 *                    Possegga due attributi interi chiave e valore 
 *                    Implementi i metodi virtuali pure di albero 
 *                    Renda visibili i metodi 'traversa' e 'elimina' 
 *       Tutti gli attributi devono essere privati 
 * Modificare l'esercizio dell'albero binario, presentato in precedenza trasformato ad oggerri in una precedente lezione. creare: 
 *          1.una classe template 'albero' analoga alla precedente albero 
 *          2.Una classe 'nodo' che contenga due numeri, la chiave casuale e il numero d'ordine (come inprecedenza) private 
 *          3.Una classe alberoChiave derivata da albero, che contenga come dato un puntatore a 'nodo' ed esegua il confronto
 *              (funzione virtuale) con la chiave. 
 *          4.Una classe AlberoDato derivata da albero, che contenga come dato un puntatore a 'nodo' ed esegua il confronto
 *              (funzione virtuale) con il dato. 
 *          5.La funzione principale costruirà 10 nodi e due alberi, uno derivato da alberoChiave e l'altro da alberoDato, quindi
 *              stamperà entrambi dli alberi 
 */

#include <stdio.h>	// Per le funzioni 'srand' e 'random'
#include <stdlib.h>	// Per la funzione 'time'
#include <time.h>

/**********************************************************************/
/* Classe TEMPLATE albero                                             */
/**********************************************************************/
template <class T> class albero {
    albero *sinistro;                   // Puntatore al sottoalbero sinistro
    albero *destro;                     // Puntatore al sottoalbero destro
    static albero *radice;              // Puntatore alla radice (statico)

    void inserisci (T *n);              // Inserisci ricorsiva, usata internamente
    void traversa ();                   // Traversa ricorsiva, usata internamente

    virtual int compare (T *n) = 0;     // Compare, virtuale pura
    virtual void print () = 0;          // Print, virtula pura
protected:
    void add () {  if (radice) radice -> inserisci ((T*)this); else radice = this; }
public:
    albero () { sinistro = destro = (albero*) 0; }; // Non inserisce il nodo ora perche' questo costruttore viene
                                                    // richiamato prima che la classe derivata abbia inizializzato
                                                    // il puntatore che serve per il confronto
    virtual ~albero ();
    // Funzioni statiche, inerfaccia all'albero nel suo insieme
    static void elimina () { if (radice) delete radice;};
    static void Traversa () { if (radice) radice -> traversa (); };
};

/**********************************************************************/
/* Classe nodo                                                        */
/**********************************************************************/
class nodo {
    friend class alberoChiave;  // Queste due classi accederanno per la 'compare'
    friend class alberoDato;
    int chiave;                 // Dati del nodo
    int valore;
    void print ();              // Stampa il dato
public:
    nodo (int Valore, int Chiave); // Il costruttore costruira' anche i due indici
};

/**********************************************************************/
/* Classe alberoChiave                                                */
/**********************************************************************/
class alberoChiave : private albero<alberoChiave> {
    nodo *dato;
    virtual int compare (alberoChiave *n);  // Implementazione delle virtuali pure di 'albero'
    virtual void print ();
public:
    alberoChiave (nodo *N) { dato = N; add (); };
    albero<alberoChiave>::elimina;     // Rendo di nuovo visibile 'elimina'
    albero<alberoChiave>::Traversa;    // Rendo di nuovo visibile 'Traversa'
};

/**********************************************************************/
/* Classe alberoDato                                                  */
/**********************************************************************/
class alberoDato : private albero<alberoDato> {
    nodo *dato;
    virtual int compare (alberoDato *n);  // Implementazione delle virtuali pure di 'albero'
    virtual void print ();
public:
    alberoDato (nodo *N) { dato = N; add (); };
    ~alberoDato () { delete dato; }; // La definisco solo qui, perche' se no cancellerei due volte il nodo
    albero<alberoDato>::elimina;     // Rendo di nuovo visibile 'elimina'
    albero<alberoDato>::Traversa;    // Rendo di nuovo visibile 'Traversa'
};

/**********************************************************************/
/* Classe TEMPLATE albero - Implementazione                           */
/**********************************************************************/

/* Aggiunge all'albero o sottoalbero puntato da 'a'
 * il nodo puntato da 'n' */
template <class T> void albero<T>::inserisci (T *n)
{
  if (compare (n))  // Chiama la 'compare' virtuale, per sapere da che parte mettere il nuovo nodo
  {	// Se il nodo va' aggiunto a sinistra
    if (sinistro != NULL) // Se esiste un sottoalbero di sinistra
      sinistro -> inserisci (n);	// Aggiungo a quel sottalbero
    else	// Se il sottalbero non esiste
      sinistro = (albero<T>*)n;	// diventa radice del sottoalbero
  }
  else
  {	// Se il nodo va' aggiunto a destra
    if (destro != NULL) // Se esiste un sottoalbero di destra
      destro -> inserisci (n);	// Aggiungo a quel sottalbero
    else	// Se il sottalbero non esiste
      destro = (albero<T>*)n;	// diventa radice del sottoalbero
  }
} 

/* Metodo che stampa in ordine un sottoalbero, puntato da 'a' */
template <class T> void albero<T>::traversa ()
{
  if (sinistro)		// Se esiste il sottoalbero sinistro
    sinistro -> traversa ();	// Lo stampa
  print ();             // Chiama la 'print' virtuale per stampare il nodo
  if (destro)		// Se esiste il sottoalbero destro
    destro -> traversa ();	// Lo stampa
}

/* Funzione che libera la memoria occupata da un sottoalbero */
template <class T> albero<T>::~albero<T> ()
{
  if (sinistro)		// Se esiste un sottoalbero sinistro
    delete sinistro;	// Libera prima la memoria di quel sottoalbero
  if (destro)		// Se esiste un sottoalbero destro
    delete destro;	// Libera prima la memoria di quel sottoalbero
}

/**********************************************************************/
/* Classe nodo - Implementazione                                      */
/**********************************************************************/

nodo::nodo (int Valore, int Chiave)
{ 
  chiave = Chiave;         // Inizializza i valori
  valore = Valore; 
  new alberoChiave (this); // Crea indice per chiave 
  new alberoDato (this);   // Crea indice per valore
}

void nodo::print ()
{
  // Stampa i valori del nodo (chiave in un campo di 12, '->' ed il valore in un campo di 2
  printf ("%14d -> %2d\n", chiave, valore);
}

/**********************************************************************/
/* Classe alberoChiave - Implementazione                              */
/**********************************************************************/

// La calsse reale albero<alberoChiave> ha una statica che va' definita!!!
albero<alberoChiave> *albero<alberoChiave>::radice = 0;

int alberoChiave::compare (alberoChiave *n)
{
  return (n -> dato -> chiave) < (dato -> chiave); // Ritorna il confronto fra le chiavi (il confronto)
}

void alberoChiave::print ()
{
  // Richiama la funzione di stampa del nodo
  dato -> print ();
}

/**********************************************************************/
/* Classe alberoDato - Implementazione                                */
/**********************************************************************/

// La calsse reale albero<alberoDato> ha una statica che va' definita!!!
albero<alberoDato> *albero<alberoDato>::radice = 0;

int alberoDato::compare (alberoDato *n)
{
  return (n -> dato -> valore) < (dato -> valore); // Ritorna il confronto fra i dati
}

void alberoDato::print ()
{
  // Richiama la funzione di stampa del nodo
  dato -> print ();
}


/* Funzione principale */
int main ()
{
  srandom (time (NULL));              /* Inizializza il generatore di numeri casuali
                                       * utilizzando il tempo corrente (molto variabile) */
  for (int i = 0; i < 10; i++)	      // Ciclo da 0 a 19
    new nodo (i, random ());          // crea nodo con valore l'indice e chiave casuale
  alberoChiave::Traversa ();          // Stampa l'albero tramite il metodo statico
  printf ("\n");
  alberoDato::Traversa ();            // Stampa l'albero tramite il metodo statico
  alberoChiave::elimina ();           // Libera la memoria dell'albero tramite il metodo statico,
  alberoDato::elimina ();             // Libera la memoria dell'albero tramite il metodo statico,
                                      // visto che non ho accesso alla radice
  return 0;
}