//////////////////////////////////////////////////////////////////////////////
//
// Axon_System.cpp
//
// Created by NJD - 16 August 2006
//
// Copyright (C) 2006 Rebellion, All Rights Reserved.
//
//////////////////////////////////////////////////////////////////////////////

// I N C L U D E S ///////////////////////////////////////////////////////////

// core
#include "Core/Asura.h"

#ifdef ASURA_USE_AXON

// header
#include "Core/Axon_System.h"

// asura
#include "AI/Asura_AI_Navigation.h"
#include "Core/Asura_HashedList.h"
#include "Core/Asura_Scheduler.h"
#include "Core/Asura_Timers.h"
#include "Debugging/Asura_Assert.h"
#include "Debugging/Asura_CPUProfile.h"
#include "Entity/Locations/OrientedNode/GamesceneNode/Asura_ServerNode_GS2Node.h"
#include "Entity/Locations/Spline/GamesceneSpline/Asura_ServerNode_GS2Spline.h"
#if !defined(ASURA_NO_DEBUG_OUTPUT) && defined(ASURA_SUPPORT_COMMAND_CONSOLE)
#include "Interface/Asura_CommandConsole.h"
#endif

// axon
#include "AnimTemplates/Axon_Gamescene_AnimTemplate_System.h"
#include "Core/Axon_Brain.h"
#include "Core/Axon_Sound_Registry.h"

//////////////////////////////////////////////////////////////////////////////
//
// Axon_System
//
//////////////////////////////////////////////////////////////////////////////

Axon_System*             Axon_System::s_pxAxonSystem    = NULL;
Axon_Sound_Registry*     Axon_System::s_pxSoundRegistry = NULL;
Asura_List< Axon_Brain > Axon_System::s_xBrainList;
Asura_Scheduler          Axon_System::s_xBehaviourUpdateScheduler;
Asura_Scheduler          Axon_System::s_xKnowledgeUpdateScheduler;

#ifndef ASURA_NO_DEBUG_OUTPUT
Asura_Guid Axon_System::s_uRenderActorGuid = ASURA_GUID_UNREGISTERED;
bool Axon_System::s_bRenderActorAll = false;
bool Axon_System::s_bRenderGS2 = false;
bool Axon_System::s_bRenderNavWPs = false;
bool Axon_System::s_bRenderNavLinksAll = false;
bool Axon_System::s_bRenderNavLinksBlocked = false;
bool Axon_System::s_bRenderNavCoverpoints = false;
float Axon_System::s_fRenderNavRangeSQ = 20.0f;
bool Axon_System::s_bUpdateAI = true;
bool Axon_System::s_bRenderScheduler = false;
#endif

//////////////////////////////////////////////////////////////////////////////
//
// 
//
//////////////////////////////////////////////////////////////////////////////

#if !defined(ASURA_NO_DEBUG_OUTPUT) && defined(ASURA_SUPPORT_COMMAND_CONSOLE)
#define CONSOLE_CMDNAME_RENDERACTORGUID			L"AI.Actor.Render.Guid"
#define CONSOLE_CMDNAME_RENDERACTORALL			L"AI.Actor.Render.All"
#define CONSOLE_CMDNAME_RENDERGS2				L"AI.GS2.Render"
#define CONSOLE_CMDNAME_RENDERNAVWPS			L"AI.Nav.Render.WPs"
#define CONSOLE_CMDNAME_RENDERNAVLINKSALL		L"AI.Nav.Render.Links.All"
#define CONSOLE_CMDNAME_RENDERNAVLINKSBLOCKED	L"AI.Nav.Render.Links.Blocked"
#define CONSOLE_CMDNAME_RENDERNAVCOVERPOINTS	L"AI.Nav.Render.Coverpoints"
#define CONSOLE_CMDNAME_RENDERNAVRANGE			L"AI.Nav.Render.Range"
#define CONSOLE_CMDNAME_UPDATEAI				L"AI.Update"
#define CONSOLE_CMDNAME_RENDERSCHEDULER			L"AI.Scheduler.Render"

bool Axon_System::ConsoleCmd_RenderActorGuid( u_int uGuid, bool bInterrogate, bool bHelp )
{
	if ( bHelp )
	{
		Asura_CommandConsole::Printf( CONSOLE_CMDNAME_RENDERACTORGUID );
		Asura_CommandConsole::Printf( "Toggles AI rendering for this Actor Guid." );       
	}
	else
	{
		if ( !bInterrogate )
		{
			s_uRenderActorGuid = uGuid;
			s_bRenderActorAll = false;
		}

		Asura_CommandConsole::Printf( "Render Actor Guid: %u", s_uRenderActorGuid );
	}

	return true;
}

bool Axon_System::ConsoleCmd_RenderActorAll( bool bRender, bool bInterrogate, bool bHelp )
{
	if ( bHelp )
	{
		Asura_CommandConsole::Printf( CONSOLE_CMDNAME_RENDERACTORALL );
		Asura_CommandConsole::Printf( "Toggles AI rendering for all actors." );       
	}
	else
	{
		if ( !bInterrogate )
		{
			s_bRenderActorAll = bRender;
			s_uRenderActorGuid = ASURA_GUID_UNREGISTERED;
		}

		Asura_CommandConsole::Printf( "Render All Actors: %s", ((s_bRenderActorAll) ? "On" : "Off" ) );
	}

	return true;
}

bool Axon_System::ConsoleCmd_RenderGS2( bool bRender, bool bInterrogate, bool bHelp )
{
	if ( bHelp )
	{
		Asura_CommandConsole::Printf( CONSOLE_CMDNAME_RENDERGS2 );
		Asura_CommandConsole::Printf( "Toggles rendering of GS2 objects." );       
	}
	else
	{
		if ( !bInterrogate )
		{
			s_bRenderGS2 = bRender;
		}

		Asura_CommandConsole::Printf( "Render GS2 Objects: %s", ((s_bRenderGS2) ? "On" : "Off" ) );
	}

	return true;
}

bool Axon_System::ConsoleCmd_RenderNavWPs( bool bRender, bool bInterrogate, bool bHelp )
{
	if ( bHelp )
	{
		Asura_CommandConsole::Printf( CONSOLE_CMDNAME_RENDERNAVWPS );
		Asura_CommandConsole::Printf( "Toggles rendering of Nav Waypoints." );       
	}
	else
	{
		if ( !bInterrogate )
		{
			s_bRenderNavWPs = bRender;
		}

		Asura_CommandConsole::Printf( "Render Nav WPs: %s", ((s_bRenderNavWPs) ? "On" : "Off" ) );
	}

	return true;
}

bool Axon_System::ConsoleCmd_RenderNavLinksAll( bool bRender, bool bInterrogate, bool bHelp )
{
	if ( bHelp )
	{
		Asura_CommandConsole::Printf( CONSOLE_CMDNAME_RENDERNAVLINKSALL );
		Asura_CommandConsole::Printf( "Toggles rendering of all Nav Links." );       
	}
	else
	{
		if ( !bInterrogate )
		{
			s_bRenderNavLinksAll = bRender;
		}

		Asura_CommandConsole::Printf( "Render All Nav Links: %s", ((s_bRenderNavLinksAll) ? "On" : "Off" ) );
	}

	return true;
}

bool Axon_System::ConsoleCmd_RenderNavLinksBlocked( bool bRender, bool bInterrogate, bool bHelp )
{
	if ( bHelp )
	{
		Asura_CommandConsole::Printf( CONSOLE_CMDNAME_RENDERNAVLINKSBLOCKED );
		Asura_CommandConsole::Printf( "Toggles rendering of Blocked Nav Links." );       
	}
	else
	{
		if ( !bInterrogate )
		{
			s_bRenderNavLinksBlocked = bRender;
		}

		Asura_CommandConsole::Printf( "Render Blocked Nav Links: %s", ((s_bRenderNavLinksBlocked) ? "On" : "Off" ) );
	}

	return true;
}

bool Axon_System::ConsoleCmd_RenderNavCoverpoints( bool bRender, bool bInterrogate, bool bHelp )
{
	if ( bHelp )
	{
		Asura_CommandConsole::Printf( CONSOLE_CMDNAME_RENDERNAVCOVERPOINTS );
		Asura_CommandConsole::Printf( "Toggles rendering of Coverpoints." );       
	}
	else
	{
		if ( !bInterrogate )
		{
			s_bRenderNavCoverpoints = bRender;
		}

		Asura_CommandConsole::Printf( "Render Coverpoints: %s", ((s_bRenderNavCoverpoints) ? "On" : "Off" ) );
	}

	return true;
}

bool Axon_System::ConsoleCmd_RenderNavRange( float fRange, bool bInterrogate, bool bHelp )
{
	if ( bHelp )
	{
		Asura_CommandConsole::Printf( CONSOLE_CMDNAME_RENDERNAVRANGE );
		Asura_CommandConsole::Printf( "Sets the Range to render NAV data." );       
	}
	else
	{
		if ( !bInterrogate )
		{
			s_fRenderNavRangeSQ = fRange * fRange;
		}

		Asura_CommandConsole::Printf( "Nav data render range: %f", Asura_Maths::Sqrt( s_fRenderNavRangeSQ ) );
	}

	return true;
}

bool Axon_System::ConsoleCmd_UpdateAI( bool bUpdate, bool bInterrogate, bool bHelp )
{
	if ( bHelp )
	{
		Asura_CommandConsole::Printf( CONSOLE_CMDNAME_UPDATEAI );
		Asura_CommandConsole::Printf( "Toggles the AI updating." );       
	}
	else
	{
		if ( !bInterrogate )
		{
			s_bUpdateAI = bUpdate;
		}

		Asura_CommandConsole::Printf( "Update AI: %s", ((s_bUpdateAI) ? "On" : "Off" ) );
	}

	return true;
}

bool Axon_System::ConsoleCmd_RenderScheduler( bool bRender, bool bInterrogate, bool bHelp )
{
	if ( bHelp )
	{
		Asura_CommandConsole::Printf( CONSOLE_CMDNAME_RENDERSCHEDULER );
		Asura_CommandConsole::Printf( "Toggles the rendering of the scheduler." );       
	}
	else
	{
		if ( !bInterrogate )
		{
			s_bRenderScheduler = bRender;
		}

		Asura_CommandConsole::Printf( "Render Scheduler: %s", ((s_bRenderScheduler) ? "On" : "Off" ) );
	}

	return true;
}
#endif

void Axon_System::Initialise()
{
    Asura_Assert( ( s_pxAxonSystem == NULL ), "Axon_System::Initialise - system already initialised" );
    if ( !s_pxAxonSystem )
    {
        s_pxAxonSystem = Axon_System::Project_CreateAxonSystem();
    }

    Asura_Assert( s_pxAxonSystem, "Axon_System::Initialise - unable to initialise system" );
    if ( s_pxAxonSystem )
    {
        s_pxAxonSystem->InitialiseThis();
    }

#if !defined(ASURA_NO_DEBUG_OUTPUT) && defined(ASURA_SUPPORT_COMMAND_CONSOLE)
	Asura_CommandConsole::AddCmd( CONSOLE_CMDNAME_RENDERACTORGUID, Axon_System::ConsoleCmd_RenderActorGuid );
	Asura_CommandConsole::AddCmd( CONSOLE_CMDNAME_RENDERACTORALL, Axon_System::ConsoleCmd_RenderActorAll );
	Asura_CommandConsole::AddCmd( CONSOLE_CMDNAME_RENDERGS2, Axon_System::ConsoleCmd_RenderGS2 );
	Asura_CommandConsole::AddCmd( CONSOLE_CMDNAME_RENDERNAVWPS, Axon_System::ConsoleCmd_RenderNavWPs );
	Asura_CommandConsole::AddCmd( CONSOLE_CMDNAME_RENDERNAVLINKSALL, Axon_System::ConsoleCmd_RenderNavLinksAll );
	Asura_CommandConsole::AddCmd( CONSOLE_CMDNAME_RENDERNAVLINKSBLOCKED, Axon_System::ConsoleCmd_RenderNavLinksBlocked );
	Asura_CommandConsole::AddCmd( CONSOLE_CMDNAME_RENDERNAVCOVERPOINTS, Axon_System::ConsoleCmd_RenderNavCoverpoints );
	Asura_CommandConsole::AddCmd( CONSOLE_CMDNAME_RENDERNAVRANGE, Axon_System::ConsoleCmd_RenderNavRange );
	Asura_CommandConsole::AddCmd( CONSOLE_CMDNAME_UPDATEAI, Axon_System::ConsoleCmd_UpdateAI );
	Asura_CommandConsole::AddCmd( CONSOLE_CMDNAME_RENDERSCHEDULER, Axon_System::ConsoleCmd_RenderScheduler );

#undef CONSOLE_CMDNAME_RENDERACTORGUID
#undef CONSOLE_CMDNAME_RENDERACTORALL
#undef CONSOLE_CMDNAME_RENDERGS2
#undef CONSOLE_CMDNAME_RENDERNAVWPS
#undef CONSOLE_CMDNAME_RENDERNAVLINKSALL
#undef CONSOLE_CMDNAME_RENDERNAVLINKSBLOCKED
#undef CONSOLE_CMDNAME_RENDERNAVCOVERPOINTS
#undef CONSOLE_CMDNAME_RENDERNAVRANGE
#undef CONSOLE_CMDNAME_UPDATEAI
#undef CONSOLE_CMDNAME_RENDERSCHEDULER
#endif
}

void Axon_System::Update()
{
	ASURA_CPU_PROFILE__START_LABEL( ASURA_CPU_PROFILE__AXON_UPDATE, "Axon Update" );

    Asura_Assert( s_pxAxonSystem, "Axon_System::Update - system is not initialised" );
    if ( s_pxAxonSystem )
    {
        s_pxAxonSystem->UpdateThis();
    }

	ASURA_CPU_PROFILE__STOP( ASURA_CPU_PROFILE__AXON_UPDATE );
}

void Axon_System::Shutdown()
{
    Asura_Assert( s_pxAxonSystem, "Axon_System::Shutdown - system is not initialised" );
    if ( s_pxAxonSystem )
    {
        s_pxAxonSystem->ShutdownThis();
        delete s_pxAxonSystem;
    }
}

void Axon_System::Reset()
{
	Asura_Assert( s_pxAxonSystem, "Axon_System::Reset - system is not initialised" );
	if ( s_pxAxonSystem )
	{
		s_pxAxonSystem->ResetThis();
	}
}

void Axon_System::PostLoadInitialise()
{
    Asura_Assert( s_pxAxonSystem, "Axon_System::PostLoadInitialise - system is not initialised" );
    if ( s_pxAxonSystem )
    {
        s_pxAxonSystem->PostLoadInitialiseThis();
    }
}

void Axon_System::ClearSounds()
{
	GetSoundRegistry()->Clear();
}

//////////////////////////////////////////////////////////////////////////////
//
// 
//
//////////////////////////////////////////////////////////////////////////////

void Axon_System::InitialiseThis()
{
    Axon_Gamescene_AnimTemplate_System::Initialise();

    s_pxSoundRegistry = s_pxAxonSystem->CreateSoundRegistry();
    if ( s_pxSoundRegistry )
    {
        s_pxSoundRegistry->Initialise();
    }
}

void Axon_System::UpdateThis()
{
	// Go through all the brains and do the per-frame update.
	// Do this here so that we can guarantee the per-frame updates happen before the behaviour updates.
	const float fTimeslice = Asura_Timers::GetGameTimeSlice();
	bool bAllowUpdate = true;
#ifndef ASURA_NO_DEBUG_OUTPUT
	bAllowUpdate = Axon_System::GetUpdateAI();
#endif

	if ( bAllowUpdate )
	{

		for ( Asura_List_It< Axon_Brain > xBrainIter( &s_xBrainList ); !xBrainIter.Done(); xBrainIter.Next() )
		{
			Axon_Brain* pxBrain = xBrainIter.GetCurrent();
			if ( pxBrain->IsActive() )
			{
				pxBrain->DoPerFrameUpdate( fTimeslice );
#ifndef ASURA_NO_DEBUG_OUTPUT
				if ( GetRenderActorAll() || (GetRenderActorGuid() == pxBrain->GetOwnerGuid()) )
				{
					pxBrain->DoRenderDebugInfo();
				}
#endif
			}
		}

		s_xBehaviourUpdateScheduler.Update();
		s_xKnowledgeUpdateScheduler.Update();
#if !defined(ASURA_NO_DEBUG_OUTPUT) && defined(ASURA_SCHEDULER_DEBUG)
		DoRenderScheduler();
#endif
	}

    if ( s_pxSoundRegistry )
    {
        s_pxSoundRegistry->Update();
    }

#ifndef ASURA_NO_DEBUG_OUTPUT

	DoRenderNavWPs();
	DoRenderNavLinks();
	DoRenderNavLinksBlocked();
	DoRenderNavCoverPoints();
	DoRenderGS2();
		
#endif
}

void Axon_System::ShutdownThis()
{
    Asura_Assert( s_xBrainList.IsEmpty(), "Axon_System::ShutdownThis - brains still exist" );

    if ( s_pxSoundRegistry )
    {
        s_pxSoundRegistry->Shutdown();
    }
    DestroySoundRegistry( s_pxSoundRegistry );

    Axon_Gamescene_AnimTemplate_System::Shutdown();
}

void Axon_System::ResetThis()
{
	Asura_Assert( s_xBrainList.IsEmpty(), "Axon_System::ResetThis - brains still exist" );

	if ( s_pxSoundRegistry )
	{
		s_pxSoundRegistry->Clear();
	}
}

void Axon_System::PostLoadInitialiseThis()
{
}

Axon_Sound_Registry* Axon_System::CreateSoundRegistry()
{
    return new Axon_Sound_Registry();
}

void Axon_System::DestroySoundRegistry( Axon_Sound_Registry*& pxRegistry )
{
    delete pxRegistry;
    pxRegistry = NULL;
}

#ifndef ASURA_NO_DEBUG_OUTPUT
void Axon_System::DoRenderNavWPs()
{
	if ( Axon_System::GetRenderNavWPs() )
	{
		Asura_AI_Navigation_System::RenderWaypoints();
	}
}

void Axon_System::DoRenderNavLinks()
{
	if ( Axon_System::GetRenderNavLinksAll() )
	{
		Asura_AI_Navigation_System::RenderLinks();
	}
}

void Axon_System::DoRenderNavLinksBlocked()
{
	if ( Axon_System::GetRenderNavLinksBlocked() )
	{
		Asura_AI_Navigation_System::RenderBlockedLinks();
	}
}

void Axon_System::DoRenderNavCoverPoints()
{
	if ( Axon_System::GetRenderNavCoverpoints() )
	{
		Asura_AI_Navigation_System::RenderCoverpoints();
	}
}

void Axon_System::DoRenderGS2()
{
	if ( Axon_System::GetRenderGS2() )
	{
		Asura_ServerNode_GS2Node::DoRenderDebugInfo();
		Asura_ServerNode_GS2Spline::DoRenderDebugInfo();
	}
}

#ifdef ASURA_SCHEDULER_DEBUG
void Axon_System::DoRenderScheduler()
{
	if ( Axon_System::GetRenderScheduler() )
	{
		Asura_Vector_2 xTextVec( 10.0f, 10.0f );
		s_xBehaviourUpdateScheduler.RenderDebugInfo( xTextVec );
		xTextVec.y += 10.0f;
		s_xKnowledgeUpdateScheduler.RenderDebugInfo( xTextVec );
	}
}
#endif // ASURA_SCHEDULER_DEBUG 
#endif // ASURA_NO_DEBUG_OUTPUT

//////////////////////////////////////////////////////////////////////////////
//
// 
//
//////////////////////////////////////////////////////////////////////////////

#endif
