//
// Tools_Overlay_DVS.cpp
//
// Created by Jake Turner - 22/07/2007
// 
// Copyright (C) 2007 Rebellion, All Rights Reserved.
//

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

#if defined ASURA_DEBUG_VARIABLE_SYSTEM

// Asura
#include "Core/Asura_Maths.h"
#include "Core/Asura_StringBuffer.h"
#include "Interface/Asura_CommandConsole.h"
#include "Interface/Asura_Overlay.h"
#include "Interface/Asura_Overlay_System.h"

// Tools
#include "Tools_Overlays.h"
#include "Tools_System.h"

static const char* g_szNone = "*None*";//string for none option in tree

bool Tools_Overlay_DVS::s_bIsOpen  = false;
bool Tools_Overlay_DVS::s_bUpdated = false;
Asura_TreeDialog_Tree Tools_Overlay_DVS::s_xDVSVarsTree;
Asura_Tree_Dialog* Tools_Overlay_DVS::s_pxTreeDialog = NULL;
bool Tools_Overlay_DVS::s_bSelectionChanged = false;
Asura_TreeDialog_Item* Tools_Overlay_DVS::s_pxSelectedVariable = NULL;
float Tools_Overlay_DVS::s_fFloatIncrement = 0.01f;
u_int Tools_Overlay_DVS::s_uIntIncrement = 1;

Asura_UnicodeChar* Tools_Overlay_DVS::s_wszCommandOutputBuffer = 0;
u_int Tools_Overlay_DVS::s_uCommandOutputBufferLength = 0;

Asura_Vector_2 Tools_Overlay_DVS::s_xPosition( 6.0f, 6.0f );
Asura_Vector_2 Tools_Overlay_DVS::s_xUserSetTreeSize(0.0f,0.0f);

void Tools_Overlay_DVS::Initialise()
{
	// Load the data for the overlay.
	s_bUpdated = false;
	s_pxSelectedVariable = NULL;
}

//////////////
// Shutdown //
//////////////
void Tools_Overlay_DVS::Shutdown()
{
	// Close the overlay.
	if ( IsOpen() )
	{
		ToggleOverlay();
	}
}

////////////
// Update //
////////////
void Tools_Overlay_DVS::Update()
{
	// Make sure overlay is created on going into tools if needed.
	if ( !s_bUpdated )
	{
		s_bUpdated = true;
		CreateOverlay();
	}
	if(s_bIsOpen && !Asura_Overlay_System::DoesOverlayExist(TOOLS_OVERLAY_DVS))
	{
		//Overlay probably been closed using the X gadget thing. RAR 26/10/2007
		s_bIsOpen = false;
	}

	if ( !s_bIsOpen )
	{
		return;
	}

	if ( s_bSelectionChanged )
	{
		Asura_TreeDialog_Item* const pxSelected = s_xDVSVarsTree.GetSelectedItem();
		if ( pxSelected )
		{
			if ( pxSelected->GetNumChildren() == 0 )
			{
				s_pxSelectedVariable = pxSelected;
			}
			else
			{
				s_pxSelectedVariable = NULL;
			}
		}
		s_bSelectionChanged = false;
	}
	HandleKeyboard();
}

void Tools_Overlay_DVS::HandleKeyboard()
{
	if ( ( Asura_Overlay_System::OverlaysHaveKeyboardFocus() ) || ( Asura_UserInput::KeyPressed( ASURA_KEYID_ALT ) ) )
	{
		return;
	}

	// Ignore keyboard if a variable isn't selected
	if ( s_pxSelectedVariable == NULL )
	{
		return;
	}

	float fMultiplier = 1.0f;
	if ( Asura_UserInput::KeyPressed( ASURA_KEYID_CTRL ) )
	{
		fMultiplier = 10.0f;
	}
	if ( Asura_UserInput::KeyPressed( ASURA_KEYID_SHIFT ) )
	{
		fMultiplier = 0.1f;
	}

	// 'PAGE_DOWN' Decrement value
	// 'PAGE_UP' or return: Increment value
	if ( Asura_UserInput::DebouncedKeyPressed( ASURA_KEYID_PAGEUP ) || Asura_UserInput::DebouncedKeyPressed( ASURA_KEYID_ENTER ) )
	{
		ChangeValue( +fMultiplier );
	}
	if ( Asura_UserInput::DebouncedKeyPressed( ASURA_KEYID_PAGEDOWN ) )
	{
		ChangeValue( -fMultiplier );
	}

	// Note - change value updates the string text of the selected item in the tree dialog
}

///////////////////
// CreateOverlay //
///////////////////
void Tools_Overlay_DVS::CreateOverlay()
{
	if ( !IsOpen() ) 
	{
		return;
	}

	// Build the tree view of available DVS variables
	s_xDVSVarsTree.DeleteAll();

	for( const Asura_DebugVariableSystem::DVS_Item* pxItem = Asura_DebugVariableSystem::RootItem(); pxItem; pxItem = pxItem->m_pxNext )
	{
		const Asura_UnicodeChar* wszItem = pxItem->m_wszName;

		// Break the name apart into its parents
		const u_int uLevelOfVariable = Asura_StringUtil::CountInstancesOfCharacter( wszItem, L'.' );
		Asura_TreeDialog_Item* pxParent = NULL;
		for ( u_int uLevel = 0; uLevel <= uLevelOfVariable; uLevel++ )
		{
			u_int uVariableSubstringStart = 0;
			u_int uVariableSubstringLength = 0;
			if ( Asura_StringUtil::FindStringPartByIndex( wszItem, L'.', uLevel, uVariableSubstringStart, uVariableSubstringLength ) == true )
			{
				Asura_StringBuffer<1024> xName( "%S", wszItem+uVariableSubstringStart );
				xName.Truncate( uVariableSubstringLength );
				const Asura_Hash_ID xNameHash = xName.GetHash();

				Asura_StringBuffer<1024> xParentName( "%S", wszItem );
				if ( uVariableSubstringStart != 0 )
				{
					uVariableSubstringStart--;
				}
				xParentName.Truncate( uVariableSubstringStart );
				const Asura_Hash_ID xParentHash = xParentName.GetHash();

				// Make the string of parent + name
				Asura_StringBuffer<1024> xFullName( xParentName );
				if ( xParentName.GetLength() > 0 )
				{
					xFullName += ".";
				}
				xFullName += xName.GetString();
				const Asura_Hash_ID xFullNameHash = xFullName.GetHash();

				Asura_TreeDialog_Item* pxParentCheck = s_xDVSVarsTree.FindByData( xParentHash );
				Asura_Assert( pxParentCheck == pxParent, "" );

				Asura_TreeDialog_Item* pxNewParent = s_xDVSVarsTree.FindByData( xFullNameHash );
				// Only add to tree if we don't exist
				if ( pxNewParent == NULL )
				{
					if ( pxParent == NULL )
					{
						// xFullNameHash = xNameHash at this point because we don't have a parent
						Asura_Assert( xFullNameHash == xNameHash, "" );
						pxNewParent = s_xDVSVarsTree.AddItem( xName.GetString(), xFullNameHash );
					}
					else
					{
						Asura_UnicodeChar wszHelpBuffer[1024];
						wszHelpBuffer[0] = 0;
						// Is it the final variable entry
						if ( uLevel == uLevelOfVariable )
						{
							Asura_UnicodeChar wszValueBuffer[256];
							if (pxItem->m_pxVariable)
							{
								Asura_DebugVariableSystem::GetVariableValue(pxItem->m_pxVariable, wszValueBuffer, sizeof(wszValueBuffer));
							}
							else
							{
								wszValueBuffer[0] = 0;
								if (pxItem->m_pxCommand)
								{
									s_wszCommandOutputBuffer = wszHelpBuffer;
									s_uCommandOutputBufferLength =  sizeof(wszHelpBuffer) / sizeof( wszHelpBuffer[0] );

									Asura_UnicodeChar wszHelpCommand[128];
									Asura_PrintfToBfr(wszHelpCommand, L"? %s", 128, true, pxItem->m_pxCommand->GetName() );
									Asura_CommandConsole::Execute(wszHelpCommand, 0, CatchCommandOutputCallback);

									if (pxItem->m_pxCommand->GetType() == Asura_CommandConsole_CmdList::CMDTYPE_BOOL)
									{
										Asura_CommandConsole::Execute(pxItem->m_pxCommand->GetName(), 0, CatchCommandOutputCallback);
									}

									s_wszCommandOutputBuffer = 0;
									s_uCommandOutputBufferLength = 0;
								}
							}
							xName.CatPrint( "%S", wszValueBuffer );
						}
						// Add in using the combined parent & name hash
						pxNewParent = s_xDVSVarsTree.AddItem( pxParent, xName.GetString(), xFullNameHash );
						if (wszHelpBuffer[0])
						{
							char szHelpCharBuffer[1024];
							Asura_PrintfToBfr(szHelpCharBuffer, "%S", 1024, true, wszHelpBuffer);
							pxNewParent->SetHelpText(szHelpCharBuffer);
						}
					}
				}
				pxParent = pxNewParent;
			}
		}
	}

	Asura_Vector_2 xOffset( 1.0f, 3.0f );
	Asura_Dialog* pxNewDialog = NULL;

	Asura_Overlay* pxNewOverlay = new Asura_Overlay("DVS", false, &s_xPosition);
	pxNewOverlay->m_uID = TOOLS_OVERLAY_DVS;

	s_pxTreeDialog = new Asura_Tree_Dialog( xOffset, &s_xDVSVarsTree, 30.0f, 30.0f, &s_bSelectionChanged );
	s_pxTreeDialog->SetUsePerItemHelp(true);
	pxNewOverlay->AddDialog( s_pxTreeDialog );
	xOffset.y += 30.0f + 1.0f;

	//add control for resizing the tree
	pxNewDialog = new Asura_Resize_Dialog(s_pxTreeDialog, &s_xUserSetTreeSize);
	pxNewOverlay->AddDialog(pxNewDialog);


	pxNewDialog = new Asura_Function_Dialog("Close", xOffset, Close);
	pxNewOverlay->AddDialog( pxNewDialog );

	pxNewOverlay->MakeDialogsRelativeToResizableDialog();
	Asura_Overlay_System::SubmitNewOverlay( pxNewOverlay );
}

void Tools_Overlay_DVS::CatchCommandOutputCallback( const Asura_UnicodeChar* wszOutput )
{
	if (!s_wszCommandOutputBuffer)
	{
		return;
	}
	if (s_wszCommandOutputBuffer[0])
	{
		Asura_PrintfToBfr(s_wszCommandOutputBuffer + wcslen(s_wszCommandOutputBuffer), L"\n%s", s_uCommandOutputBufferLength - wcslen(s_wszCommandOutputBuffer), true, wszOutput);
	}
	else
	{
		Asura_PrintfToBfr(s_wszCommandOutputBuffer, L"%s", s_uCommandOutputBufferLength, true, wszOutput);
	}
}

void Tools_Overlay_DVS::RefreshOverlay(void)
{
	Asura_Overlay_System::CloseOverlays( TOOLS_OVERLAY_DVS );
	CreateOverlay();
}

///////////////////
// ToggleOverlay //
///////////////////

void Tools_Overlay_DVS::ToggleOverlay()
{
	s_bUpdated = true;
	s_bIsOpen = !s_bIsOpen;

	if ( IsOpen() )
	{
		CreateOverlay();
	}
	else
	{
		Close();
	}
}

void Tools_Overlay_DVS::Render(void)
{
	if ( s_bIsOpen )
	{
	}
}

void Tools_Overlay_DVS::Close()
{
	Asura_Overlay_System::CloseOverlays( TOOLS_OVERLAY_DVS );
	s_bIsOpen = false;
}

void Tools_Overlay_DVS::ChangeValue( const float fScale )
{
	Asura_Assert( s_pxSelectedVariable, "" );
	if ( s_pxSelectedVariable == NULL )
	{
		return;
	}
	Asura_DebugVariableSystem::DVS_Item* pxItem = Asura_DebugVariableSystem::FindItem(s_pxSelectedVariable->GetUserData());
	if (pxItem)
	{
		Asura_CommandConsole_VarRepository::Var* pxVar = pxItem->m_pxVariable;
		if( pxVar && !pxVar->m_bReadOnly )
		{
			bool bModified = false;
			switch( pxVar->m_eType )
			{		
				case Asura_CommandConsole_VarRepository::Var::TYPE_BOOL: 
				{			
					*pxVar->m_xData.pbData = !*pxVar->m_xData.pbData; 
					bModified = true;
					break;
				}
				case Asura_CommandConsole_VarRepository::Var::TYPE_FLOAT: 
				{		
					const float fValue = *pxVar->m_xData.pfData;
					*pxVar->m_xData.pfData += fScale * s_fFloatIncrement; 

					*pxVar->m_xData.pfData = Asura_Maths::ClampToRange( *pxVar->m_xData.pfData,
						pxVar->m_xData.fLowerLimit, pxVar->m_xData.fUpperLimit );

					bModified = ( fValue != *pxVar->m_xData.pfData );
					break;
				}
				case Asura_CommandConsole_VarRepository::Var::TYPE_INT: 
				{		
					const int iValue = *pxVar->m_xData.piData;
					*pxVar->m_xData.piData += static_cast<u_int>( fScale * s_uIntIncrement ); 

					*pxVar->m_xData.piData = Asura_Maths::ClampToRange( *pxVar->m_xData.piData,
						pxVar->m_xData.iLowerLimit, pxVar->m_xData.iUpperLimit );

					bModified = ( iValue != *pxVar->m_xData.piData );
					break;
				}
				case Asura_CommandConsole_VarRepository::Var::TYPE_STRING: 
				case Asura_CommandConsole_VarRepository::Var::TYPE_INVALID: 
				{		
					break;
				}
			}

			if ( bModified && pxVar->m_pbModified )
			{
				*pxVar->m_pbModified = true;
			}
		}
		else if (pxItem->m_pxCommand)
		{
			switch (pxItem->m_pxCommand->GetType())
			{
				case Asura_CommandConsole_CmdList::CMDTYPE_VOID:
				{
					Asura_CommandConsole_CmdList::Exec(pxItem->m_pxCommand->GetName(), L"", 0, 0);
					break;
				}
				case Asura_CommandConsole_CmdList::CMDTYPE_BOOL:
				{
					if (fScale > 0.0f)
					{
						Asura_CommandConsole_CmdList::Exec(pxItem->m_pxCommand->GetName(), L"1", 0, 0);
					}
					else
					{
						Asura_CommandConsole_CmdList::Exec(pxItem->m_pxCommand->GetName(), L"0", 0, 0);
					}
					break;
				}
				default:
				{
					break;
				}
			}
		}
	}
	Asura_StringBuffer<1024> xName( s_pxSelectedVariable->GetName() );
	Asura_UnicodeChar wszValueBuffer[256];
	if (pxItem->m_pxVariable)
	{
		Asura_DebugVariableSystem::GetVariableValue(pxItem->m_pxVariable, wszValueBuffer, sizeof(wszValueBuffer));
	}
	else
	{
		wszValueBuffer[0] = 0;
		if (pxItem->m_pxCommand)
		{
			Asura_UnicodeChar wszHelpBuffer[1024];
			wszHelpBuffer[0] = 0;
			s_wszCommandOutputBuffer = wszHelpBuffer;
			s_uCommandOutputBufferLength =  sizeof(wszHelpBuffer) / sizeof( wszHelpBuffer[0] );

			Asura_UnicodeChar wszHelpCommand[128];
			Asura_PrintfToBfr(wszHelpCommand, L"? %s", 128, true, pxItem->m_pxCommand->GetName() );
			Asura_CommandConsole::Execute(wszHelpCommand, 0, CatchCommandOutputCallback);

			if (pxItem->m_pxCommand->GetType() == Asura_CommandConsole_CmdList::CMDTYPE_BOOL)
			{
				Asura_CommandConsole::Execute(pxItem->m_pxCommand->GetName(), 0, CatchCommandOutputCallback);
			}

			s_wszCommandOutputBuffer = 0;
			s_uCommandOutputBufferLength = 0;
			char szHelpCharBuffer[1024];
			Asura_PrintfToBfr(szHelpCharBuffer, "%S", 1024, true, wszHelpBuffer);
			s_pxSelectedVariable->SetHelpText(szHelpCharBuffer);
		}
	}
	// Find the string ' :'
	int iPos = 0;
	if ( xName.Contains( " : ", &iPos ) == true )
	{
		xName[iPos] = '\0';
		xName.CatPrint( "%S", wszValueBuffer );
		s_pxSelectedVariable->SetName( xName.GetString() );
	}
}

#endif //#if defined ASURA_DEBUG_VARIABLE_SYSTEM

