/* animals.cpp */
#include <iostream>
#include <string>

using namespace std;


/*
 * The class Animal determines common characteristics and behaviour of
 * the animals.
 */
class Animal {
protected:
  // name - the animal's name.
  string name;

  // Display a message for the animal on the console.
  void answer(const string& str) const {
    cout << name << ": " << str << endl;
  }

public:
  // Change the animal's name.
  virtual void setName(const string& nm) {
    name = nm;
  }

  // Display a message that the receiver can't talk.
  virtual void talk() const {
    answer("I can't talk");
  }

  // Declare the class as abstract.
  virtual ~Animal() = 0;
};

// Show that virtual destructor is called.
inline Animal::~Animal() {
  cout << "~Animal()" << endl;
}




/*
 * The class Bird determines the common characteristics and behaviour of
 * birds.
 */
class Bird : public Animal {
public:
  // flying - the ability to fly.
  bool flying;

  // Declare the class as abstract.
  ~Bird() = 0;
};

// Show that (virtual) destructor is called.
inline Bird::~Bird() {
  cout << "~Bird()" << endl;
}




/*
 * The class Parrot determines the common characteristics and behaviour of
 * parrots.
 */
class Parrot : public Bird {
protected:
  // vocabulary - the parrot's vocabulary.
  string vocabulary;

public:
  // Display a message containing the parrot's vocabulary.
  void talk() const {
    answer(vocabulary);
  }

  // Change the receiver parrot's vocabulary to aString.
  void setVocabulary(const string& str) {
    vocabulary = str;
  }

  // Show that (virtual) destructor is called.
  ~Parrot() {
    cout << "~Parrot()" << endl;
  }
};




/*
 * The class Mammal determines the common characteristics and behaviour of
 * mammals.
 */
class Mammal : public Animal {
public:
  // Declare the class as abstract.
  ~Mammal() = 0;
};

// Show that (virtual) destructor is called.
inline Mammal::~Mammal() {
  cout << "~Mammal()" << endl;
}




/**
 * The class Dog determines the common characteristics and behaviour of
 * dogs.
 */
class Dog : public Mammal {
protected:
  // barksALot - if the dog is barking a lot.
  bool barksALot;

  // barkInitalized - initial state, if the dog is barking a lot.
  bool barkInitialized;

  // Let the dog bark by displaying a bark message.
  void bark() const {
    if (barksALot) {
      answer("Bow Wow, Bow Wow, Bow Wow");
    }
    else {
      answer("Woof");
    }
  }

public:
  // Instantiate a dog.
  Dog() : barkInitialized(false) { }

  // Change the status of the dog to noisy.
  void beNoisy() {
    // Visibility:
    // beNoisy() ist Teil des public Interfaces.
    barksALot = true;
    answer("I'll bark a lot");
    barkInitialized = true;
  }

  // Change the status of the receiver dog to quiet.
  void beQuiet() {
    barksALot = false;
    answer("I won't bark much");
    barkInitialized = true;
  }

  // Let the dog talk by barking unless barkInitialized is false,
  // in which case the superclass can decide how to talk.
  void talk() const {
    if (!barkInitialized) {
      Dog::talk();
    }
    else {
      bark();
    }
  }

  // Show that (virtual) destructor is called.
  ~Dog() {
    cout << "~Dog()" << endl;
  }

};




// Test the animal habitat.
void test() {
// Animal *animal = new Animal();   // Error: cannot allocate an object of type `Animal'
                                    // since the following virtual functions are abstract:
                                    // Animal::~Animal()
  Dog *snoopy = new Dog;
  snoopy->setName("Snoopy");
  snoopy->beQuiet();

  Dog *lassie = new Dog;
  lassie->setName("Lassie");
  lassie->beNoisy();

  Parrot *polly = new Parrot;
  polly->setName("Polly");
  polly->setVocabulary("Polly want a Cracker");


  lassie->talk();
  snoopy->talk();
  polly->talk();
  polly->talk();
  polly->talk();
  polly->setVocabulary("Screeech@#!? Don't bother me!");
  polly->talk();
  snoopy->beNoisy();
  snoopy->talk();
  lassie->beQuiet();
  lassie->talk();
  delete snoopy;
  snoopy = 0;
  delete lassie;
  lassie = 0;
  delete polly;
  polly = 0;


  // Casting tests and destructors:
  Animal *animal = new Parrot;         // Upcasting: provide pure virtual destructor/s
  Parrot *parrot;
  parrot = (Parrot *)animal;           // Downcasting
  parrot->setName("Parrot");
  parrot->setVocabulary("Get lost, man!");
  animal->talk();
  delete animal;
  animal = 0;
// delete parrot;                      // Don't delete parrot, object is already released!
  parrot = 0;

  // Casting tests and destructors:
  Bird *bird = new Parrot;             // Upcasting
  parrot = (Parrot *)bird;             // Downcasting
  parrot->setName("Parrot");
  parrot->setVocabulary("I'm a bird!");
  bird->talk();
  delete bird;
  bird = 0;
  parrot = 0;
}



// Entry point.
int main() {
  test();
  return 0;
}
