/**
 * Primer sa praktikuma, 28. 11. 2005.
 * 
 * @author Goran Rakic, <gox AT devbase DOT net>
 *
 */

#include <iostream>
#include <string>
using namespace std;

/**
 * Maksimalna duzina melodije u karakterima
 */
#define MEL_LEN 10

/**
 * Apstraktna klasa instrumenta
 */
class Instrument {

 private:
      /**
       * Melodija instrumenta
       * Incijalizuje se na prazan string u konstruktoru.
       * Kasnije postavljanje iz klasa naslednica.
       */
      char melodija[MEL_LEN];

      /**
       * Brojac instanci
       */
      static int brojac;

 public: 
  /**
   * Konstruktor
   * Inicijalizuje melodiju i povecava brojac instanci.
   */
  Instrument() {
      melodija[0] = '\0';
      ++brojac;
      cout << "Poziva se Instrument()" << endl;
  } 

  /**
   * Destruktor
   * Umanjuje brojac instanci.
   */
  virtual ~Instrument() {
      --brojac;
      cout << "Poziva se ~Instrument()" << endl;
  }

  /**
   * Vraca brojac instancix
   */
  int getBroj() const { return brojac; }

  /**
   * Apstraktne metode
   */
  virtual char* Ime() const =0;
  virtual char* Tip() const =0;

  virtual bool imaZice() const =0;
  virtual bool imaUdaraljke() const =0;

  virtual bool odMetala() const =0;
  virtual bool odDrveta() const =0;
  virtual bool odPlastike() const =0;

  /**
   * Stimovanje
   * Postavlja melodiju iz prvih MEL_LEN karaktera parametra.
   * @param char* mel String sa melodijom
   */
  void Nastimuj(char *mel) {
     strncpy(melodija, mel, MEL_LEN);
  }

  /**
   * Sviraj
   * Ako je melodija postavljena, vraca melodiju.
   * Inace vraca string "Nije nastimovan."
   * @todo Koristi izuzetke ili vrati prazan string uz assert().
   */
  const char *Sviraj() const {
     if(strlen(melodija)>0) return melodija;
     else return "Nije nastimovan.";
  }  

  /**
   * Ispisuje informacije o instrumentu
   * @param ostream& out Izlazni stream 
   */
  void Ispis(ostream& out) const {
     out << "Broj instrumenata je " << getBroj() << endl;
     out << "Instument" << Ime() << " je tipa " << Tip() << endl;
     out << "On " << (imaZice() ? "ima" : "nema" ) << " zice" << endl;
     out << "On " << (imaUdaraljke() ? "ima" : "nema" ) << " udaraljke" << endl;
     out << "On " << (odMetala() ? "je" : "nije" ) << " od metala" << endl;
     out << "On " << (odDrveta() ? "je" : "nije" ) << " od drveta" << endl;
     out << "On " << (odPlastike() ? "je" : "nije" ) << " od plastike" << endl;
     out << "Demonstracija sviranja " << Sviraj() << endl;
  }

};

int Instrument::brojac = 0;

/**
 * ZicaniInstrument nasledjuje Instrument
 */
class ZicaniInstrument : public Instrument {
  public:
     ZicaniInstrument() {
        cout << "Poziva se ZicaniInstrument()" << endl;
     }      

     ~ZicaniInstrument() {
        cout << "Poziva se ~ZicaniInstrument()" << endl;
     }  

     /**
      * Implementira apstraktne metode
      */
     char* Tip() const {return "Zicani instrument";}
     bool imaZice() const {return true;}
     bool imaUdaraljke() const {return false;}
};

/**
 * GudackiInstrument nasledjuje Instrument
 */
class GudackiInstrument : public Instrument {
  public:
     GudackiInstrument() {
        cout << "Poziva se GudackiInstrument()" << endl;  
     }
     
     ~GudackiInstrument() {
        cout << "Poziva se ~GudackiInstrument()" << endl;
     }
     
     /**
      * Implementira apstraktne metode
      */
     char* Tip() const {return "Gudacki instrument";}
     bool imaZice() const {return false;}
     bool imaUdaraljke() const {return false;}
};

/**
 * Udaraljke nasledjuje Instrument
 */
class Udaraljke : public Instrument {
  public:
     Udaraljke() {
        cout << "Poziva se Udaraljke()" << endl;
     }
      
     ~Udaraljke() {
        cout << "Poziva se ~Udaraljke()" << endl;
     }
         
     /**
      * Implementira apstraktne metode
      */
     char* Tip() const {return "Instrument sa udaraljkama";}
     bool imaZice() const {return false;}                                                                                                      
     bool imaUdaraljke() const {return true;}
};


/**
 * Violina nasledjuje ZicaniInstrument
 */
class Violina : public ZicaniInstrument {
  public:
     Violina() {
        cout << "Poziva se Violina()" << endl;
     }      

     ~Violina() {
        cout << "Poziva se ~Violina()" << endl;
     }

     /**
      * Implementira apstraktne metode
      */
     bool odMetala() const {return false;}
     bool odPlastike() const {return false;}
     bool odDrveta() const {return true;}
  
     char* Ime() const { return "Violina"; };
};

/**
 * Violina nasledjuje ZicaniInstrument
 */
class Viola : public ZicaniInstrument {
  public:
     Viola() {
        cout << "Poziva se Viola()" << endl;
     }
        
     ~Viola() {
        cout << "Poziva se ~Viola()" << endl;
     }  
      
     /**
      * Implementira apstraktne metode
      */
     bool odMetala() const {return false;}   
     bool odPlastike() const {return false;}
     bool odDrveta() const {return true;}
 
     char* Ime() const { return "Viola"; };
};

/**
 * Kontrabas nasledjuje ZicaniInstrument
 */
class Kontrabas : public ZicaniInstrument {
  public:
     Kontrabas() {
        cout << "Poziva se Kontrabas()" << endl;
     }
        
     ~Kontrabas() {
        cout << "Poziva se ~Kontrabas()" << endl;
     }  
      
     /**
      * Implementira apstraktne metode
      */
     bool odMetala() const {return false;}   
     bool odPlastike() const {return false;}
     bool odDrveta() const {return true;}
 
     char* Ime() const { return "Kontrabas"; };
};

/**
 * Gitara nasledjuje ZicaniInstrument                                                                                                        
 */
class Gitara : public ZicaniInstrument {                                                                                                     
  public:
     Gitara() {
        cout << "Poziva se Gitara()" << endl;
     }
        
     ~Gitara() {
        cout << "Poziva se ~Gitara()" << endl;
     }  
      
     /**
      * Implementira apstraktne metode
      */
     bool odMetala() const {return false;}    
     bool odPlastike() const {return false;}
     bool odDrveta() const {return true;}
   
     char* Ime() const { return "Gitara"; };
};

/**
 * Klavir nasledjuje ZicaniInstrument                                                                                                        
 */
class Klavir : public ZicaniInstrument {                                                                                                     
  public:
     Klavir() {
        cout << "Poziva se Klavir()" << endl;
     }
        
     ~Klavir() {
        cout << "Poziva se ~Klavir()" << endl;
     }  
      
     /**
      * Implementira apstraktne metode
      */
     bool odMetala() const {return true;}    
     bool odPlastike() const {return false;}
     bool odDrveta() const {return true;}
   
     char* Ime() const { return "Klavir"; };
};

/**
 * Bubanj nasledjuje Udaraljke                                                                                                        
 */
class Bubanj : public Udaraljke {                                                                                                     
  public:
     Bubanj() {
        cout << "Poziva se Bubanj()" << endl;
     }
        
     ~Bubanj() {
        cout << "Poziva se ~Bubanj()" << endl;
     }  
      
     /**
      * Implementira apstraktne metode
      */
     bool odMetala() const {return false;}    
     bool odPlastike() const {return false;}
     bool odDrveta() const {return true;}
   
     char* Ime() const { return "Bubanj"; };
};

/**
 * Truba nasledjuje GudackiInstrument
 */
class Truba : public GudackiInstrument {
  public:
     Truba() {
        cout << "Poziva se Truba()" << endl;
     }
      
     ~Truba() {
        cout << "Poziva se ~Truba()" << endl;
     }
        
     /**
      * Implementira apstraktne metode
      */
     bool odMetala() const {return true;}
     bool odPlastike() const {return false;}
     bool odDrveta() const {return false;}
   
     char* Ime() const { return "Truba"; };
};

ostream& operator << (ostream& out, const Instrument& a) {
     a.Ispis(out);
     return out;
}

int main(int argc, char* argv[]) {

   int i;
   Instrument* orkestar[7];
   orkestar[0] = new Violina;
   orkestar[1] = new Gitara;
   orkestar[2] = new Bubanj;
   orkestar[3] = new Klavir;
   orkestar[4] = new Truba;
   orkestar[5] = new Viola;
   orkestar[6] = new Kontrabas;

/*
   Violina a;
   cout << a.Ime() << " " << a.Tip() << endl;
   cout << a.Sviraj() << endl;
   a.Nastimuj("ViiLiii");
   cout << a.Sviraj() << endl;
   a.Ispis(cout);
   cout << a;
*/
   
  orkestar[0]->Nastimuj("ViiLiii");
  orkestar[1]->Nastimuj("TangTang");
  orkestar[2]->Nastimuj("BangDum");
  orkestar[3]->Nastimuj("Lalala");
  orkestar[4]->Nastimuj("Duuuu");
  orkestar[5]->Nastimuj("LiiFii");
  orkestar[6]->Nastimuj("DumTrum");

  for(i=0;i<7;i++) {
     cout << orkestar[i]->Ime() << " " << orkestar[i]->Tip() << endl;
     cout << orkestar[i]->Sviraj() << endl;
     cout << *orkestar[i];
     cout << endl << endl;
  }

  for(i=0;i<7;i++) delete orkestar[i];

}

