/******************************************************************
  
  Module:  aiblackboard.cpp
  
  Author: Borut Pfeifer
  
  Copyright 2005 Sony Online Entertainment.  All rights reserved.
  
*******************************************************************/

//-------------------------------------------------------- Includes
#include "aiblackboard.h"
#include "../netpacket.h"
#include "../registry.h"
#include "../titan.h"
#include "../titanpeeringnetwork.h"

//---------------------------------------------- Class Declarations

//----------------------------------------- Functions Declarations

//------------------------------------ Member Functions Definitions

//------------------------------------ cAIBlackboard

cAIBlackboard* cAIBlackboard::smpInstance = NULL;

cAIBlackboard::Record::Record()
: mType(BBR_INVALID)
{
  InitPropObject(AIBLACKBOARD_RECORD_CLASSID);
}

int cAIBlackboard::Record::InitPropClass()
{
  AddClass( AIBLACKBOARD_RECORD_CLASSID, 
            "cAIBlackboard_Record", 
            Creator, 
            AIBLACKBOARD_RECORD_CLASSID, 
            0 ); 

  SyPropEnum *propEnum;
  AddEnumProperty(AIBLACKBOARD_RECORD_CLASSID,0x0000,SyMemberOffset(cAIBlackboard::Record,mType),"mType",&propEnum);
  propEnum->Add(BBR_INVALID,"BBR_INVALID");
  propEnum->Add(BBR_ATTACKING_MELEE,"BBR_ATTACKING_MELEE");
  propEnum->Add(BBR_ATTACKING_RANGED,"BBR_ATTACKING_RANGED");
  propEnum->Add(BBR_FLANK_REQUEST,"BBR_FLANK_REQUEST");
  propEnum->Add(BBR_FLANK_PARTNER,"BBR_FLANK_PARTNER");
  propEnum->Add(BBR_MAX_TYPES,"BBR_MAX_TYPES");

  AddInt32Property(AIBLACKBOARD_RECORD_CLASSID,0x0001,SyMemberOffset(cAIBlackboard::Record,mSourceID),"mSourceID");
  AddInt32Property(AIBLACKBOARD_RECORD_CLASSID,0x0002,SyMemberOffset(cAIBlackboard::Record,mTargetID),"mTargetID");
  AddTimeProperty(AIBLACKBOARD_RECORD_CLASSID, 0x0003,SyMemberOffset(cAIBlackboard::Record,mCreatedTimestamp), "mCreatedTimestamp");

  return 0;
}

SyPropObject* cAIBlackboard::Record::Creator()
{
  SyPropObject *pObject;

  pObject = SyNew cAIBlackboard::Record();
  if(pObject == NULL)
  {
    SyAssert(0);
    return(NULL);
  }

  return(pObject);
}


cAIBlackboard::cAIBlackboard()
: mSearchRecordType(BBR_INVALID),
  mSearchSourceID(ID_NONE),
  mSearchTargetID(ID_NONE),
  mSearchIndex(-1),
  mpOwner(NULL),
  mbRemote(false)
{
  SyAssertf(smpInstance == NULL, "Trying to allocate two cAIBlackboards!");
  smpInstance = this;

  InitPropObject( AIBLACKBOARD_CLASSID );
}

cAIBlackboard::~cAIBlackboard()
{
  for (int iRec = mRecords.Begin(); iRec!=mRecords.End(); iRec = mRecords.Next(iRec))
  {
    delete mRecords(iRec);
  }
  mRecords.Clear();

  smpInstance = NULL;
}

int cAIBlackboard::InitPropClass()
{
  AddClass( AIBLACKBOARD_CLASSID, 
            "cAIBlackboard", 
            Creator, 
            AIBLACKBOARD_CLASSID, 
            0 ); 

  AddSubObjectPtrVectorProperty<Record>(AIBLACKBOARD_CLASSID, 0x0000, SyMemberOffset(cAIBlackboard,mRecords), "Records");
//  AddVectorProperty(AIBLACKBOARD_CLASSID,0x0000,SyMemberOffset(cAIBlackboard,mRecords),"mRecords");

  Record::InitPropClass();
  return 0;
}

SyPropObject* cAIBlackboard::Creator()
{
  SyPropObject *pObject;

  pObject = SyNew cAIBlackboard();
  if(pObject == NULL)
  {
    SyAssert(0);
    return(NULL);
  }

  return(pObject);
}

void cAIBlackboard::Init(cLevelObject* pLevelObj)
{
  SyAssertf(pLevelObj != NULL, "Bad cAIBlackboard init");

  mpOwner = pLevelObj;
}
void cAIBlackboard::SetRemote(bool bRemote)
{
  mbRemote = bRemote;
}

bool cAIBlackboard::IsRemote()
{
  return mbRemote;
}

int cAIBlackboard::CountRecords(RecordType type, tGameObjectID sourceID, tGameObjectID targetID)
{
  int iRec;
  int endRec = mRecords.End();
  int count = 0;

  for (iRec = mRecords.Begin(); iRec!=endRec; iRec = mRecords.Next(iRec))
  {
    if (mRecords(iRec)->mType == type &&
        (ID_NONE == sourceID || mRecords(iRec)->mSourceID == sourceID) &&
        (ID_NONE == targetID || mRecords(iRec)->mTargetID == targetID))
    {
      ++count;
    }
  }

  return count;
}

bool cAIBlackboard::GetFirstRecord(RecordType type, tGameObjectID sourceID, tGameObjectID targetID, Record& rec)
{
  mSearchRecordType = type;
  mSearchSourceID = sourceID;
  mSearchTargetID = targetID;
  mSearchIndex = 0;

 return GetNextRecord(rec);
}

bool cAIBlackboard::GetNextRecord(Record& rec)
{
  int numRecs = mRecords.Size();
  bool bFound = false;

  for (int i=mSearchIndex; i<numRecs && !bFound; ++i)
  {
    if (mRecords(i)->mType == mSearchRecordType &&
        (ID_NONE == mSearchSourceID || mRecords(i)->mSourceID == mSearchSourceID) &&
        (ID_NONE == mSearchTargetID || mRecords(i)->mTargetID == mSearchTargetID))
    {
      rec = *(mRecords(i));
      mSearchIndex = i+1;
      bFound = true;
    }
  }

  return bFound;
}

void cAIBlackboard::AddRecord(RecordType type,
                              tGameObjectID sourceID,
                              tGameObjectID targetID,
                              unsigned int data,
                              const SyVect3& location)
{
  SendAddRecord(type, sourceID, targetID, data, location);

  if (!IsRemote())
  {
    AddRecordLocal(type, sourceID, targetID, data, location);
  }
}

void cAIBlackboard::SendAddRecord(RecordType type,
                                  tGameObjectID sourceID,
                                  tGameObjectID targetID,
                                  unsigned int data,
                                  const SyVect3& location)
{
  cNetAIBlackboardPacket packet;
  packet.mRecordType = type;
  packet.mSourceID = sourceID;
  packet.mTargetID = targetID;
  packet.mData = data;
  packet.mLocation = location;
  packet.mbRemoveIfExists = false;

  char buf[1024];
  int len = packet.PackBuffer(buf, sizeof(buf));

  SyAssertf(mpOwner!=NULL && mpOwner->GetTitan()!=NULL && mpOwner->GetTitan()->GetPeeringNetwork()!=NULL, "Bad owner for blackboard");

  if (mpOwner!=NULL && mpOwner->GetTitan()!=NULL && mpOwner->GetTitan()->GetPeeringNetwork()!=NULL)
  {
    if (IsRemote())
    {
      // remotes only send to the owner
      mpOwner->GetTitan()->GetPeeringNetwork()->ObjectMessage(mpOwner->GetID(), buf, len);
    }
    else
    {
      // the owner always broadcasts
      mpOwner->GetTitan()->GetPeeringNetwork()->ObjectBroadcast(mpOwner->GetID(), buf, len);
    }
  }
}


void cAIBlackboard::AddRecordLocal(RecordType type,
                                   tGameObjectID sourceID,
                                   tGameObjectID targetID,
                                   unsigned int data,
                                   const SyVect3& location)
{
  int iRec;
  int endRec = mRecords.End();
  bool bFound = false;

  for (iRec = mRecords.Begin(); iRec!=endRec && !bFound; iRec = mRecords.Next(iRec))
  {
    if (mRecords(iRec)->mType == type &&
        mRecords(iRec)->mSourceID == sourceID &&
        mRecords(iRec)->mTargetID == targetID)
    {
      mRecords(iRec)->mData = data;
      mRecords(iRec)->mLocation = location;
      bFound = true;
    }
  }

  if (!bFound)
  {
    Record* pRec = new Record;
    SyAssert(pRec);
    if (pRec)
    {
      pRec->mType = type;
      pRec->mSourceID = sourceID;
      pRec->mTargetID = targetID;
      pRec->mData = data;
      pRec->mLocation = location;
      pRec->mCreatedTimestamp = SyTimer::GetTime();
      mRecords.Add(pRec);
    }
  }
}

void cAIBlackboard::RemoveRecords(RecordType type,
                                  tGameObjectID sourceID,
                                  tGameObjectID targetID)
{
  SendRemoveRecords(type, sourceID, targetID);

  if (!IsRemote())
  {
    RemoveRecordsLocal(type, sourceID, targetID);
  }
}

void cAIBlackboard::SendRemoveRecords(RecordType type,
                                      tGameObjectID sourceID,
                                      tGameObjectID targetID)
{
  cNetAIBlackboardPacket packet;
  packet.mRecordType = type;
  packet.mSourceID = sourceID;
  packet.mTargetID = targetID;
  packet.mData = 0;
  packet.mLocation(0.0f, 0.0f, 0.0f);
  packet.mbRemoveIfExists = true;

  char buf[1024];
  int len = packet.PackBuffer(buf, sizeof(buf));

  SyAssertf(mpOwner!=NULL && mpOwner->GetTitan()!=NULL && mpOwner->GetTitan()->GetPeeringNetwork()!=NULL, "Bad owner for blackboard");

  if (mpOwner!=NULL && mpOwner->GetTitan()!=NULL && mpOwner->GetTitan()->GetPeeringNetwork()!=NULL)
  {
    if (IsRemote())
    {
      // remotes only send to the owner
      mpOwner->GetTitan()->GetPeeringNetwork()->ObjectMessage(mpOwner->GetID(), buf, len);
    }
    else
    {
      // the owner always broadcasts
      mpOwner->GetTitan()->GetPeeringNetwork()->ObjectBroadcast(mpOwner->GetID(), buf, len);
    }
  }
}

void cAIBlackboard::RemoveRecordsLocal(RecordType type,
                                       tGameObjectID sourceID,
                                       tGameObjectID targetID)
{
  int iRec = mRecords.Begin();

  while (iRec != mRecords.End())
  {
    if ((BBR_INVALID == type || mRecords(iRec)->mType == type) &&
        (ID_NONE == sourceID || mRecords(iRec)->mSourceID == sourceID) &&
        (ID_NONE == targetID || mRecords(iRec)->mTargetID == targetID))
    {
      delete mRecords(iRec);
      iRec = mRecords.ReplaceLast(iRec);
    }
    else
    {
      iRec = mRecords.Next(iRec);
    }
  }
}

void cAIBlackboard::NetworkReceiveBroadcast(const char *state, int maxlen)
{
  // receive data broadcast from the owning blackboard

  cNetAIBlackboardPacket packet;
  packet.UnpackBuffer(state, maxlen);

  SyAssertf(IsRemote(), "Owner receiving network instructions?"); 
  SyAssertf(packet.mType == cNetPacket::NET_AIBLACKBOARD, "Unknown packet");

  if (packet.mType != cNetPacket::NET_AIBLACKBOARD || !IsRemote())
  {
    return;
  }

  if (packet.mbRemoveIfExists)
  {
    RemoveRecordsLocal((cAIBlackboard::RecordType)packet.mRecordType, packet.mSourceID, packet.mTargetID);
  }
  else
  {
    AddRecordLocal((cAIBlackboard::RecordType)packet.mRecordType, packet.mSourceID, packet.mTargetID, packet.mData, packet.mLocation);
  }
}

void cAIBlackboard::NetworkReceiveMessage(const char *state, int maxlen)
{
  // the owning blackboard is being notified by remotes of data
  // added or removed or removed remotely

  cNetAIBlackboardPacket packet;
  packet.UnpackBuffer(state, maxlen);

  SyAssertf(!IsRemote(), "Blackboard owner should only be receiving direct messages, not remotes"); 
  SyAssertf(packet.mType == cNetPacket::NET_AIBLACKBOARD, "Unknown packet");

  if (packet.mType != cNetPacket::NET_AIBLACKBOARD || IsRemote())
  {
    return;
  }

  if (packet.mbRemoveIfExists)
  {
    RemoveRecordsLocal((cAIBlackboard::RecordType)packet.mRecordType, packet.mSourceID, packet.mTargetID);

    // update all the other peers
    SendRemoveRecords((cAIBlackboard::RecordType)packet.mRecordType, packet.mSourceID, packet.mTargetID);
  }
  else
  {
    AddRecordLocal((cAIBlackboard::RecordType)packet.mRecordType, packet.mSourceID, packet.mTargetID, packet.mData, packet.mLocation);

    // update all the other peers
    SendAddRecord((cAIBlackboard::RecordType)packet.mRecordType, packet.mSourceID, packet.mTargetID, packet.mData, packet.mLocation);
  }
}
// EOF
