// Prof Simão - DAINF/UTFPR - Ctba - TecProg S71 - 1 Sem 2026 - 1a Parcial
// Hah couts e elses suplementares para fins de entendimento da execução.

#include <iostream>
#include <list>
#include <vector>
using namespace std;

#include <stdlib.h>
#include <time.h>
#include <Windows.h>

//classe base abstrata
class Ser
{
    private:
        int id;

    public:
        bool coletivo;

    public:
        Ser(int i = -1):id (i), coletivo (false) { }

        virtual ~Ser(){
                cout << "Destruindo o Ser de número: " << id << endl; // << endl;
        }

        virtual void viver() = 0;

        const int getId () const { return id;} // por boa prática a função foi deixada constante.

        static void sementear() {
            rand(); Sleep(100); time_t t;       // https://pt.wikipedia.org/wiki/Time_t
            srand((unsigned) time(&t)); rand(); // Para função srand e rand vide: https://www.tutorialspoint.com/c_standard_library/c_function_srand.htm
        }
};


class Floresta : public Ser
{

    public:

    // Classe para Arv(ore) derivada de Ser.
    class Arv : public Ser
    {
        private:
            float altura;
            static long int contador;

        public:
            Arv(const float alt = -1.0, const int i = -1): // Parâmetros constantes por boa prática
                Ser(i),
                altura( alt )
            {
                contador++;
                // abaixo - razoabilidade
                const float max = 100.0;
                if (altura < 1) altura = 1; // Razoabilidade... assumindo valores em metros
                else if (altura > max) altura =100; // idem et ibidem
            }
            ~Arv() { altura = -1; contador--;}

            static const long int getContador() { return contador; }

            const float getAltura() const { return altura; }

            void viver()
            {
                float const max = 100.0; // apenas para manter valores razoaveis, assumindo que sejam metros.
                if (altura < max)
                {
                    sementear();
                    if ( (rand()%100) < 25 )
                    {
                        altura++;
                    }
                }
                //cout << "O valor de altura da Arvore " << getId() << " eh: " << altura << endl << endl;
            }
    };

    // Classe Ani(mal) derivada de Ser.
    class Ani : public Ser
    {
        private:
            int vida;
            Arv* pArv;

        public:

            Ani(Arv* p = NULL, int i = -1) : Ser(i), vida(1), pArv(p) {
                sementear();
                vida = (rand()%50)+1;
            }

            ~Ani() {
                vida = -1;
                pArv = NULL;
            }

            void viver()
            {
                if (pArv)
                {
                    //cout << "Incrementando vida de: " << getId() << endl;
                    vida += (int)pArv->getAltura()/10.0 +
                            (int)Arv::getContador()/100;
                }

                if (vida > 50 ) vida = 50;
                else if (vida < 1) vida = 1;

                //cout << "O valor de vida do Animal " << getId() << " eh:" << vida << endl << endl;
            }
    };

    public:
    //private: // transformado em provado para fins de bons principios, mas poderia ficar sim publico dado o enunciado
        vector<Arv*> arvs;
        vector<Ani*> anis;

    private:

        void criarArvs();
        void criarAnis();

    public:

        Floresta(int i = -1) : Ser(i), arvs(), anis()
        {
            coletivo = true;
            arvs.clear();
            anis.clear();
            criarArvs();
            criarAnis();
        }

       virtual ~Floresta()
        {
            arvs.clear();
            anis.clear();
        }

    public:

        void viver()
        {
            int max = (int)arvs.size();
            for (int i = 0; i < max; i++){
                if (arvs[i]) { arvs[i]->viver(); }
            }

            max = (int)anis.size();
            for (int i = 0; i < max; i++){
                if (anis[i]) { anis[i]->viver(); }
            }
        }
};

long int Floresta::Arv::contador = 0;


class Bioma : public Ser
{
    public:
        static list<Ser*> entes;

    private:
        list<Ser*>::iterator it;

    public:

        Bioma(int i = -1): Ser (i), it(entes.begin())
        {
            coletivo = true;
            criarFlorestas();
        }

        ~Bioma()
        {
            Ser* p = NULL;
            while (!entes.empty())
            {
                //cout << "Tirando primeiro elemento de entes!" << endl;
                p = entes.front();
                entes.pop_front();
                if (!p) { cout << "Ponteiro Nulo!" << endl; }
                else delete p;
                p = NULL;
            }
        }

        void criarFlorestas()
        {
            Floresta* pF = NULL;
            int quant = 0;

            do {
                cout << "Quantas florestas: uma, duas ou três?" << endl;
                cin >> quant;
            }while ( (quant<1) || (quant>3));
            cout << endl;

            for (int i = 0; i < quant; i++){
                pF = new Floresta(i);
                if (pF) inserir(static_cast<Ser*>(pF));
            }
        }

        void viver()
        {
            for (it = entes.begin(); it != entes.end(); it++){
                if (*it) {
                    if ((*it)->coletivo){
                       (*it)->viver();
                    }
                }
            }
        }

        static void inserir(Ser* p) { if (p) entes.push_back(static_cast<Ser*>(p)); }
};
list<Ser*> Bioma::entes;

void Floresta::criarArvs()
{
    int max = 10;
    for (int i = 0; i < max; i++)
    {
        Arv* pAr = NULL;
        pAr = new Arv((float)i+10.0, i+3+Bioma::entes.size());
        if (pAr)
        {
            arvs.push_back(pAr);
            Bioma::inserir(static_cast<Ser*>(pAr));
        }
    }
}

void Floresta::criarAnis()
{
    int max = 10;
    Ani* pAn = NULL;
    for (int i = 0; i < max; i++)
    {
        Arv* pAr = arvs[i];
        pAn = new Ani(pAr, i+3+(max)+Bioma::entes.size());
        if (pAn)
        {
            anis.push_back(pAn);
            Bioma::inserir(static_cast<Ser*>(pAn));
        }
    }
}

int main()
{
    Bioma b;
    b.viver();

    cout << endl;
    cout << "FIM" << endl;

    return 0;
}

