/* Autor: Leandro Bernsmuller
Implementa um agente reativo.

Estimulo -> Acao ( -> Estimulo -> Acao -> ... )

Os estimulos podem vir de outros agentes, de si proprio ou do ambiente.
Cada estimulo ativa uma acao.
A acao ativada vai depender do estimulo e do agente.
A acao que vai ser ativada por um estimulo pode ser diferente de agente para
agente. Diferentes agentes podem possuir uma mesma acao para
varios estimos.

O agente trata os estimulos como eventos.

Existe: conjunto de agentes, conjunto de eventos, conjunto de acoes */



class CCenario;              // interface com o DX
class CCenarioFisico;        // interface com o DX
#include <windows.h>
#include "..\princip\defs.h"
#include "dados\dados.h"
#include "acoes\acao.h"
#include "acoes\nasce.h"
#include "acoes\morre.h"
#include "acoes\mudadir.h"
#include "acoes\movelivr.h"
#include "acoes\mata.h"
#include "acoes\metabol.h"
#include "acoes\absalim.h"
#include "acoes\reproduz.h"
#include "acoes\aproxima.h"
#include "acoes\fornalim.h"
#include "acoes\pre-repr.h"
#include "acoes\interv\interv.h"
#include "acoes\interv\dragalei.h"
#include "acoes\interv\agrotox.h"
#include "acoes\interv\rede.h"
#include "acoes\interv\jetski.h"
#include "acoes\interv\pescap.h"
#include "acoes\interv\esgpluv.h"
#include "acoes\interv\esgindus.h"
#include "acoes\interv\gasolina.h"
#include "acoes\interv\lixo.h"
#include "agente.h"



/* construtor - inicia agente sem comportamentos e tipo
**********************************************************/
CAgente::CAgente ()
{
   m_paoOsAgentes = NULL;
   m_poDados = NULL;
   m_poCenario = NULL;
   m_bSubstAcao = FALSE;
   m_dwSubstNdxEvento = NDX_EVENTO_NULO;
   m_dwSubstIDNovaAcao = ID_ACAO_NULA;
   m_dwSubstIDSubsAcao = ID_ACAO_NULA;
   m_bReconfigurarAgente = FALSE;
   
   // inicia reconfiguracao nula
   for ( int ncC = 0; ncC < TAM_CONFIG; ncC ++ )
      m_adwConfiguracao [ ncC ] = 0;

   for ( int ncEv = 0; ncEv < NUM_EVENTOS; ncEv ++ )
      m_apoAcoes [ ncEv ] = NULL;
}



/* destrutor - desaloca agente
**********************************************************/
CAgente::~CAgente ()
{
   mDestroi ();
}



/* Cria o agente.
padwConfiguracao - indica qual a acao relacionada a cada evento e outros dados do agente.
paoAgentes - aponta demais agentes
poCen - aponta o cenario
return TRUE - certo / FALSE - erro
**********************************************************/
BOOL CAgente::mCria ( DWORD* padwConfiguracao, CAgente* paoOsAgentes, CCenarioFisico* poCenario )
{
   mDestroi ();

   m_paoOsAgentes = paoOsAgentes;
   m_poCenario = poCenario;

   // cria as acoes para o agente
   for ( int ncC = 0; ncC < NUM_EVENTOS * 2; ncC += 2 ) { // para cada evento

      mSubstituiAcao ( ncC / 2, padwConfiguracao [ ncC ], padwConfiguracao [ ncC + 1 ] );

      // verifica se nao criou
      if ( m_apoAcoes [ ncC / 2 ] == NULL )
         return FALSE;
   }

   return TRUE;
}



/* Destroi - desaloca dados do agente
**********************************************************/
void CAgente::mDestroi ()
{
   // desaloca acoes
   for ( int ncEv = 0; ncEv < NUM_EVENTOS; ncEv ++ )
      if ( m_apoAcoes [ ncEv ] != NULL ) {
         delete m_apoAcoes [ ncEv ];
         m_apoAcoes [ ncEv ] = NULL;
      }

   m_paoOsAgentes = NULL;
   m_poCenario = NULL;
}



/* Prepara para substituir uma acao por outra
**********************************************************/
void CAgente::mPreparaSubstituirPos ( DWORD dwNdxEvento, DWORD dwIDNovaAcao, DWORD dwIDSubsAcao )
{
   m_bSubstAcao = TRUE;  // indica que deve haver troca de acoes
                         // quando a acao que estah em execucao
                         // acabar.
                         
   // qual acao e pelo oque substituir   
   m_dwSubstNdxEvento = dwNdxEvento;
   m_dwSubstIDNovaAcao = dwIDNovaAcao;
   m_dwSubstIDSubsAcao = dwIDSubsAcao;
}



/* Prepara para reconfigurar o agente (alterar todas acoes)
**********************************************************/
void CAgente::mPreparaReconfigurarPos ( DWORD *padwConfiguracao )
{
   m_bReconfigurarAgente = TRUE;  // indica reconfigurar
                                  // quando a acao que estah em execucao
                                  // acabar.

   // copia nova configuracao
   for ( int ncC = 0; ncC < TAM_CONFIG; ncC ++ )
      m_adwConfiguracao [ ncC ] = padwConfiguracao [ ncC ];
}



/* Substitui a acao de um evento por outra. Se nao existe
acao anterior definida somente inclui a nova.
nNdxEvento - evento que receberah a acao
dwIDNovaAcao - a nova acao
dwIDSubsAcao - define, para a nova acao, qual a acao que a
substituira caso a acao resolva se remover.
**********************************************************/
void CAgente::mSubstituiAcao ( DWORD nNdxEvento, DWORD dwIDNovaAcao, DWORD dwIDSubsAcao )
{
   // verifica se deve desalocar acao
   if ( m_apoAcoes [ nNdxEvento ] != NULL ) {
      delete m_apoAcoes [ nNdxEvento ];
      m_apoAcoes [ nNdxEvento ] = NULL;
   }

   // aloca
   switch ( dwIDNovaAcao ) {

      case ID_ACAO_NULA:
         m_apoAcoes [ nNdxEvento ] = new CAcao;
         break;

      case ID_ACAO_NASCE:
         m_apoAcoes [ nNdxEvento ] = (CAcao*) new CNasce;
         break;

      case ID_ACAO_MORRE:
         m_apoAcoes [ nNdxEvento ] = (CAcao*) new CMorre;
         break;

      case ID_ACAO_MUDA_DIRECAO:
         m_apoAcoes [ nNdxEvento ] = (CAcao*) new CMudaDirecao;
         break;

      case ID_ACAO_MOVE_LIVRE:
         m_apoAcoes [ nNdxEvento ] = (CAcao*) new CMoveLivre;
         break;

      case ID_ACAO_MATA:
         m_apoAcoes [ nNdxEvento ] = (CAcao*) new CMata;
         break;

      case ID_ACAO_METABOLISMO:
         m_apoAcoes [ nNdxEvento ] = (CAcao*) new CMetabolismo;
         break;

      case ID_ACAO_ABSORVE_ALIMENTO:
         m_apoAcoes [ nNdxEvento ] = (CAcao*) new CAbsorveAlimento;
         break;         

      case ID_ACAO_REPRODUZ:
         m_apoAcoes [ nNdxEvento ] = (CAcao*) new CReproduz;
         break;         

      case ID_ACAO_APROXIMA:
         m_apoAcoes [ nNdxEvento ] = (CAcao*) new CAproxima;
         break;         

      case ID_ACAO_INTERVIR:
         m_apoAcoes [ nNdxEvento ] = (CAcao*) new CIntervir;
         break;                  

      case ID_ACAO_DRAGA_LEITO:
         m_apoAcoes [ nNdxEvento ] = (CAcao*) new CDragaLeito;
         break;                  

      case ID_ACAO_FORNECE_ALIMENTO:
         m_apoAcoes [ nNdxEvento ] = (CAcao*) new CForneceAlimento;
         break;

      case ID_ACAO_AGROTOXICO:
         m_apoAcoes [ nNdxEvento ] = (CAcao*) new CAgrotoxico;
         break;

      case ID_ACAO_REDE:
         m_apoAcoes [ nNdxEvento ] = (CAcao*) new CRede;
         break;

      case ID_ACAO_LIXO:
         m_apoAcoes [ nNdxEvento ] = (CAcao*) new CLixo;
         break;

      case ID_ACAO_JETSKI:
         m_apoAcoes [ nNdxEvento ] = (CAcao*) new CJetski;
         break;

      case ID_ACAO_PESCA_PREDATORIA:
         m_apoAcoes [ nNdxEvento ] = (CAcao*) new CPescaPredatoria;
         break;

      case ID_ACAO_ESGOTO_PLUVIAL:
         m_apoAcoes [ nNdxEvento ] = (CAcao*) new CEsgotoPluvial;
         break;

      case ID_ACAO_ESGOTO_INDUSTRIAL:
         m_apoAcoes [ nNdxEvento ] = (CAcao*) new CEsgotoIndustrial;
         break;

      case ID_ACAO_GASOLINA:
         m_apoAcoes [ nNdxEvento ] = (CAcao*) new CGasolina;
         break;

      case ID_ACAO_PREPARA_REPRODUZ:
         m_apoAcoes [ nNdxEvento ] = (CAcao*) new CPreparaReproduz;
         break;
   }

   // se alocou acao
   if ( m_apoAcoes [ nNdxEvento ] != NULL ) {
      // prepara acao para ser ativada
      m_apoAcoes [ nNdxEvento ]->m_poMeuAgente = this;
      m_apoAcoes [ nNdxEvento ]->m_dwNdxMeuEvento = (DWORD) nNdxEvento;
      m_apoAcoes [ nNdxEvento ]->m_dwTipoSubs = dwIDSubsAcao;
   }
}



/* Ativa uma certa acao correspondente ao evento recebido.
Apohs executar a acao, faz substituicao de acoes ou reconfiguracao,
se for o caso.
dwEvento - evento a ser tratado
pDado1 e pDado2 - dados associados ao evento (podem ser ponteiros e/ou
valores numericos)
return TRUE - certo / FALSE - erro
**********************************************************/
BOOL CAgente::mTrataEvento ( DWORD dwEvento, PTRVAL pDado1, PTRVAL pDado2 )
{
   // executa acao associada ao evento

   // verifica se tem acao
   if ( m_apoAcoes [ dwEvento ] == NULL )
      return FALSE;
   
   BOOL
      bRet = FALSE;
   switch ( m_apoAcoes [ dwEvento ]->m_dwTipoAcao ) {

      case ID_ACAO_NULA:
         bRet = ( (CAcao*)  m_apoAcoes [ dwEvento ] )->mAtiva ( pDado1, pDado2 );
         break;

      case ID_ACAO_NASCE:
         bRet = ( (CNasce*) m_apoAcoes [ dwEvento ] )->mAtiva ( pDado1, pDado2 );
         break;

      case ID_ACAO_MORRE:
         bRet = ( (CMorre*) m_apoAcoes [ dwEvento ] )->mAtiva ( pDado1, pDado2 );
         break;

      case ID_ACAO_MUDA_DIRECAO:
         bRet = ( (CMudaDirecao*) m_apoAcoes [ dwEvento ] )->mAtiva ( pDado1, pDado2 );
         break;

      case ID_ACAO_MOVE_LIVRE:
         bRet = ( (CMoveLivre*) m_apoAcoes [ dwEvento ] )->mAtiva ( pDado1, pDado2 );
         break;

      case ID_ACAO_MATA:
         bRet = ( (CMata*) m_apoAcoes [ dwEvento ] )->mAtiva ( pDado1, pDado2 );
         break;

      case ID_ACAO_METABOLISMO:
         bRet = ( (CMetabolismo*) m_apoAcoes [ dwEvento ] )->mAtiva ( pDado1, pDado2 );
         break;

      case ID_ACAO_ABSORVE_ALIMENTO:
         bRet = ( (CAbsorveAlimento*) m_apoAcoes [ dwEvento ] )->mAtiva ( pDado1, pDado2 );
         break;

      case ID_ACAO_REPRODUZ:
         bRet = ( (CReproduz*) m_apoAcoes [ dwEvento ] )->mAtiva ( pDado1, pDado2 );
         break;         

      case ID_ACAO_APROXIMA:
         bRet = ( (CAproxima*) m_apoAcoes [ dwEvento ] )->mAtiva ( pDado1, pDado2 );
         break;         

      case ID_ACAO_FORNECE_ALIMENTO:
         bRet = ( (CForneceAlimento*) m_apoAcoes [ dwEvento ] )->mAtiva ( pDado1, pDado2 );
         break;

      case ID_ACAO_INTERVIR:
         bRet = ( (CIntervir*) m_apoAcoes [ dwEvento ] )->mAtiva ( pDado1, pDado2 );
         break;                  

      case ID_ACAO_DRAGA_LEITO:
         bRet = ( (CDragaLeito*) m_apoAcoes [ dwEvento ] )->mAtiva ( pDado1, pDado2 );
         break;                  

      case ID_ACAO_AGROTOXICO:
         bRet = ( (CAgrotoxico*) m_apoAcoes [ dwEvento ] )->mAtiva ( pDado1, pDado2 );
         break;

      case ID_ACAO_REDE:
         bRet = ( (CRede*) m_apoAcoes [ dwEvento ] )->mAtiva ( pDado1, pDado2 );
         break;

      case ID_ACAO_LIXO:
         bRet = ( (CLixo*) m_apoAcoes [ dwEvento ] )->mAtiva ( pDado1, pDado2 );
         break;

      case ID_ACAO_JETSKI:
         bRet = ( (CJetski*) m_apoAcoes [ dwEvento ] )->mAtiva ( pDado1, pDado2 );
         break;

      case ID_ACAO_PESCA_PREDATORIA:
         bRet = ( (CPescaPredatoria*) m_apoAcoes [ dwEvento ] )->mAtiva ( pDado1, pDado2 );
         break;

      case ID_ACAO_ESGOTO_PLUVIAL:
         bRet = ( (CEsgotoPluvial*) m_apoAcoes [ dwEvento ] )->mAtiva ( pDado1, pDado2 );
         break;

      case ID_ACAO_ESGOTO_INDUSTRIAL:
         bRet = ( (CEsgotoIndustrial*) m_apoAcoes [ dwEvento ] )->mAtiva ( pDado1, pDado2 );
         break;

      case ID_ACAO_GASOLINA:
         bRet = ( (CGasolina*) m_apoAcoes [ dwEvento ] )->mAtiva ( pDado1, pDado2 );
         break;

      case ID_ACAO_PREPARA_REPRODUZ:
         bRet = ( (CPreparaReproduz*) m_apoAcoes [ dwEvento ] )->mAtiva ( pDado1, pDado2 );
         break;
   }

   // verifica se deve ser feita reconfiguracao de todas acoes
   if ( m_bReconfigurarAgente ) {
      mCria ( m_adwConfiguracao, m_paoOsAgentes, m_poCenario );
      m_bReconfigurarAgente = FALSE;
   }

   // verifica se acao solicitou substituicao
   if ( m_bSubstAcao ) {
      mSubstituiAcao ( m_dwSubstNdxEvento,
                       m_dwSubstIDNovaAcao,
                       m_dwSubstIDSubsAcao );
      // dados foram usado, substituicao terminada
      m_bSubstAcao = FALSE;
   }

   return bRet;
}
