// Prof Simão - DAINF/UTFPR - Ctba - TecProg S71 - 2 Sem 2025 - 1a Parcial
// Hah couts e elses suplementares para fins de entendimento da execução.

#include <iostream>
using namespace std;

#include <stdlib.h>
#include <time.h>
#include <Windows.h>

void semente()
{
    rand();
    Sleep(100);
    time_t t;                   // https://pt.wikipedia.org/wiki/Time_t
    srand((unsigned) time(&t)); // Para função srand e rand vide: https://www.tutorialspoint.com/c_standard_library/c_function_srand.htm
    rand();
}

class Academico
{
    protected:
        int id;
        static int cont_ind;

    private:
        virtual void executar() = 0;

    public:
        Academico():id (cont_ind++) { semente(); }
        virtual ~Academico()
        {
            cout << "Destruindo o acadêmico de número: " << id << endl; // << endl;
            id = -1;
        }

        virtual const int getNota () { return -1;}
        const int getId () const { return id;}

};
int Academico::cont_ind=0;

class Discente : public Academico
{
    private:
        int nota;

    private:
        void executar()
        {
            //semente();
            operator--();
            operator++();
        }

    public:
        void operator--() {
            semente();
            if ( nota > 0)
                if ( rand()%10 < 3 )
                    nota--;
        }

        void operator++() {
            semente();
            if ( nota < 100)
                if ( rand()%10 < 7 )
                    nota++;
        }

        public:

        Discente(): Academico(), nota((rand()%100)+1) { semente(); }
        ~Discente() { nota = -1; }

        const int getNota() {
            //system("Pause");
            executar();
            return nota; }
};

class Turma
{
    private:

        class Nodo
        {
            public:
                Academico* pAcad;
                Nodo* pProx;
                bool dinamico;

            public:
                Nodo(Academico* pA = NULL, const bool d = true) :
                    pAcad (pA),
                    pProx (NULL),
                    dinamico(d)
                { }
                ~Nodo() {
                    if (pAcad)
                    {
                        if (dinamico) delete pAcad ;
                        pAcad = NULL;
                    }
                    pProx = NULL;
                }
        };

    private:
        Nodo* pPrim;

    public:

        Turma(): pPrim(NULL) { }

        ~Turma()
        {
            cout << endl << endl
             << endl <<  "Destruindo a turma: " << this << endl << endl; system ("Pause");
            Nodo* pAux = NULL;
            while (pPrim)
            {
                pAux = pPrim;
                pPrim = pPrim->pProx;
                //pAux->pProx=NULL;
                delete pAux;
                pAux = NULL;
            }
            pPrim = NULL;
        }

        void incluir(Academico* p, const bool d = true)
        {
            if (p)
            {
                Nodo* pAux = new Nodo(p, d);

                if (!pAux) { cerr << "Nodo nao alocado!" << endl; return; }

                if (!pPrim)
                {
                    pPrim = pAux ;
                }
                else
                {
                    pAux->pProx = pPrim;
                    pPrim = pAux;
                }
            }
            else
            {
                cerr << "Ponteiro nulo - sem inclusão!" << endl;
            }
        }

        const float getMedia()const
        {
            Nodo* pAux = pPrim;
            int soma = 0;
            int cont = 0;
            //for (pAux = pPrim; pAux != NULL; pAux = pAux->pProx )
            while (pAux)
            {
                if (pAux->pAcad)
                {
                    int valor = -1;
                    valor = (pAux->pAcad)->getNota();

                    cout << "O academico " << pAux->pAcad->getId() << " tem nota: " << valor << endl;

                    if (valor >= 0)
                    {
                        soma+= valor;
                        cont++;
                    }
                }
                else
                {
                    cerr << "Ponteiro nulo no conjunto" << endl;
                }
                pAux = pAux->pProx;
            }
            cout << endl;

            if (cont>0) return (float)soma/cont;
            else return -1;
        }
};

class Docente : public Academico
{
    private:
        Turma* pTur;

    private:
        void executar()
        {
            if (NULL == pTur) { cerr << "pTur nulo!" << endl; return; }

            const float media = pTur->getMedia();
            cout << "A média academica desta turma é: " << media << endl << endl;
        }

    public:

        Docente(Turma* pT = NULL): Academico(), pTur(pT) { semente(); }
        ~Docente()
        {
            cout << endl <<
            "Destruindo o docente: " << getId() << "  - TURMA: " << pTur << endl << endl; system("pause");
            executar();
            pTur = NULL;
        }

};

class Universidade
{
    private:
        Turma T1;
        Turma T2;
        Docente *pProf1;
        Docente *pProf2;

    private:

        void criarDocentes()
        {
            cout << "Criação dos Docentes!" << endl;

            pProf1 = new Docente(&T1);
            if (!pProf1) cout << "Professor 1 nulo" << endl;

            pProf2 = new Docente(&T2);
            if (!pProf2) cout << "Professor 2 nulo" << endl;
        }

        void criarDiscentes()
        {
            cout << "Criação dos Discentes!" << endl;

            const int max = 20;
            const int metade = max/2;

            Discente* pDis = NULL;

            for (int i = 0; i < max; i++)
            {
                pDis = new Discente();

                if (pDis)
                {
                    if (i < metade)
                    {
                        T1.incluir(static_cast<Academico*>(pDis), true);
                    }
                    else
                    {
                        T2.incluir(static_cast<Academico*>(pDis), true);
                    }
                }
                else
                {
                    cerr << "Discente não alocado!" << endl;
                }
                pDis = NULL;
            }

            T1.incluir(static_cast<Academico*>(pProf2, false));
            // Aqui tem que ter o segundo parâmetro como false, dado que o Professor vai ser destruido antes da desalocação da lista....
        }

        void executar()
        {
            if (pProf1) { delete pProf1;  }
            pProf1 = NULL;
            //pProf2 = NULL;

            if (pProf2) { delete pProf2;  }
            pProf2 = NULL;
        }

        public:

        Universidade(): T1(), T2(), pProf1(NULL), pProf2(NULL)
        {
            semente();

            criarDocentes();
            system("pause");

            criarDiscentes();
            system("pause");
        }

        ~Universidade()
        {
            executar();
        }
};

int main()
{
    semente();

    Universidade U;

    //U.executar();

    cout << endl;
    cout << "FIM" << endl;

    return 0;
}

