//////////////////////////////////////////////////////////////////////////////
//
// Tools_Entity_Objective.cpp
//
// Copyright (c) 2007 Rebellion. All rights reserved.
//
// Description:
//
//     Base Asura Objective Tools Object
//
//////////////////////////////////////////////////////////////////////////////

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

#include "Core/Asura.h"

// This file's header.
#include "Entity/Tools_Entity_Objective.h"

#include "Core/Asura_HashedLocalisedText.h"
#include "Entity/Asura_Entity_Event.h"
#include "Entity/Objective/Asura_ServerEntity_Objective.h"
#include "Interface/Asura_Dialog.h"
#include "Interface/Asura_Overlay.h"
#include "Network/Asura_Message.h"
#include "Rendering/Asura_Text_Output.h"

#include "Load&Save/Tools_SaveAsura.h"
#include "Objects/Tools_Object_System.h"

Tools_Entity_Objective::Tools_Entity_Objective(Asura_Entity* pxEntity)
:PARENT(pxEntity)
, m_iTextIndex(0)
, m_pszTextList(0)
, m_iTextListLength(0)
{
	Type = TOT_Entity_Objective;
	SetMesh("ToolsShape_Objective");

	m_pxObjective = static_cast<Asura_ServerEntity_Objective*>(pxEntity);

	//set up the text list for the first time
	GenerateTextList();
}

void Tools_Entity_Objective::GenerateTextList()
{
	//text list setup code
	//in it's own function so it can be redone on changes

	// clear out old list
	delete [] m_pszTextList;
	m_pszTextList = 0;


	//set up text list. page is based on hash from entity, so project overrides should "just work"
	//if page isn't found, this will silently cope
	m_iTextIndex = 0;
	m_iTextListLength = 1;
	const Asura_HashedLocalisedText_Page* pxPage = Asura_HashedLocalisedText::GetPage(m_pxObjective->GetObjectiveLocalisedPageID());
	if (pxPage)
	{
		for (int i = 0; i < pxPage->GetNumberOfStrings(); ++i)
		{
			if (m_pxObjective->GetTextHash() == Asura_GetHashID(pxPage->GetTextIDStringByIndex(i)))
			{
				m_iTextIndex = i + 1;
			}
		}
		m_iTextListLength += pxPage->GetNumberOfStrings();
	}
	//populate list, starting with ASURA_HASH_ID_UNSET mapping
	m_pszTextList = new const char*[m_iTextListLength];
	m_pszTextList[0] = "<unset>";
	for (int i = 1; i < m_iTextListLength; ++i)
	{
		if (pxPage)
		{
			m_pszTextList[i] = pxPage->GetTextIDStringByIndex(i - 1);
		}
	}
}

Tools_Entity_Objective::~Tools_Entity_Objective()
{
	delete [] m_pszTextList;
}

Tools_Object* Tools_Entity_Objective::Create( Asura_Entity* pxEntity )
{
	return new Tools_Entity_Objective( pxEntity );
}

void Tools_Entity_Objective::Create()
{
	// Get a new GUID
	Asura_Guid uGuid = Asura_Entity_System::GetCreatedByToolsGuid();
	
	// Set up tools data and auto-create a unique name (prompt for new name if there is a clash).
	Tools_Entity_Data* pxToolsData = CreateToolsDataWithDefaultName("Objective", "Objective", uGuid, GetNamePrefix());
	if (!pxToolsData) return;

	// Create the server object and set its data.
	Asura_ServerEntity_Objective* pxServerObject = new Asura_ServerEntity_Objective(uGuid);
	Asura_Assert(pxServerObject,"");
	if (pxServerObject)
	{
		pxServerObject->ToolsData = pxToolsData;
	}

	// Create the tools object and select it.
	Tools_Object* pxToolsObject = Create(pxServerObject);
	Tools_Object_System::SelectObject(pxToolsObject);
}

void Tools_Entity_Objective::AddOverlayControls(class Asura_Overlay* pxOverlay,class Asura_Vector_2& xOffset)
{
	PARENT::AddOverlayControls(pxOverlay, xOffset);

	Asura_Dialog* pxNewDialog;

	// checkboxes for states (these implicitly work as radio buttons, as they directly change the underlying state)
	// pending is spaced seperately, as it the only state that goes before "ready"
	//no checkbox for ready itself - all tickboxes off equal default state, IE ready.
	xOffset.y += Asura_Dialog::SmallSpacing();
	pxNewDialog = new Asura_Divider_Dialog( xOffset );
	pxOverlay->AddDialog( pxNewDialog );
	xOffset.y += Asura_Dialog::SmallSpacing();

	AddSharedParamDialogToOverlay( pxOverlay, xOffset, g_uHash_Objective_Pending );
	xOffset.y += Asura_Dialog::LargeSpacing();

	AddSharedParamDialogToOverlay( pxOverlay, xOffset, g_uHash_Objective_Complete );
	xOffset.y += Asura_Dialog::SmallSpacing();

	AddSharedParamDialogToOverlay( pxOverlay, xOffset, g_uHash_Objective_Failed );
	xOffset.y += Asura_Dialog::SmallSpacing();

	AddSharedParamDialogToOverlay( pxOverlay, xOffset, g_uHash_Objective_Skipped);
	xOffset.y += Asura_Dialog::DefaultSpacing();

	xOffset.y += Asura_Dialog::SmallSpacing();
	pxNewDialog = new Asura_Divider_Dialog( xOffset );
	pxOverlay->AddDialog( pxNewDialog );
	xOffset.y += Asura_Dialog::SmallSpacing();

	// NY - 13 December 2007 - objective number and name aren't "blue boxed", as you won't normally want multiple objectives to have them the same
	pxNewDialog = new Asura_Numerical_Dialog("Objective Number", xOffset, &m_pxObjective->m_uObjectiveNumber, 0, 999, 1, "%i", &Changed);
	pxNewDialog->SetHelpText("Number of Objective, for ordering and optional rendering");
	pxOverlay->AddDialog(pxNewDialog);
	xOffset.y += Asura_Dialog::DefaultSpacing();

	xOffset.y += Asura_Dialog::SmallSpacing();
	pxNewDialog = new Asura_Divider_Dialog( xOffset );
	pxOverlay->AddDialog( pxNewDialog );
	xOffset.y += Asura_Dialog::SmallSpacing();

	pxNewDialog = new Asura_StringDisplay_Dialog("Objective Text:", xOffset);
	pxOverlay->AddDialog(pxNewDialog);
	xOffset.y += Asura_Dialog::DefaultSpacing();

	pxNewDialog = new Asura_List_Dialog(xOffset, m_pszTextList, &m_iTextIndex, 10, m_iTextListLength, 0, &Changed);
	pxNewDialog->SetHelpText("Objective text to display");
	pxOverlay->AddDialog(pxNewDialog);
	xOffset.y += Asura_Dialog::DefaultSpacing();
}

bool Tools_Entity_Objective::GetParameter(Asura_Hash_ID uParamID, class Tools_Parameter* pxParam) const
{
	// checkboxes are ticked if objective is in that state
	switch(uParamID)
	{
		case g_uHash_Objective_Pending:
		{
			pxParam->SetBool(m_pxObjective->GetObjectiveState() == ASURA_OBJECTIVE_PENDING);
			break;
		}
		case g_uHash_Objective_Complete:
		{
			pxParam->SetBool(m_pxObjective->GetObjectiveState() == ASURA_OBJECTIVE_COMPLETE);
			break;
		}
		case g_uHash_Objective_Failed:
		{
			pxParam->SetBool(m_pxObjective->GetObjectiveState() == ASURA_OBJECTIVE_FAILED);
			break;
		}
		case g_uHash_Objective_Skipped:
		{
			pxParam->SetBool(m_pxObjective->GetObjectiveState() == ASURA_OBJECTIVE_SKIPPED);
			break;
		}
		default :
		{
			return PARENT::GetParameter(uParamID, pxParam);
		}
	}
	return true;
}

bool Tools_Entity_Objective::SetParameter(Asura_Hash_ID uParamID,const class Tools_Parameter* pxParam)
{
	//map the checkbox changes onto state changes
	switch(uParamID)
	{
		case g_uHash_Objective_Pending:
		{
			if(pxParam->GetBool())
			{
				m_pxObjective->SetObjectiveState(ASURA_OBJECTIVE_PENDING);
			}
			else
			{
				m_pxObjective->SetObjectiveState(ASURA_OBJECTIVE_READY);
			}
			break;
		}
		case g_uHash_Objective_Complete:
		{
			if(pxParam->GetBool())
			{
				m_pxObjective->SetObjectiveState(ASURA_OBJECTIVE_COMPLETE);
			}
			else
			{
				m_pxObjective->SetObjectiveState(ASURA_OBJECTIVE_READY);
			}
			break;
		}
		case g_uHash_Objective_Failed:
		{
			if(pxParam->GetBool())
			{
				m_pxObjective->SetObjectiveState(ASURA_OBJECTIVE_FAILED);
			}
			else
			{
				m_pxObjective->SetObjectiveState(ASURA_OBJECTIVE_READY);
			}
			break;
		}
		case g_uHash_Objective_Skipped:
		{
			if(pxParam->GetBool())
			{
				m_pxObjective->SetObjectiveState(ASURA_OBJECTIVE_SKIPPED);
			}
			else
			{
				m_pxObjective->SetObjectiveState(ASURA_OBJECTIVE_READY);
			}
			break;
		}
		default:
		{
			return PARENT::SetParameter(uParamID, pxParam);
		}
	}

	SetEntityModified();

	return true;
}

bool Tools_Entity_Objective::AddSharedParamDialogToOverlay( Asura_Overlay* pxOverlay, const Asura_Vector_2& xOffset, Asura_Hash_ID uParamID )
{
	Asura_Dialog* pxDialog = NULL;

	switch( uParamID )
	{
		case g_uHash_Objective_Pending:
		{
			pxDialog = new Asura_SharedTickbox_Dialog( "Pending", xOffset, &g_xParam_Objective_Pending );
			pxDialog->SetHelpText("Objective is not yet ready for the player to complete; however, unlike an inactive objective it will still be rendered.");
			break;
		}
		case g_uHash_Objective_Complete:
		{
			pxDialog = new Asura_SharedTickbox_Dialog( "Complete", xOffset, &g_xParam_Objective_Complete );
			pxDialog->SetHelpText("Objective is already completed.");
			break;
		}
		case g_uHash_Objective_Failed:
		{
			pxDialog = new Asura_SharedTickbox_Dialog( "Failed", xOffset, &g_xParam_Objective_Failed );
			pxDialog->SetHelpText("Objective is already failed.");
			break;
		}
		case g_uHash_Objective_Skipped:
		{
			pxDialog = new Asura_SharedTickbox_Dialog( "Skipped", xOffset, &g_xParam_Objective_Skipped );
			pxDialog->SetHelpText("Objective is no longer available for the player to complete; however, unlike an inactive objective it will still be rendered");
			break;
		}
		default:
		{
			break;
		}
	}

	if( pxDialog )
	{
		pxOverlay->AddDialog( pxDialog );
        return true;
	}
    else
    {
		return PARENT::AddSharedParamDialogToOverlay(pxOverlay, xOffset, uParamID);
    }
}



bool Tools_Entity_Objective::HandlesThisMessage(int iMessageID)
{
	switch (iMessageID)
	{
		case ASURA_MSG_OBJECTIVE_READY:
		case ASURA_MSG_OBJECTIVE_COMPLETE:
		case ASURA_MSG_OBJECTIVE_FAIL:
		case ASURA_MSG_OBJECTIVE_SKIP:
		{
			//handle all the objective messages
			return true;
		}
		default:
		{
			break;
		}
	}
	return PARENT::HandlesThisMessage(iMessageID);
}

bool Tools_Entity_Objective::HandlesThisEvent(int iEventID) const
{
	switch(iEventID)
	{
		case ASURA_ENTITY_EVENT_ONCOMPLETED:
		case ASURA_ENTITY_EVENT_ONFAILED:
		{
			//events for objective completion and failure
			return true;
		}
		default:
		{
			break;
		}
	}
	return PARENT::HandlesThisEvent(iEventID);
}

void Tools_Entity_Objective::UpdateWhileSelected()
{
	if(Changed)
	{
		m_pxObjective->SetTextHash(ASURA_HASH_ID_UNSET);
		if (m_iTextIndex > 0)
		{
			m_pxObjective->SetTextHash(Asura_GetHashID(m_pszTextList[m_iTextIndex]));
		}
		GenerateTextList();
		CreateOverlay();
	}
	PARENT::UpdateWhileSelected();
}

void Tools_Entity_Objective::PrintInfo(float& fX,float& fY)
{
	PARENT::PrintInfo(fX, fY);
	
	Asura_Text_Output::Print(fX, fY++,0xffffffff,0,"Text ID : %s", m_pszTextList[m_iTextIndex]);
	if (m_iTextIndex > 0)
	{
		const Asura_HashedLocalisedText_Page* pxPage = Asura_HashedLocalisedText::GetPage(m_pxObjective->GetObjectiveLocalisedPageID());
		if (pxPage)
		{
			Asura_Text_Output::Print(fX, fY++,0xffffffff,0,"Text : %S", pxPage->GetStringByIndex(m_iTextIndex - 1));
		}
	}
	switch (m_pxObjective->GetObjectiveState())
	{
		case ASURA_OBJECTIVE_PENDING:
		{
			Asura_Text_Output::Print(fX, fY++,0xffafafaf,0,"State : Pending");
			break;
		}
		case ASURA_OBJECTIVE_READY:
		{
			Asura_Text_Output::Print(fX, fY++,0xffffffff,0,"State : Ready");
			break;
		}
		case ASURA_OBJECTIVE_COMPLETE:
		{
			Asura_Text_Output::Print(fX, fY++,0xff00ff00,0,"State : Complete");
			break;
		}
		case ASURA_OBJECTIVE_FAILED:
		{
			Asura_Text_Output::Print(fX, fY++,0xffff0000,0,"State : Failed");
			break;
		}
		case ASURA_OBJECTIVE_SKIPPED:
		{
			Asura_Text_Output::Print(fX, fY++,0xff3f3f3f,0,"State : Skipped");
			break;
		}
		default:
		{
			Asura_Assert(0, "unhandled objective state");
			break;
		}
	}
}

void Tools_Entity_Objective::Copy(const Tools_Object* pxObject)
{
	PARENT::Copy(pxObject);

	const Tools_Entity_Objective* pxFromObject = static_cast<const Tools_Entity_Objective*>(pxObject);

	Asura_ServerEntity_Objective* pxFromEntity = pxFromObject->m_pxObjective;
	Asura_ServerEntity_Objective* pxToEntity = m_pxObjective;

	pxToEntity->SetObjectiveNumber(pxFromEntity->GetObjectiveNumber());
	pxToEntity->SetObjectiveState(pxFromEntity->GetObjectiveState());
	pxToEntity->SetTextHash(pxFromEntity->GetTextHash());
	//regen text list so overlay is in sync
	GenerateTextList();
	if (IsSelected())
	{
		CreateOverlay();
	}
}

Tools_Object* Tools_Entity_Objective::CreateObjectOfThisType() const
{
	Tools_Object* pxOldSelection = Tools_Object_System::SelectedObject;
	Tools_Entity_Objective::Create();
	Tools_Object* pxNewSelection = Tools_Object_System::SelectedObject;

	return (pxOldSelection!=pxNewSelection) ? pxNewSelection : 0;
}
