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

//-------------------------------------------------------- Includes
#include "lostable.h"
#include "SyEnvRay.h"
#include "SyScene.h"
#include "aidebugdraw.h"

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

//----------------------------------------- Functions Declarations
//------------------------------------ Member Functions Definitions
//------------------------------------ cLOSTable

cLOSTable::LOSEntry cLOSTable::smEntries[cLOSTable::MAX_LOS_ENTRIES];

cLOSTable::LOSEntry::LOSEntry()
: mUsedCount(0),
  mStatus(LOS_INVALID)
{
}

//------------------------------------------- cLOSTable
cLOSTable::LOSStatus cLOSTable::RequestLOS(const SyVect3& sourcePos,
                                           const SyVect3& targetPos,
                                           int& ticket)
{
  static const float DIST_TOLERANCE_SQR = 0.5f * 0.5f;
  SyVect3 deltaSource, deltaTarget;
  int emptyIndex = -1;
  ticket = -1;

  for (int i=0; i < MAX_LOS_ENTRIES; ++i)
  {
    if (0 == smEntries[i].mUsedCount && -1 == emptyIndex)
    {
      emptyIndex = i;
    }

    if (LOS_INVALID != smEntries[i].mStatus)
    {
      deltaSource = sourcePos - smEntries[i].mSourcePos;
      deltaSource = sourcePos - smEntries[i].mSourcePos;
      if (smEntries[i].mSourcePos.DistanceSquared(sourcePos) <= DIST_TOLERANCE_SQR &&
          smEntries[i].mTargetPos.DistanceSquared(targetPos) <= DIST_TOLERANCE_SQR)
      {
        smEntries[i].mUsedCount++;
        ticket = i;
        return smEntries[i].mStatus;
      }
    }
  }

  // if we got here there was no match
  smEntries[emptyIndex].mSourcePos = sourcePos;
  smEntries[emptyIndex].mTargetPos = targetPos;
  smEntries[emptyIndex].mHitPos(0.0f, 0.0f, 0.0f);
  smEntries[emptyIndex].mHitNormal(0.0f, 1.0f, 0.0f);
  smEntries[emptyIndex].mStatus = LOS_PENDING;
  smEntries[emptyIndex].mRequestTime = SyTimer::GetTime(); 
  smEntries[emptyIndex].mUsedCount++;
  ticket = emptyIndex;
  return LOS_PENDING;
}

void cLOSTable::CancelLOS(int ticket)
{
  SyAssertf(ticket>=0 && ticket < MAX_LOS_ENTRIES, "cLosTable - trying to cancel bad ticket %d", ticket);

  if (ticket < 0 || ticket >= MAX_LOS_ENTRIES)
  {
    return;
  }

  SyAssertf(smEntries[ticket].mUsedCount > 0, "CLOSTable trying to cancel already canceled LOS %d", ticket);
  if (smEntries[ticket].mUsedCount > 0)
  {
    smEntries[ticket].mUsedCount -= 1;
  }
}

cLOSTable::LOSStatus cLOSTable::HasLOS(int ticket,
                                       SyVect3* pHitPos,
                                       SyVect3* pHitNormal)
{
  SyAssertf(ticket>=0 && ticket < MAX_LOS_ENTRIES, "cLosTable - trying to access bad ticket %d", ticket);

  if (ticket < 0 || ticket >= MAX_LOS_ENTRIES)
  {
    return LOS_INVALID;
  }

  if (LOS_BLOCKED == smEntries[ticket].mStatus)
  {
    if (pHitPos)
    {
      *pHitPos = smEntries[ticket].mHitPos;
    }

    if (pHitNormal)
    {
      *pHitNormal = smEntries[ticket].mHitNormal;
    }
  }

  SyAssertf(smEntries[ticket].mUsedCount > 0, "CLOSTable trying to access invalid LOS %d", ticket);
  return smEntries[ticket].mStatus;
}

void cLOSTable::Update(SyScene* pScene)
{
  SyAssertf(pScene != NULL, "cLOSTable::Update - bad scene");

  if (!pScene)
  {
    return;
  }

  int oldestEntries[MAX_LOS_PER_FRAME];
  int replaceLOS;
  SyTime oldestTimes[MAX_LOS_PER_FRAME];
  SyTime waitTime, replaceTime;
  SyTime curTime = SyTimer::GetTime();

  SyCollRay ray;
  cLOSSceneFilter filter;

  //
  // Find the oldest entries
  //
  for (int iLOS = 0; iLOS < MAX_LOS_PER_FRAME; ++iLOS)
  {
    oldestEntries[iLOS] = -1;
    oldestTimes[iLOS] = SYTIME_MIN;
  }

  // store the n-oldest entries in the oldestEntries array
  for (int iEntry = 0; iEntry < MAX_LOS_ENTRIES; ++iEntry)
  {
    if (smEntries[iEntry].mUsedCount > 0 && LOS_PENDING == smEntries[iEntry].mStatus)
    {
      waitTime = curTime - smEntries[iEntry].mRequestTime;
      replaceLOS = -1;
      replaceTime = SYTIME_MIN;

      for (int iLOS = 0; iLOS < MAX_LOS_PER_FRAME; ++iLOS)
      {
        if (waitTime >= oldestTimes[iLOS] && oldestTimes[iLOS] >= replaceTime)
        {          
          replaceTime = oldestTimes[iLOS];
          replaceLOS = iLOS;
          break;
        }
      }

      if (replaceLOS >= 0)
      {
        oldestTimes[replaceLOS] = waitTime;
        oldestEntries[replaceLOS] = iEntry;
      }
    }
  }

  //
  // Now that we've found the oldest, perform the actual raycasts
  //
  for (int iLOS = 0; iLOS < MAX_LOS_PER_FRAME; ++iLOS)
  {
    if (oldestEntries[iLOS] >= 0)
    {
      filter.Init();
      ray.Init(smEntries[oldestEntries[iLOS]].mSourcePos, smEntries[oldestEntries[iLOS]].mTargetPos);

      if (pScene->Collide(ray, filter))
      {
//        AIDEBUGDRAW_LINE(0, smEntries[oldestEntries[iLOS]].mSourcePos, smEntries[oldestEntries[iLOS]].mTargetPos, cAIDebugDraw::RED);
        smEntries[oldestEntries[iLOS]].mStatus = LOS_BLOCKED;
        smEntries[oldestEntries[iLOS]].mHitPos = ray.GetHitPoint();
        smEntries[oldestEntries[iLOS]].mHitNormal = ray.GetHitNormal();
      }
      else
      {
//        AIDEBUGDRAW_LINE(0, smEntries[oldestEntries[iLOS]].mSourcePos, smEntries[oldestEntries[iLOS]].mTargetPos, cAIDebugDraw::GREEN);
        smEntries[oldestEntries[iLOS]].mStatus = LOS_CLEAR;
      }
    }
  }  
}


int cLOSSceneFilter::FilterActor( SyScene& scene, SyActorHandle actorHandle )
{
  SySpriteType type = scene.GetActorSpriteType(actorHandle);
  if (type != SYSPRITETYPE_BASE)  
  {
    return true;
  }

  return false;
}

// EOF
