//
// Asura_EntityWatch_System.cpp
//
// Created by Richard Rice - 28/04/2005
// 
// Copyright (C) 2005 Rebellion, All Rights Reserved.
//

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

#ifdef ASURA_SUPPORT_ENTITYWATCH

#include "Core/Asura_String.h"
#include "Debugging/Asura_DebugText.h"
#include "Entity/Asura_Entity.h"
#include "Interface/Asura_CommandConsole.h"
#include "Interface/Asura_CommandConsole_IO.h"

#ifdef ASURA_TOOLS
#include "Entity/Tools_Entity_Data.h"
#endif //ASURA_TOOLS


bool Asura_EntityWatch_System::s_bEnabled = true;
bool Asura_EntityWatch_System::s_bWatchMessages = false;
u_int Asura_EntityWatch_System::s_uNumberWatched = 0;
Asura_Guid Asura_EntityWatch_System::s_auWatchedEntity[ASURA_ENTITYWATCH_MAXENTITIES];

u_int Asura_EntityWatch_System::s_uTopLeftPrintX = 1;
u_int Asura_EntityWatch_System::s_uTopLeftPrintY = 1;

u_int Asura_EntityWatch_System::s_uCurPrintY = 0;
u_int Asura_EntityWatch_System::s_uXOffset = 0;

//width and height of 'box' in which text can appear.
u_int Asura_EntityWatch_System::s_uPrintWidth = 80;
u_int Asura_EntityWatch_System::s_uPrintHeight = 80;

char Asura_EntityWatch_System::s_szText[ASURA_ENTITYWATCH_MAXWIDTH+1];

static const int g_uIndent = 1;//indentation for debug text relative to guid/name


#define COMMAND_ENTITYWATCHON		L"EntityWatchOn"
#define COMMAND_ENTITYWATCHOFF		L"EntityWatchOff"
#define COMMAND_SHOWENTITYWATCH		L"ShowEntityWatch"
#define COMMAND_ENTITYWATCHSETBOX   L"EntityWatchSetBox"
#define COMMAND_ENTITYWATCHSHOWMESSAGES L"EntityWatchShowMessages"



void Asura_EntityWatch_System::Update()
{
	if(!s_bEnabled || !s_uNumberWatched) return;

	s_uCurPrintY = s_uTopLeftPrintY;
	s_uXOffset = 0;

	//render text for each entity in turn
	for(u_int i=0;i<s_uNumberWatched;i++)
	{
		Asura_Entity_Server* pxEntity = Asura_Entity_Server::FindEntity(s_auWatchedEntity[i]);
		if(pxEntity)
		{
			char szText[100];

			u_int uWidth = s_uPrintWidth;
			if(uWidth>100) uWidth = 100;

			//display the entities name/guid
			#ifdef ASURA_TOOLS
			if(pxEntity->ToolsData)
			{
				Asura_PrintfToBfr(szText,"Entity %u (%s)",uWidth,true,pxEntity->GetGuid(),pxEntity->ToolsData->Name.GetString());
			}
			else
			#endif
			{
				Asura_PrintfToBfr(szText,"Entity %u",uWidth,true,pxEntity->GetGuid());
			}


			if(s_uCurPrintY-s_uTopLeftPrintY<s_uPrintHeight)
			{
#if defined( ASURA_USE_DEBUG_TEXT)
				//only print if it won't overwrite the console
				if((float)s_uCurPrintY*ASURA_DEBUGTEXT_OUTPUT_FONT_HEIGHT >= Asura_CommandConsole::GetBottomY())
				{
					Asura_DebugTextPrint((s_uTopLeftPrintX,s_uCurPrintY,0xff00ff00,0,"%s",szText));
				}
#endif
			}

			
			//Render entity specific stuff
			NextLine();
			pxEntity->DisplayEntityWatchText();

			//move print position down below text just displayed
			//(and print any text queued up)
			if(s_uXOffset)
			{
				NextLine();
			}

		}
		else
		{
			//the watched entity no longer exists , so remove it from the list
			for(u_int j=i;j<s_uNumberWatched-1;j++)
			{
				s_auWatchedEntity[j] = s_auWatchedEntity[j+1];
			}
			s_uNumberWatched--;
			i--;

		}
	}
}

void Asura_EntityWatch_System::PrintText(u_int uFieldWidth,const char* pszString,...)
{
	char szText[ASURA_ENTITYWATCH_MAXWIDTH+1];

	bool bSizeFieldToString = false;
	if(uFieldWidth==0)
	{
		//Passed width us zero , so size it to string. Use maximum for now
		//until after the sprintf
		bSizeFieldToString = true;
		uFieldWidth = ASURA_ENTITYWATCH_MAXWIDTH;
	}
	else
	{
		if(uFieldWidth > s_uPrintWidth - g_uIndent)
		{
			uFieldWidth = s_uPrintWidth - g_uIndent;
		}
		if(uFieldWidth>ASURA_ENTITYWATCH_MAXWIDTH) uFieldWidth = ASURA_ENTITYWATCH_MAXWIDTH;
	}

	va_list ap;
	va_start(ap, pszString);
	Asura_PrintfToBfr(szText, pszString, ap, uFieldWidth+1, true);
	va_end(ap);

	if(bSizeFieldToString)
	{
		uFieldWidth = strlen(szText);
	}

	//space left on this line?
	if(s_uXOffset+uFieldWidth > s_uPrintWidth)
	{
		NextLine();
	}

	Asura_Assert(s_uXOffset+uFieldWidth <= ASURA_ENTITYWATCH_MAXWIDTH,"Should always be enough space");

	strncpy(&s_szText[s_uXOffset],szText,uFieldWidth);

	//pad out rest of string with spaces
	//(the last space will be replaced by a terminator when it gets printed).
	for(u_int i=strlen(szText);i<=uFieldWidth;i++)
	{
		s_szText[s_uXOffset+i] = ' ';
	}


	//keep track of next place to show text
	s_uXOffset += uFieldWidth+1;
}

void Asura_EntityWatch_System::NextLine()
{
	//print text for previous line , if any
	if(s_uXOffset)
	{
		if(s_uCurPrintY-s_uTopLeftPrintY<s_uPrintHeight)
		{
			s_szText[s_uXOffset] = 0;
#if defined( ASURA_USE_DEBUG_TEXT)
			//only print if it won't overwrite the console
			if((float)s_uCurPrintY*ASURA_DEBUGTEXT_OUTPUT_FONT_HEIGHT >= Asura_CommandConsole::GetBottomY())
			{
				Asura_DebugTextPrint((s_uTopLeftPrintX+g_uIndent,s_uCurPrintY,0xffffffff,0,"%s",s_szText));
			}
#endif
		}
	}
	//and move to next
	s_uXOffset = 0;
	s_uCurPrintY++;
}



void Asura_EntityWatch_System::AddConsoleCommands()
{
	Asura_CommandConsole::AddCmd( COMMAND_ENTITYWATCHON, ConsoleCmd_EntityWatchOn,Asura_CommandConsole::REMOVE_AT_SHUTDOWN);
	Asura_CommandConsole::AddCmd( COMMAND_ENTITYWATCHOFF, ConsoleCmd_EntityWatchOff,Asura_CommandConsole::REMOVE_AT_SHUTDOWN);
	Asura_CommandConsole::AddCmd( COMMAND_SHOWENTITYWATCH, ConsoleCmd_EnableEntityWatch,Asura_CommandConsole::REMOVE_AT_SHUTDOWN);
	Asura_CommandConsole::AddCmd( COMMAND_ENTITYWATCHSETBOX, ConsoleCmd_EntityWatchSetBox,Asura_CommandConsole::REMOVE_AT_SHUTDOWN);
	Asura_CommandConsole::AddCmd( COMMAND_ENTITYWATCHSHOWMESSAGES, ConsoleCmd_ShowMessages,Asura_CommandConsole::REMOVE_AT_SHUTDOWN);
}


bool Asura_EntityWatch_System::ConsoleCmd_EntityWatchOn(const char* pszText,bool bInterrogate, bool bHelp)
{
	//Start watching an entity
	if(bHelp || bInterrogate)
	{
        Asura_CommandConsole::Printf( L"Enables watching of various variables for the specified entity" );
		#ifdef ASURA_TOOLS
        Asura_CommandConsole::Printf( L"Syntax: " COMMAND_ENTITYWATCHON L" <guid or name>" );
		#else
        Asura_CommandConsole::Printf( L"Syntax: " COMMAND_ENTITYWATCHON L" <guid>" );
		#endif
		return true;
	}
	
	Asura_Entity_Server* pxEntity = FindEntity(pszText);

	if(!pxEntity)
	{
        Asura_CommandConsole::Printf( L"Failed to find entity." );
		return false;
	}

	AddEntity(pxEntity);

	return true;
}

bool Asura_EntityWatch_System::ConsoleCmd_EntityWatchOff(const char* pszText,bool bInterrogate, bool bHelp)
{
	//Stop watching an entity
	if(bHelp || bInterrogate)
	{
        Asura_CommandConsole::Printf( L"Disables watching of the specified entity" );
		#ifdef ASURA_TOOLS
        Asura_CommandConsole::Printf( L"Syntax: " COMMAND_ENTITYWATCHOFF L" <guid or name>" );
		#else
        Asura_CommandConsole::Printf( L"Syntax: " COMMAND_ENTITYWATCHOFF L" <guid>" );
		#endif
		return true;

	}

	Asura_Entity_Server* pxEntity = FindEntity(pszText);

	if(!pxEntity)
	{
        Asura_CommandConsole::Printf( L"Failed to find entity." );
		return false;
	}

	RemoveEntity(pxEntity);

	return true;
}

bool Asura_EntityWatch_System::ConsoleCmd_EnableEntityWatch(bool bEnable,bool bInterrogate, bool bHelp)
{
	//Enable/Disable displaying of the text
	if(bHelp)
	{
        Asura_CommandConsole::Printf( L"Sets whether entity watch text should be displayed" );
        Asura_CommandConsole::Printf( L"Syntax: " COMMAND_SHOWENTITYWATCH L" <true | false>" );
	}
	else
	{
		if(!bInterrogate)
		{
			s_bEnabled = bEnable;
		}
        Asura_CommandConsole::Printf( L"Entity watch: %ls.", s_bEnabled ? L"enabled" : L"disabled" );
	}
	return true;
}

bool Asura_EntityWatch_System::ConsoleCmd_ShowMessages(bool bEnable,bool bInterrogate, bool bHelp)
{
	//Enable/Disable displaying of the text
	if(bHelp)
	{
        Asura_CommandConsole::Printf( L"Causes a log of messages sent to and from the watched entities to be sent to the console." );
        Asura_CommandConsole::Printf( L"Syntax: " COMMAND_ENTITYWATCHSHOWMESSAGES L" <true | false>" );
	}
	else
	{
		if(!bInterrogate)
		{
			s_bWatchMessages = bEnable;
		}
        Asura_CommandConsole::Printf( L"Entity watch messages: %ls.", s_bWatchMessages ? L"enabled" : L"disabled" );
	}
	return true;
}


bool Asura_EntityWatch_System::ConsoleCmd_EntityWatchSetBox(const char* pszText,bool bInterrogate, bool bHelp)
{
	if(bHelp)
	{
        Asura_CommandConsole::Printf( L"Sets the area in which entity watch text can be displayed , measured in characters." );
        Asura_CommandConsole::Printf( L"Syntax: " COMMAND_ENTITYWATCHSETBOX L" <TopLeftX> <TopLeftY> <Width> <Height>" );
        Asura_CommandConsole::Printf( L"Width and height are optional." );
	}
	else
	{
		if(!bInterrogate)
		{
			//read the numbers from the string
			u_int uX,uY,uWidth,uHeight;
			int iRead = sscanf(pszText,"%d %d %d %d",&uX,&uY,&uWidth,&uHeight);
			if( iRead >= 2)
			{
				s_uTopLeftPrintX = uX;
				s_uTopLeftPrintY = uY;
				if(iRead>=3)s_uPrintWidth = uWidth;
				if(iRead>=4)s_uPrintHeight = uHeight;
			}
			else
			{
		        Asura_CommandConsole::Printf( L"Failed to get all required paramaters from input." );
				return false;
			}
		}
		Asura_CommandConsole::Printf(L"Position : %dX%d  Size : %dX%d.",s_uTopLeftPrintX,s_uTopLeftPrintY,s_uPrintWidth,s_uPrintHeight);
	}
	return true;
}

Asura_Entity_Server* Asura_EntityWatch_System::FindEntity(const char* pszText)
{
	//get a guid from the text and search for entity that way

	int iGuid;
	if(Asura_StringUtil::GetIntValue(pszText,&iGuid))
	{
		Asura_Entity_Server* pxEntity = Asura_Entity_Server::FindEntity((Asura_Guid) iGuid);
		return pxEntity;			
	}

	#ifdef ASURA_TOOLS
	Asura_Entity_Server* pxPartialMatchEntity = 0;

	//Search for entity by name
	for(Asura_Classified_List_It<Asura_Entity_Server> xIt(Asura_Entity_Server::GetList());!xIt.Done();xIt.Next())
	{
		Asura_Entity_Server* pxEntity = xIt.GetCurrent();
		//ignore entities marked for destruction
		if(pxEntity->IsDestroyed()) continue;

		//got tools data?
		if(!pxEntity->ToolsData) continue;

		//check the name
		if(!stricmp(pxEntity->ToolsData->Name.GetString(),pszText))
		{
			return pxEntity;
		}
		else if(!strnicmp(pxEntity->ToolsData->Name.GetString(),pszText,strlen(pszText)))
		{
			//text matches the start of an entity name.
			//store it in case there is no full match
			pxPartialMatchEntity = pxEntity;
		}
	}
	if(pxPartialMatchEntity) return pxPartialMatchEntity;
	#endif //ASURA_TOOLS

	return 0;
}

void Asura_EntityWatch_System::AddEntity(const class Asura_Entity_Server* pxEntity)
{
	Asura_Guid uGuid = pxEntity->GetGuid();

	//make sure it isn't already in list
	for(u_int i=0;i<s_uNumberWatched;i++)
	{
		if(s_auWatchedEntity[i] == uGuid)
		{
			Asura_CommandConsole::Printf( L"Already watching that entity." );
			return;
		}
	}

	if(s_uNumberWatched == ASURA_ENTITYWATCH_MAXENTITIES)
	{
		Asura_CommandConsole::Printf( L"Watching too many entities already." );
		return;
	}

	//Indicate that command has worked
	#ifdef ASURA_TOOLS
	if(pxEntity->ToolsData)
	{
		Asura_CommandConsole::Printf( L"Watching entity %u (%S)",uGuid, pxEntity->ToolsData->Name.GetString());
	}
	else
	#endif //ASURA_TOOLS
	{
		Asura_CommandConsole::Printf( L"Watching entity %u",uGuid);
	}

	//and add to the array
	s_auWatchedEntity[s_uNumberWatched++] = uGuid;
		
}

void Asura_EntityWatch_System::RemoveEntity(const class Asura_Entity_Server* pxEntity)
{
	Asura_Guid uGuid = pxEntity->GetGuid();

	//remove from array of watched entities
	for(u_int i=0;i<s_uNumberWatched;i++)
	{
		if(s_auWatchedEntity[i] == uGuid)
		{
			//Indicate that command has worked
			#ifdef ASURA_TOOLS
			if(pxEntity->ToolsData)
			{
				Asura_CommandConsole::Printf( L"No longer watching entity %u (%S)",uGuid, pxEntity->ToolsData->Name.GetString());
			}
			else
			#endif //ASURA_TOOLS
			{
				Asura_CommandConsole::Printf( L"No longer watching entity %u",uGuid);
			}

			
			//shuffle the rest down
			s_uNumberWatched--;
			for(;i<s_uNumberWatched;i++)
			{
				s_auWatchedEntity[i] = s_auWatchedEntity[i+1];
			}
			return;
		}
	}

	//wasn't watching this one
	Asura_CommandConsole::Printf( L"Failed to find entity in list being watched." );

}

bool Asura_EntityWatch_System::ShouldDisplayMessageFor(Asura_Guid uGuid)
{
	if(!s_bEnabled || !s_bWatchMessages) return false;
	for(u_int i=0;i<s_uNumberWatched;i++)
	{
		if(s_auWatchedEntity[i] == uGuid)
		{
			return true;
		}
	}
	return false;
}

#endif //ASURA_SUPPORT_ENTITYWATCH

/*
EntityWatchOn <name>
EntityWatchOff <name>
EnableEntityWatch <on/off>
*/
