//========= Copyright © 1996-2005, Valve Corporation, All rights reserved. ============//
//
// Purpose:
//
// $NoKeywords: $
//=============================================================================//

#include "cbase.h"

#include "isaverestore.h"
#include "ai_behavior.h"
#include "scripted.h"

// memdbgon must be the last include file in a .cpp file!!!
#include "tier0/memdbgon.h"

bool g_bBehaviorHost_PreventBaseClassGatherConditions;

//-----------------------------------------------------------------------------
// CAI_BehaviorBase
//-----------------------------------------------------------------------------

BEGIN_DATADESC_NO_BASE( CAI_BehaviorBase )

END_DATADESC()

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

CAI_ClassScheduleIdSpace *CAI_BehaviorBase::GetClassScheduleIdSpace()
{
	return GetOuter()->GetClassScheduleIdSpace();
}

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

void CAI_BehaviorBase::GatherConditions()
{
	Assert( m_pBackBridge != NULL );
	
	m_pBackBridge->BackBridge_GatherConditions();
}

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

void CAI_BehaviorBase::PrescheduleThink()
{
}

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

void CAI_BehaviorBase::OnScheduleChange()
{
}

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

void CAI_BehaviorBase::OnStartSchedule( int scheduleType )
{
}

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

int CAI_BehaviorBase::SelectSchedule()
{
	Assert( m_pBackBridge != NULL );
	
	return m_pBackBridge->BackBridge_SelectSchedule();
}

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

int CAI_BehaviorBase::SelectFailSchedule( int failedSchedule, int failedTask, AI_TaskFailureCode_t taskFailCode )
{
	m_fOverrode = false; 
	return SCHED_NONE;
}

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

void CAI_BehaviorBase::StartTask( const Task_t *pTask )
{
	m_fOverrode = false;
}

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

void CAI_BehaviorBase::RunTask( const Task_t *pTask )
{
	m_fOverrode = false;
}

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

void CAI_BehaviorBase::AimGun( void )
{
	m_fOverrode = false;
}

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

int CAI_BehaviorBase::TranslateSchedule( int scheduleType )
{
	Assert( m_pBackBridge != NULL );
	
	return m_pBackBridge->BackBridge_TranslateSchedule( scheduleType );
}

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

CAI_Schedule *CAI_BehaviorBase::GetSchedule(int schedule)
{
	if (!GetClassScheduleIdSpace()->IsGlobalBaseSet())
	{
		Warning("ERROR: %s missing schedule!\n", GetSchedulingErrorName());
		return g_AI_SchedulesManager.GetScheduleFromID(SCHED_IDLE_STAND);
	}
	if ( AI_IdIsLocal( schedule ) )
	{
		schedule = GetClassScheduleIdSpace()->ScheduleLocalToGlobal(schedule);
	}

	if ( schedule == -1 )
		return NULL;

	return g_AI_SchedulesManager.GetScheduleFromID( schedule );
}

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

bool CAI_BehaviorBase::IsCurSchedule( int schedule, bool fIdeal )
{
	if ( AI_IdIsLocal( schedule ) )
		schedule = GetClassScheduleIdSpace()->ScheduleLocalToGlobal(schedule);

	return GetOuter()->IsCurSchedule( schedule, fIdeal );
}

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

const char *CAI_BehaviorBase::GetSchedulingErrorName()
{
	return "CAI_Behavior";
}

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

Activity CAI_BehaviorBase::NPC_TranslateActivity( Activity activity )
{
	Assert( m_pBackBridge != NULL );
	
	return m_pBackBridge->BackBridge_NPC_TranslateActivity( activity );
}

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

bool CAI_BehaviorBase::IsCurTaskContinuousMove()
{
	m_fOverrode = false;
	return false;
}

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

float CAI_BehaviorBase::GetDefaultNavGoalTolerance()
{
	Assert( m_pBackBridge != NULL );
	
	return m_pBackBridge->BackBridge_GetDefaultNavGoalTolerance();
}

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

bool CAI_BehaviorBase::FValidateHintType( CAI_Hint *pHint )
{
	m_fOverrode = false;
	return false;
}

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

bool CAI_BehaviorBase::IsValidEnemy( CBaseEntity *pEnemy )
{
	Assert( m_pBackBridge != NULL );
	
	return m_pBackBridge->BackBridge_IsValidEnemy( pEnemy );
}

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

CBaseEntity *CAI_BehaviorBase::BestEnemy( void )
{
	Assert( m_pBackBridge != NULL );
	
	return m_pBackBridge->BackBridge_BestEnemy();
}

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

bool CAI_BehaviorBase::IsValidCover( const Vector &vLocation, CAI_Hint const *pHint )
{
	Assert( m_pBackBridge != NULL );
	
	return m_pBackBridge->BackBridge_IsValidCover( vLocation, pHint );
}

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

bool CAI_BehaviorBase::IsValidShootPosition( const Vector &vLocation, CAI_Node *pNode, CAI_Hint const *pHint )
{
	Assert( m_pBackBridge != NULL );
	
	return m_pBackBridge->BackBridge_IsValidShootPosition( vLocation, pNode, pHint );
}

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

bool CAI_BehaviorBase::ShouldIgnoreSound( CSound *pSound )
{
	Assert( m_pBackBridge != NULL );
	
	return m_pBackBridge->BackBridge_ShouldIgnoreSound( pSound );
}

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

bool CAI_BehaviorBase::IsInterruptable( void )
{
	Assert( m_pBackBridge != NULL );
	
	return m_pBackBridge->BackBridge_IsInterruptable();
}

bool CAI_BehaviorBase::ShouldPlayerAvoid( void )
{
	Assert( m_pBackBridge != NULL );
	
	return m_pBackBridge->BackBridge_ShouldPlayerAvoid();
}

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

int CAI_BehaviorBase::OnTakeDamage_Alive( const CTakeDamageInfo &info )
{
	Assert( m_pBackBridge != NULL );
	
	return m_pBackBridge->BackBridge_OnTakeDamage_Alive( info );
}

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

float CAI_BehaviorBase::GetReasonableFacingDist( void )
{
	Assert( m_pBackBridge != NULL );
	
	return m_pBackBridge->BackBridge_GetReasonableFacingDist();
}

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

bool CAI_BehaviorBase::ShouldAlwaysThink()
{
	m_fOverrode = false;
	return false;
}

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

bool CAI_BehaviorBase::NotifyChangeBehaviorStatus( bool fCanFinishSchedule )
{
	bool fInterrupt = GetOuter()->OnBehaviorChangeStatus( this, fCanFinishSchedule );
	
	if ( !GetOuter()->IsInterruptable())
		return false;
		
	if ( fInterrupt )
	{
		if ( GetOuter()->m_hCine )
			GetOuter()->m_hCine->CancelScript();

		//!!!HACKHACK
		// this is dirty, but it forces NPC to pick a new schedule next time through.
		GetOuter()->ClearSchedule();
	}

	return fInterrupt;
}

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

int	CAI_BehaviorBase::Save( ISave &save )				
{ 
	return save.WriteAll( this, GetDataDescMap() );	
}

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

int	CAI_BehaviorBase::Restore( IRestore &restore )
{ 
	return restore.ReadAll( this, GetDataDescMap() );	
}

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

#define BEHAVIOR_SAVE_BLOCKNAME "AI_Behaviors"
#define BEHAVIOR_SAVE_VERSION	2

void CAI_BehaviorBase::SaveBehaviors(ISave &save, CAI_BehaviorBase *pCurrentBehavior, CAI_BehaviorBase **ppBehavior, int nBehaviors )		
{ 
	save.StartBlock( BEHAVIOR_SAVE_BLOCKNAME );
	short temp = BEHAVIOR_SAVE_VERSION;
	save.WriteShort( &temp );
	temp = (short)nBehaviors;
	save.WriteShort( &temp );

	for ( int i = 0; i < nBehaviors; i++ )
	{
		if ( strcmp( ppBehavior[i]->GetDataDescMap()->dataClassName, CAI_BehaviorBase::m_DataMap.dataClassName ) != 0 )
		{
			save.StartBlock();
			save.WriteString( ppBehavior[i]->GetDataDescMap()->dataClassName );
			bool bIsCurrent = ( pCurrentBehavior == ppBehavior[i] );
			save.WriteBool( &bIsCurrent );
			ppBehavior[i]->Save( save );
			save.EndBlock();
		}
	}

	save.EndBlock();
}

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

int CAI_BehaviorBase::RestoreBehaviors(IRestore &restore, CAI_BehaviorBase **ppBehavior, int nBehaviors )	
{ 
	int iCurrent = -1;
	char szBlockName[SIZE_BLOCK_NAME_BUF];
	restore.StartBlock( szBlockName );
	if ( strcmp( szBlockName, BEHAVIOR_SAVE_BLOCKNAME ) == 0 )
	{
		short version;
		restore.ReadShort( &version );
		if ( version == BEHAVIOR_SAVE_VERSION )
		{
			short nToRestore;
			char szClassNameCurrent[256];
			restore.ReadShort( &nToRestore );
			for ( int i = 0; i < nToRestore; i++ )
			{
				restore.StartBlock();
				restore.ReadString( szClassNameCurrent, sizeof( szClassNameCurrent ), 0 );
				bool bIsCurrent;
				restore.ReadBool( &bIsCurrent );

				for ( int j = 0; j < nBehaviors; j++ )
				{
					if ( strcmp( ppBehavior[j]->GetDataDescMap()->dataClassName, szClassNameCurrent ) == 0 )
					{
						if ( bIsCurrent )
							iCurrent = j;
						ppBehavior[j]->Restore( restore );
					}
				}

				restore.EndBlock();

			}
		}
	}
	restore.EndBlock();
	return iCurrent; 
}


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