/*******************************************************************
/*  RegUnit.cpp
/*  Author: Vadim Berman
/*
/*  Description:
/*  implementation of PersonalLog and EventsRegistry classes, making it
/*  possible for the Agents to remember events and to learn from their
/*  past mistakes
/*
/*  The contents of this file are subject to the Brainiac Public License.
/*  Version 1.0 (the "License"); you may not use this file except in
/*  compliance with the License. You may obtain a copy of the License at
/*  http://www.twilightminds.com
/*
/*  Software distributed under the License is distributed on an "AS IS"
/*  basis WITHOUT WARRANTY OF ANY KIND, either express or implied. See
/*  the License for the specific language governing rights and limitations
/*  under the License.
/*
/*  Copyright (C) 1999 Twilight Minds. All rights reserved.
/********************************************************************/
//---------------------------------------------------------------------------
#include "RegUnit.h"
//---------------------------------------------------------------------------
unsigned short gMaxLogLen = 10;
//***************************************************************************
//AddWitnessToEvent
//********** Description: **************
//attach given witness ID to the given event
//********** Parameters:  **************
//EventEntry *eepEv - given event
//ID ToAdd - witness ID
//---------------------------------------------------------------------------
void BBECALL AddWitnessToEvent(EventEntry *eepEv,ID ToAdd)
{
    ID *idpSwapBuf;
    if(eepEv == NULL)
        return;
    if(eepEv->Witnesses == NULL || eepEv->WitnessesQty == 0)
        return;
    idpSwapBuf = new ID[eepEv->WitnessesQty];
    if(idpSwapBuf == NULL)
        return;
    memcpy(idpSwapBuf,eepEv->Witnesses,eepEv->WitnessesQty);
    eepEv->WitnessesQty++;
    delete []eepEv->Witnesses;
    eepEv->Witnesses = new ID[eepEv->WitnessesQty];
    if(eepEv->Witnesses == NULL)
    {
        eepEv->WitnessesQty = 0;
        delete []idpSwapBuf;
        return;
    }
    memcpy(eepEv->Witnesses,idpSwapBuf,eepEv->WitnessesQty);
    eepEv->Witnesses[eepEv->WitnessesQty - 1] = ToAdd;
    delete []idpSwapBuf;
}

//***************************************************************************
//RemoveWitnessFromEvent
//********** Description: **************
//remove given witness ID from the given event
//********** Parameters:  **************
//EventEntry *eepEv - given event
//ID ToRemove - witness ID
//---------------------------------------------------------------------------
void BBECALL RemoveWitnessFromEvent(EventEntry *eepEv,ID ToRemove)
{
    ID *idpSwapBuf;
    unsigned short i,j = 0;
    if(eepEv == NULL)
        return;
    if(eepEv->Witnesses == NULL || eepEv->WitnessesQty == 0)
        return;
    idpSwapBuf = new ID[eepEv->WitnessesQty];
    if(idpSwapBuf == NULL)
        return;
    memcpy(idpSwapBuf,eepEv->Witnesses,eepEv->WitnessesQty);
    delete []eepEv->Witnesses;

    eepEv->Witnesses = new ID[--(eepEv->WitnessesQty)];
    if(eepEv->Witnesses == NULL)
    {
        eepEv->WitnessesQty = 0;
        delete []idpSwapBuf;
        return;
    }
    for(i = 0; i < eepEv->WitnessesQty; i++)
        if(idpSwapBuf[j] != ToRemove)
            eepEv->Witnesses[i] = idpSwapBuf[j++];

    delete []idpSwapBuf;
}

//***************************************************************************
//IsEventWitness
//********** Description: **************
//returns whether the given ID is attached to the given event
//********** Parameters:  **************
//EventEntry *eepEv - given event
//ID ToCheck - witness ID
//---------------------------------------------------------------------------
BOOL BBECALL IsEventWitness(EventEntry *eepEv,ID ToCheck)
{
    unsigned short i;
    if(eepEv == NULL)
        return FALSE;
    if(eepEv->Witnesses == NULL || eepEv->WitnessesQty == 0)
        return FALSE;
    for(i = 0; i < eepEv->WitnessesQty; i++)
        if(ToCheck == eepEv->Witnesses[i])
            return TRUE;
    return FALSE;
}

//---------------------------------------------------------------------------
//---------------------------------------------------------------------------
BBECALL PersonalLog::PersonalLog()
{
    Log = NULL;
    LogLen = 0;
    InitiatorID = 0;
    MaxLogLen = gMaxLogLen;
    NextOverwritten = 0;
}

//---------------------------------------------------------------------------
BBECALL PersonalLog::~PersonalLog()
{
    ReallocLog(0);//free memory
}

//***************************************************************************
//PersonalLog::ReallocLog
//********** Description: **************
//reallocate memory by the given number of entries
//********** Parameters:  **************
//int nNewLen - new number of entries
//---------------------------------------------------------------------------
BOOL BBECALL PersonalLog::ReallocLog(int nNewLen)
{
    unsigned short i;
    PostError(0);

    if(Log != NULL && LogLen != 0)
    {
        for(i = 0; i < LogLen; i++)
            if(Log[i].Witnesses != NULL && Log[i].WitnessesQty != 0)
            {
                Log[i].WitnessesQty = 0;
                delete []Log[i].Witnesses;
                Log[i].Witnesses = NULL;
            }
        delete []Log;
        Log = NULL;
    }
    LogLen = 0;
    if(nNewLen <= 0)
        return TRUE;
    if(nNewLen > MaxLogLen)
    {
        PostError(INVALID_PARAM);
        return FALSE;
    }
    Log = new EventEntry[nNewLen];
    if(Log == NULL)
    {
        PostError(MEM_ALLOC_FAILED);
        return FALSE;
    }
    memset(Log,0,nNewLen * sizeof(EventEntry));    //maybe I'll enable it one day
    LogLen = (unsigned short)nNewLen;//update LogLen property
    return TRUE;
}

//***************************************************************************
//PersonalLog::SetInitiatorID
//********** Description: **************
//set the given ID to the PersonalLog
//********** Parameters:  **************
//ID NewInitiatorID - ID to set
//---------------------------------------------------------------------------
void BBECALL PersonalLog::SetInitiatorID(ID NewInitiatorID)
{ InitiatorID = NewInitiatorID; }

//***************************************************************************
//PersonalLog::GetInitiatorID
//********** Description: **************
//return initiator ID
//********** Parameters:  **************
//none
//---------------------------------------------------------------------------
ID BBECALL PersonalLog::GetInitiatorID()
{ return InitiatorID; }

//***************************************************************************
//PersonalLog::GetEventIndex
//********** Description: **************
//locate the given event and return its index; if none found, -1 is returned
//********** Parameters:  **************
//ID EvID         - event (action / strategy) definition node ID
//ID Obj          - objective ID
//ID ObjItem      - objective item ID
//EventType eType - event type: ACTION_FAILURE / STRATEGY_FAILURE / etc.
//---------------------------------------------------------------------------
int BBECALL PersonalLog::GetEventIndex(ID EvID,ID Obj,ID ObjItem,
    EventType eType)
{
    unsigned short i;
    for(i = 0; i < LogLen; i++)
        if(Log[i]._Type == eType && Log[i].EventID == EvID
                && Log[i].ObjectiveID == Obj && Log[i].ObjItemID == ObjItem)
            return i;
    return -1;
}

//***************************************************************************
//PersonalLog::WasEventWitness
//********** Description: **************
//check if the given ID is present in the witnesses list of the given event
//********** Parameters:  **************
//ID EvID         - event (action / strategy) definition node ID
//ID Obj          - objective ID
//ID ObjItem      - objective item ID
//EventType eType - event type: ACTION_FAILURE / STRATEGY_FAILURE / etc.
//ID Witness      - ID to check
//---------------------------------------------------------------------------
BOOL BBECALL PersonalLog::WasEventWitness(ID EvID,ID Obj,ID ObjItem,
        EventType eType,ID Witness)
{
    int nEvIndex;
    nEvIndex = GetEventIndex(EvID,Obj,ObjItem,eType);
    if(nEvIndex < 0)
        return FALSE;
    return IsEventWitness(Log + nEvIndex,Witness);
}

//***************************************************************************
//PersonalLog::GetPromiseIndex
//********** Description: **************
//shortcut to GetEventIndex when the event is ACTION_PROMISE
//********** Parameters:  **************
//ID EvID         - event (action / strategy) definition node ID
//ID Obj          - objective ID
//ID ObjItem      - objective item ID
//---------------------------------------------------------------------------
int BBECALL PersonalLog::GetPromiseIndex(ID EvID,ID Obj,ID ObjItem)
{
    return GetEventIndex(EvID,Obj,ObjItem,ACTION_PROMISE);
}

//***************************************************************************
//PersonalLog::GetFailedActionIndex
//********** Description: **************
//shortcut to GetEventIndex when the event is ACTION_FAILURE
//********** Parameters:  **************
//ID EvID         - event (action / strategy) definition node ID
//ID Obj          - objective ID
//ID ObjItem      - objective item ID
//---------------------------------------------------------------------------
int BBECALL PersonalLog::GetFailedActionIndex(ID EvID,ID Obj,ID ObjItem)
{
    return GetEventIndex(EvID,Obj,ObjItem,ACTION_FAILURE);
}

//***************************************************************************
//PersonalLog::GetFailedStrategyIndex
//********** Description: **************
//shortcut to GetEventIndex when the event is STRATEGY_FAILURE
//********** Parameters:  **************
//ID EvID         - event (action / strategy) definition node ID
//ID Obj          - objective ID
//ID ObjItem      - objective item ID
//---------------------------------------------------------------------------
int BBECALL PersonalLog::GetFailedStrategyIndex(ID EvID,ID Obj,ID ObjItem)
{
    return GetEventIndex(EvID,Obj,ObjItem,STRATEGY_FAILURE);
}

//***************************************************************************
//PersonalLog::GetEvent
//********** Description: **************
//get pointer to event by the given index
//********** Parameters:  **************
//unsigned short nIndex - index of the event
//---------------------------------------------------------------------------
EventEntry *BBECALL PersonalLog::GetEvent(unsigned short nIndex)
{
    return (nIndex < LogLen ? Log + nIndex : NULL);
}

//***************************************************************************
//PersonalLog::SetEvent
//********** Description: **************
//set event attributes by the given index
//********** Parameters:  **************
//ID EvID         - event (action / strategy) definition node ID
//ID Obj          - objective ID
//ID ObjItem      - objective item ID
//EventType eType - event type: ACTION_FAILURE / STRATEGY_FAILURE / etc.
//unsigned short nIndex - index of the event
//---------------------------------------------------------------------------
BOOL BBECALL PersonalLog::SetEvent(ID EvID,ID Obj,ID ObjItem,
        EventType eType,unsigned short nIndex)
{
    if(nIndex >= LogLen)
        return FALSE;
    Log[nIndex].EventID = EvID;
    Log[nIndex].ObjectiveID = Obj;
    Log[nIndex].ObjItemID = ObjItem;
    Log[nIndex]._Type = eType;
    return TRUE;
}

//***************************************************************************
//PersonalLog::AddEvent
//********** Description: **************
//add event to the PersonalLog
//********** Parameters:  **************
//ID EvID         - event (action / strategy) definition node ID
//ID Obj          - objective ID
//ID ObjItem      - objective item ID
//EventType eType - event type: ACTION_FAILURE / STRATEGY_FAILURE / etc.
//unsigned short nIndex - index of the event
//---------------------------------------------------------------------------
EventEntry *BBECALL PersonalLog::AddEvent(ID EvID,ID Obj,ID ObjItem,EventType eType)
{
    EventEntry *eeSwapBuf;
    BOOL bOverwrite = FALSE;//that is, if we're out of space

    if(Log == NULL)
    {
        if(MaxLogLen < 1 || !ReallocLog(1))
            return NULL;
        Log[0].EventID = EvID;
        Log[0].ObjectiveID = Obj;
        Log[0].ObjItemID = ObjItem;
        Log[0].Witnesses = NULL;
        Log[0].WitnessesQty = 0;
        Log[0]._Type = eType;
        NextOverwritten++;
        return Log;
    }

    if(GetEventIndex(EvID,Obj,ObjItem,eType) > -1)
        return NULL;
    //event already registered - SHOULDN'T REGISTER IT ONCE AGAIN
    if(LogLen >= MaxLogLen)
        bOverwrite = TRUE;
    if(bOverwrite)
    {
        if(NextOverwritten >= LogLen)
            NextOverwritten = 0;
    }
    else
    {
        eeSwapBuf = new EventEntry[LogLen];
        if(eeSwapBuf == NULL)
            return NULL;
        memcpy(eeSwapBuf,Log,sizeof(EventEntry) * LogLen);
        if(!ReallocLog(LogLen+1))
            return NULL;
        NextOverwritten = (unsigned short)(LogLen-1);
        //LogLen changed
        memcpy(Log,eeSwapBuf,sizeof(EventEntry) * (LogLen-1));
        delete []eeSwapBuf;
    }

    Log[NextOverwritten].EventID = EvID;
    Log[NextOverwritten].ObjectiveID = Obj;
    Log[NextOverwritten].ObjItemID = ObjItem;
    Log[NextOverwritten].Witnesses = NULL;
    Log[NextOverwritten].WitnessesQty = 0;
    Log[NextOverwritten]._Type = eType;
    NextOverwritten++;
    return Log + NextOverwritten - 1;
}

//***************************************************************************
//PersonalLog::RemoveEvent
//********** Description: **************
//remove event from the PersonalLog
//********** Parameters:  **************
//ID EvID         - event (action / strategy) definition node ID
//ID Obj          - objective ID
//ID ObjItem      - objective item ID
//EventType eType - event type: ACTION_FAILURE / STRATEGY_FAILURE / etc.
//unsigned short nIndex - index of the event
//---------------------------------------------------------------------------
void BBECALL PersonalLog::RemoveEvent(unsigned short nIndex)
{
    EventEntry *eeSwapBuf;
    unsigned short i;
    eeSwapBuf = new EventEntry[LogLen];
    if(eeSwapBuf == NULL)
        return;
    memcpy(eeSwapBuf,Log,sizeof(EventEntry) * LogLen);
    if(!ReallocLog(LogLen-1))
    //which will delete nothing, by the way...
        return;
    for(i = 0; i < LogLen; i++)
        if(i != nIndex)
            Log[i] = eeSwapBuf[i];
}

//***************************************************************************
//PersonalLog::DisconnectPointer
//********** Description: **************
//remove the link between the attribute pointers to the allocated buffers
//********** Parameters:  **************
//none
//---------------------------------------------------------------------------
void BBECALL PersonalLog::DisconnectPointer()
{
    Log = NULL;
    LogLen = 0;
    NextOverwritten = 0;
}

//---------------------------------------------------------------------------
//---------------------------------------------------------------------------
BBECALL EventsRegistry::EventsRegistry()
{
    LogsQty = 0; Logs = NULL;
}

//---------------------------------------------------------------------------
BBECALL EventsRegistry::~EventsRegistry()
{
    Clear();
}

//***************************************************************************
//EventsRegistry::ReallocRegistry
//********** Description: **************
//reallocate memory by the given number of entries
//********** Parameters:  **************
//int nNewLen - new number of entries
//---------------------------------------------------------------------------
BOOL BBECALL EventsRegistry::ReallocRegistry(int nNewLen)
{
    PostError(0);

    if(Logs != NULL)
    {
        if(nNewLen > 0)
        //that means, the memory will be reused
          for(int i = 0; i < LogsQty; i++)
            Logs[i].DisconnectPointer();
        delete []Logs;
        Logs = NULL;
    }
    LogsQty = 0;
    if(nNewLen <= 0)
        return TRUE;

    Logs = new PersonalLog[nNewLen];
    if(Logs == NULL)
    {
        PostError(MEM_ALLOC_FAILED);
        return FALSE;
    }
    LogsQty = (unsigned short)nNewLen;//update LogLen property
    return TRUE;
}

//***************************************************************************
//EventsRegistry::Register
//********** Description: **************
//register the given event, creating a new PersonalLog, if needed
//********** Parameters:  **************
//ID InitiID      - initiator's ID
//ID EvID         - event (action / strategy) definition node ID
//ID Obj          - objective ID
//ID ObjItem      - objective item ID
//EventType eType - event type: ACTION_FAILURE / STRATEGY_FAILURE / etc.
//---------------------------------------------------------------------------
void BBECALL EventsRegistry::Register(ID InitiID,ID EvID,ID ObjID,ID ObjItID,
        EventType eType)
{
    PersonalLog *ToUse;
    ToUse = GetInitiatorLog(InitiID);
    if(ToUse == NULL)
    {
        ToUse = AddInitiatorLog(InitiID);
    }

    if(ToUse == NULL)
        return;
    if(ToUse->GetEventIndex(EvID,ObjID,ObjItID,eType) > -1)
    //do not register twice!
        return;
    ToUse->AddEvent(EvID,ObjID,ObjItID,eType);
}


//***************************************************************************
//EventsRegistry::IsEventRegistered
//********** Description: **************
//check if the given event is registered
//********** Parameters:  **************
//ID InitiID      - initiator's ID
//ID EvID         - event (action / strategy) definition node ID
//ID ObjID        - objective ID
//ID ObjItID      - objective item ID
//EventType eType - event type: ACTION_FAILURE / STRATEGY_FAILURE / etc.
//---------------------------------------------------------------------------
BOOL BBECALL EventsRegistry::IsEventRegistered(ID InitiID,ID EvID,ID ObjID,
        ID ObjItID,
        EventType eType)
{
    return (GetEventEntry(InitiID,EvID,ObjID,ObjItID,eType) == NULL ?
        FALSE : TRUE);
}

//***************************************************************************
//EventsRegistry::GetEventEntry
//********** Description: **************
//return pointer to event entry if exists
//********** Parameters:  **************
//ID InitiID      - initiator's ID
//ID EvID         - event (action / strategy) definition node ID
//ID ObjID        - objective ID
//ID ObjItID      - objective item ID
//EventType eType - event type: ACTION_FAILURE / STRATEGY_FAILURE / etc.
//---------------------------------------------------------------------------
EventEntry *BBECALL EventsRegistry::GetEventEntry(ID InitiID,ID EvID,ID ObjID,
        ID ObjItID,
        EventType eType)
{
    PersonalLog *ToUse;
    int nIndex;
    ToUse = GetInitiatorLog(InitiID);
    if(ToUse == NULL)
        return NULL;
    nIndex = ToUse->GetEventIndex(EvID,ObjID,ObjItID,eType);
    if(nIndex < 0)
        return NULL;
    return ToUse->GetEvent((unsigned short)nIndex);
}

//***************************************************************************
//EventsRegistry::Unregister
//********** Description: **************
//remove event from registry, if present
//********** Parameters:  **************
//ID InitiID      - initiator's ID
//ID EvID         - event (action / strategy) definition node ID
//ID ObjID        - objective ID
//ID ObjItID      - objective item ID
//EventType eType - event type: ACTION_FAILURE / STRATEGY_FAILURE / etc.
//---------------------------------------------------------------------------
void BBECALL EventsRegistry::Unregister(ID InitiID,ID EvID,ID ObjID,
        ID ObjItID,
        EventType eType)
{
    PersonalLog *ToUse;
    int nIndex;
    ToUse = GetInitiatorLog(InitiID);
    if(ToUse == NULL)
        return;
    nIndex = ToUse->GetEventIndex(EvID,ObjID,ObjItID,eType);
    if(nIndex < 0)
        return;
    ToUse->RemoveEvent((unsigned short)nIndex);
}

//***************************************************************************
//EventsRegistry::GetInitiatorLog
//********** Description: **************
//return pointer to the given initiator's PersonalLog
//********** Parameters:  **************
//ID InitiID      - initiator's ID
//---------------------------------------------------------------------------
PersonalLog *BBECALL EventsRegistry::GetInitiatorLog(ID InitiID)
{
    unsigned short i;
    if(LogsQty < 1 || Logs == NULL)
        return NULL;
    for(i = 0; i < LogsQty; i++)
    {
        if(Logs[i].GetInitiatorID() == InitiID)
            return Logs+i;
    }
    return NULL;
}

//***************************************************************************
//EventsRegistry::AddInitiatorLog
//********** Description: **************
//create a new initiator log - only if none created so far
//********** Parameters:  **************
//ID NewInitiID      - initiator's ID
//---------------------------------------------------------------------------
PersonalLog *BBECALL EventsRegistry::AddInitiatorLog(ID NewInitiID)
{
    BYTE *plSwapBuf;//PersonalLog *plSwapBuf;
    if(GetInitiatorLog(NewInitiID) != NULL)
        return NULL;

    if(Logs == NULL)
    {
        if(!ReallocRegistry(1))
            return NULL;
        Logs[LogsQty-1].SetInitiatorID(NewInitiID);
        return Logs;
    }

    plSwapBuf = new BYTE[LogsQty*sizeof(PersonalLog)];//PersonalLog[LogsQty];
    if(plSwapBuf == NULL)
        return NULL;
    memcpy(plSwapBuf,Logs,sizeof(PersonalLog) * LogsQty);
    if(!ReallocRegistry(LogsQty+1))
    {
        return NULL;
    }
    //LogsQty changed, incremented by 1
    memcpy(Logs,plSwapBuf,sizeof(PersonalLog) * (LogsQty-1));

    Logs[LogsQty-1].SetInitiatorID(NewInitiID);
    /*
    for(int i = 0; i < LogsQty - 1; i++)
        plSwapBuf[i].DisconnectPointer();
    */
    delete []plSwapBuf;
    return Logs + LogsQty - 1;
}

//***************************************************************************
//EventsRegistry::RemoveInitiatorLog
//********** Description: **************
//remove the given initiator log
//********** Parameters:  **************
//ID InitiID      - initiator's ID
//---------------------------------------------------------------------------
BOOL BBECALL EventsRegistry::RemoveInitiatorLog(ID InitiID)
{
    PersonalLog *plSwapBuf;
    unsigned short i;
    if(LogsQty == 0 || Logs == NULL)
        return FALSE;
    if(GetInitiatorLog(InitiID) == NULL)
        return FALSE;

    plSwapBuf = new PersonalLog[LogsQty-1];
    if(plSwapBuf == NULL)
        return FALSE;
    for(i = 0; i < LogsQty; i++)
        if(Logs[i].GetInitiatorID() != InitiID)
            plSwapBuf[i] = Logs[i];

    if(!ReallocRegistry(LogsQty-1))
        return FALSE;
    memcpy(Logs,plSwapBuf,sizeof(PersonalLog) * LogsQty);//LogsQty changed
    delete []plSwapBuf;
    return TRUE;
}

//***************************************************************************
//EventsRegistry::Clear
//********** Description: **************
//remove all the events from the registry
//********** Parameters:  **************
//none
//---------------------------------------------------------------------------
void BBECALL EventsRegistry::Clear()
{
    ReallocRegistry(0); //free memory
}

//---------------------------------------------------------------------------

