#include <iostream>
#include <string>

using namespace std;

class Animal {
protected:
    string name;
    int age;

public:
    Animal(string n, int a) : name(n), age(a) {}

    virtual void makeSound() = 0;

    virtual void eat() {
        cout << "The animal is eating." << endl;
    }

    virtual void sleep() {
        cout << name << " is sleeping." << endl;
    }

    virtual ~Animal() {}
};

class Mammal : public Animal {
protected:
    bool isSleeping;

public:
    Mammal(string n, int a)
        : Animal(n, a), isSleeping(false) {}

    bool canAct() {
        return !isSleeping;
    }

    void wakeUp() {
        isSleeping = false;
        cout << name << " woke up." << endl;
    }

    void makeSound() override {
        if (!canAct()) return;

        cout << "Generic mammal sound." << endl;
    }

    void eat() override {
        if (!canAct()) return;

        cout << "The mammal is eating." << endl;
    }

    void sleep() override {
        cout << name << " is sleeping." << endl;
        isSleeping = true;
    }
};

class Dog : public Mammal {
private:
    bool isTailWagging;

public:
    Dog(string n, int a)
        : Mammal(n, a), isTailWagging(false) {}

    void makeSound() override {
        if (!canAct()) return;

        cout << "Woof!" << endl;
    }

    void eat() override {
        if (!canAct()) return;

        cout << "The dog is eating." << endl;
    }

    void sleep() override {
        Mammal::sleep();
    }

    void fetch() {
        if (!canAct()) return;

        cout << "The dog is fetching." << endl;
    }

    void wagTail() {
        if (!canAct()) {
            cout << "The dog can't wag its tail because it's sleeping."
                 << endl;
            return;
        }

        isTailWagging = true;

        cout << "The dog is wagging its tail." << endl;
    }
};

class Cat : public Mammal {
private:
    int numberOfLives;

public:
    Cat(string n, int a)
        : Mammal(n, a), numberOfLives(9) {}

    void makeSound() override {
        if (!canAct()) return;

        cout << "Meow!" << endl;
    }

    void eat() override {
        if (!canAct()) return;

        cout << "The cat is eating." << endl;
    }

    void sleep() override {
        Mammal::sleep();
    }
};

class Tail : public Dog {
public:
    Tail(string n, int a)
        : Dog(n, a) {}
};

int main() {

    Dog d("Fido", 3);
    Cat c("Fluffy", 5);
    Dog d1("Barky", 3);

    Mammal* arr[] = { &d, &c, &d1 };

    cout << "Sounds:" << endl;
    for (int i = 0; i < 3; i++)
        arr[i]->makeSound();

    cout << "\nEating:" << endl;
    for (int i = 0; i < 3; i++)
        arr[i]->eat();

    cout << "\nSleeping:" << endl;
    for (int i = 0; i < 3; i++)
        arr[i]->sleep();

    cout << "\nTrying to make sounds while sleeping:" << endl;
    for (int i = 0; i < 3; i++)
        arr[i]->makeSound();

    cout << "\nTrying to eat while sleeping:" << endl;
    for (int i = 0; i < 3; i++)
        arr[i]->eat();

    cout << "\nTrying to wag tails:" << endl;
    for (int i = 0; i < 3; i++) {
        Dog* dog = dynamic_cast<Dog*>(arr[i]);

        if (dog)
            dog->wagTail();
    }

    cout << "\nWake up one dog:" << endl;
    d.wakeUp();
    d.wagTail();

    cout << "\nTail object:" << endl;
    Tail t("Taily", 2);
    t.makeSound();

    return 0;
}