// Prof Simão - DAINF/UTFPR - Ctba - TecProg S71 - 2 Sem 2026 - 1a Parcial
// Hah couts suplementares para fins de entedimento da execução.

#include <vector>
#include <list>
#include <iostream>
using namespace std;

#include <stdlib.h>
#include <time.h>

namespace Espionagem {
class Agente {
    private:
        static int cont_id;
        int id;
        int idade;

    public:

        Agente(const int idd = 20):
            id (cont_id++),
            idade(idd)
        {
            if (idade < 20) { idade = 20; }
            else { if (idade > 60) { idade = 60; } }
        }

        virtual ~Agente() { //id = -1; //idade = -1;
        }

        virtual void agir() = 0;

        const int getIdade() const { return idade; }

        const int getId() const { return id; }

        void executar() {
            cout << endl << "Executando agente: " << getId() << " --- " << this << endl << endl;
            agir(); operator++();
        }

    protected:
        void operator++() { idade++; }
};
int Agente::cont_id(0);

class Espiao;

class Secreto : public Agente
{
    private:
        vector<Espiao*>             espias;
        vector<Espiao*>::iterator   it;
        int                         forca;

    public:
        Secreto(const int idd = 20) :
            Agente(idd),
            espias(),
            it(),
            forca(100-getIdade())
        {
            espias.clear();
            it = espias.begin();
        }

        ~Secreto() { espias.clear(); forca = -1; }

        void agir(); // a ser implementado depois da criação da classe Secreto dado a dependência.

        void incluirEspiao(Espiao* p)
        {
            if (p) { espias.push_back(p); }
            else { cout << "Nao se pode incluir nulo em espias!" << endl; }
        }

        void operator--() { forca--; }

        void setForca(const int f) { forca = f; }
        const int getForca() const { return forca; }
};

class Espiao : public Agente
{
    protected:
        vector<Secreto*> secres;
        bool ruim;

    public:
        Espiao(const int idd = 20):
            Agente(idd),
            secres(),
            ruim ( (rand()%10)<7 )
        {
            secres.clear();
            time_t t; srand((unsigned) time(&t));
        }

        virtual ~Espiao() { secres.clear(); }

        virtual void agir()
        {
            time_t t; srand((unsigned) time(&t));

            if ( !(rand()%10) ) { ruim = !ruim; }

            vector<Secreto*>::iterator iterador;

            iterador = secres.begin();
            while ( iterador != secres.end() )
            {
                if ( *iterador )
                {
                    if ( (*iterador)->getForca() > 0 )
                    {
                        (*iterador)->operator--();
                        cout << "Diminuindo força de: " << *iterador << endl;
                        iterador++;
                    }
                    else
                    {
                        cout << "Espiao excluindo de secres: " << *iterador << endl;
                        iterador = secres.erase(iterador);
                        break;
                    }
                }
                else
                {
                    cout << "Ponteiro nulo em secres!" << endl;
                    iterador = secres.erase(iterador);
                }
            }
        }

        void incluirSecreto(Secreto* p)
        {
            if (p) { secres.push_back(p); }
            else { cout << "Nao se pode incluir nulo em secres!" << endl; }
        }

        const bool getRuim() const { return ruim; }
};

void Secreto::agir()
{
    if (forca <=0 ) return;

    if (espias.empty()) return;

    int cont = 0;

    it = espias.begin();

    while (it != espias.end())
    {
        if (cont > forca) break;

        if ( (*it == NULL) || (*it)->getRuim() )
        {
            if (*it)
            {
                cont++; cout << "Secreto excluindo de espias o Espiao de ID: " << (*it)->getId() << endl;
            }
            it = espias.erase(it);
        }
        else { it++; }
    }
}

class Duplo : public Espiao
{
    private:
        const int coeficiente;

    public:
        Duplo(const int idd = 20): Espiao(idd), coeficiente(10) { }
        ~Duplo() { }

        void agir() {
            if (secres.empty()) { cout << "Secres está vazio!" << endl; }
            else { //Secreto* primeiro = NULL; primeiro = secres.front(); // ...
                 if (secres[0]) { secres[0]->setForca( secres[0]->getForca() + coeficiente ); }
            }
        }
};

class Inteligencia
{
    private:
        list<Agente*> colecao;

    public:

        Inteligencia(): colecao() { colecao.clear(); criarAgentes(); }

        ~Inteligencia()
        {
            list<Agente*>::iterator it;
            it = colecao.begin();

            while (it != colecao.end())
            {
                cout << endl << "Deletando agente: " << (*it)->getId() << " --- " << *it << endl;
                if (*it) { delete *it; *it = NULL; }
                it++;
            }
            colecao.clear();
        }

        void criarAgentes()
        {
            Secreto*    pS = NULL;
            Espiao*     pE = NULL;
            Duplo*      pD = NULL;

            vector<Secreto*>    vetSecretos;
            vector<Espiao*>     vetEspioes;

            const int max2 = 2;
            const int max1 = 1;


            for (int i = 0; i < max2; i++)
            {
                pS = new Secreto(i+20);

                if (pS)
                {
                    vetSecretos.push_back(pS);
                    colecao.push_back(static_cast<Agente*>(pS));
                    //cout << "Agente secreto inserido!" << endl;
                }
                else
                {
                    cout << "Agente secreto não alocado!" << endl;
                }
            }

            // ----------------------------------

            // loop apenas para permitir eventualmente testar com mais...
            for (int i = 0; i < max1; i++)
            {
                pE = new Espiao(i+31);
                if (pE)
                {
                    vetEspioes.push_back(pE);
                    colecao.push_back(static_cast<Agente*>(pE));
                }
                else
                {
                    cout << "Agente espião não alocado!" << endl;
                }

                // ----------------------------------
            }

            // loop apenas para permitir eventualmente testar com mais...
            for (int i = 0; i < max1; i++)
            {
                pD = new Duplo(i+41);
                if (pD)
                {
                    vetEspioes.push_back(static_cast<Espiao*>(pD));
                    colecao.push_back(static_cast<Agente*>(pD));
                }
                else
                {
                    cout << "Agente duplo não alocado!" << endl;
                }
            }

            for (int i = 0; i < max2; i++)
            {
                for (int j = 0; j < max2; j++)
                {
                    vetSecretos[i]->incluirEspiao(vetEspioes[j]);
                    vetEspioes[j]->incluirSecreto(vetSecretos[i]);
                }
            }
        }

        void executar()
        {
            cout << "Método executar de Inteligência!" << endl << endl;
            list<Agente*>::iterator it;

            it = colecao.begin();
            while (it != colecao.end())
            {
                cout << endl << "Executando agente: " << (*it)->getId() << " --- " << *it << endl << endl;
                if (*it) { (*it)->executar(); }
                it++;
            }
        }
};
} using namespace Espionagem;

int main ()
{
    time_t t;   // https://pt.wikipedia.org/wiki/Time_t
                // Para função srand e rand vide: https://www.tutorialspoint.com/c_standard_library/c_function_srand.htm
    srand((unsigned) time(&t));

    Inteligencia x;

    x.executar();

    return 0;
}

