//////////////////////////////////////////////////////////////////////////////
//
// Asura_ServerEntity_Objective.cpp
//
// Copyright (c) 2007 Rebellion. All rights reserved.
//
// Description:
//
//     Base Asura Objective Entity
//
//////////////////////////////////////////////////////////////////////////////

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

#include "Core/Asura.h"

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

// Asura includes
#include "ChunkSystem/Asura_Chunk_Stream.h"
#include "Entity/Asura_Entity_Event.h"
#include "Entity/Objective/Asura_ClientEntity_Objective.h"
#include "Network/Asura_Message.h"
#include "Network/Asura_Network.h"

Asura_ServerEntity_Objective::Asura_ServerEntity_Objective(Asura_Guid uGuid)
: Asura_Entity_Server(uGuid)
, m_eObjectiveState(ASURA_OBJECTIVE_READY)
, m_uTextHash(ASURA_HASH_ID_UNSET)
, m_uObjectiveNumber(0)
{
	Classification = AsuraEntityClass_Objective;

	SetNeverUpdates(true);
	SetAlwaysUpdateClient(false);
}

Asura_ServerEntity_Objective::~Asura_ServerEntity_Objective()
{
}

void Asura_ServerEntity_Objective::Create(Asura_Guid uGuid, Asura_Chunk_Stream& xStream)
{
	Asura_ServerEntity_Objective* pxObjective = new Asura_ServerEntity_Objective(uGuid);
	if (pxObjective)
	{
		pxObjective->ReadFromChunkStream(xStream);
	}
}

bool Asura_ServerEntity_Objective::HandleMessage(const Asura_Message* pxMessage)
{
	// pass state change messages straight to the state machine
	if (pxMessage)
	{
		switch (pxMessage->GetMsgID())
		{
			case ASURA_MSG_CLIENT_REQUESTCREATEMSG:
			{
				MakeClientUpdateMessage();
				return true;
			}
			case ASURA_MSG_OBJECTIVE_READY:
			{
				RequestObjectiveState(ASURA_OBJECTIVE_READY);
				return true;
			}
			case ASURA_MSG_OBJECTIVE_COMPLETE:
			{
				RequestObjectiveState(ASURA_OBJECTIVE_COMPLETE);
				return true;
			}
			case ASURA_MSG_OBJECTIVE_FAIL:
			{
				RequestObjectiveState(ASURA_OBJECTIVE_FAILED);
				return true;
			}
			case ASURA_MSG_OBJECTIVE_SKIP:
			{
				RequestObjectiveState(ASURA_OBJECTIVE_SKIPPED);
				return true;
			}
			default:
			{
				break;
			}
		}
	}

	return PARENT::HandleMessage(pxMessage);
}

void Asura_ServerEntity_Objective::RequestObjectiveState(ASURA_OBJECTIVE_STATE eObjectiveState)
{
	//flag if we've changed state, to update the client
	bool bChange = false;
	switch (eObjectiveState)
	{
		case ASURA_OBJECTIVE_READY:
		{
			//only pending objectives can be readied
			if (m_eObjectiveState == ASURA_OBJECTIVE_PENDING)
			{
				bChange = true;
			}
			else
			{
				Asura_Assertf(0, ("trying to ready objective %u when it's not pending", GetGuid()));
			}
			break;
		}
		case ASURA_OBJECTIVE_COMPLETE:
		{
			// can only complete a ready or pending objective
			// silently ignore if already complete
			Asura_Assertf(m_eObjectiveState != ASURA_OBJECTIVE_FAILED, ("trying to complete objective %u when it's already failed", GetGuid()));
			Asura_Assertf(m_eObjectiveState != ASURA_OBJECTIVE_SKIPPED, ("trying to complete objective %u when it's already skipped", GetGuid()));
			if (m_eObjectiveState == ASURA_OBJECTIVE_READY || m_eObjectiveState == ASURA_OBJECTIVE_PENDING)
			{
				bChange = true;
				//only fire message block on the actual state change
				SendMessagesForEvent(ASURA_ENTITY_EVENT_ONCOMPLETED);
			}
			break;
		}
		case ASURA_OBJECTIVE_FAILED:
		{
			// can only fail a ready or pending objective
			// silently ignore if already failed
			Asura_Assertf(m_eObjectiveState != ASURA_OBJECTIVE_COMPLETE, ("trying to fail objective %u when it's already complete", GetGuid()));
			Asura_Assertf(m_eObjectiveState != ASURA_OBJECTIVE_SKIPPED, ("trying to fail objective %u when it's already skipped", GetGuid()));
			if (m_eObjectiveState == ASURA_OBJECTIVE_READY || m_eObjectiveState == ASURA_OBJECTIVE_PENDING)
			{
				bChange = true;
				//only fire message block on the actual state change
				SendMessagesForEvent(ASURA_ENTITY_EVENT_ONFAILED);
			}
			break;
		}
		case ASURA_OBJECTIVE_SKIPPED:
		{
			// can only skip a ready or pending objective
			// silently ignore if already skipped
			Asura_Assertf(m_eObjectiveState != ASURA_OBJECTIVE_COMPLETE, ("trying to skip objective %u when it's already complete", GetGuid()));
			Asura_Assertf(m_eObjectiveState != ASURA_OBJECTIVE_FAILED, ("trying to skip objective %u when it's already failed", GetGuid()));
			if (m_eObjectiveState == ASURA_OBJECTIVE_READY || m_eObjectiveState == ASURA_OBJECTIVE_PENDING)
			{
				bChange = true;
			}
			break;
		}
		default:
		{
			Asura_Assert(0, "unhandled objective state");
			break;
		}
	}
	if (bChange)
	{
		//change the state
		m_eObjectiveState = eObjectiveState;
		if (IsEntityActive())
		{
			//only update the client if we're active, as it may not exist if we're inactive
			//activating the client will update it
			RequestClientUpdate();
		}
	}
}

Asura_Hash_ID Asura_ServerEntity_Objective::GetObjectiveLocalisedPageID() const
{
	return uASURA_HASH_TEXTPAGE_OBJECTIVES;	
}

void Asura_ServerEntity_Objective::ReadFromChunkStream(Asura_Chunk_Stream& xStream)
{
	u_int uVersion;

	xStream >> uVersion;

	if (uVersion > uOBJECTIVE_CHUNK_VERSION)
	{
		Asura_Assert(0, "Unhandled chunk version in Asura_ServerEntity_Objective::ReadFromChunkStream");
		return;
	}

	PARENT::ReadFromChunkStream(xStream);

	//keep each version's code together, to make the current version more readable, and allow obsolete versions to be ifdef'd to tools only
	if (uVersion == 0)
	{
		u_int uState;
		xStream >> uState;
		m_eObjectiveState = static_cast<ASURA_OBJECTIVE_STATE>(uState);
		xStream >> m_uTextHash;
		xStream >> m_uObjectiveNumber;
	}
}

void Asura_ServerEntity_Objective::WriteToChunkStream(Asura_Chunk_Stream& xStream) const
{
	xStream << uOBJECTIVE_CHUNK_VERSION;

	PARENT::WriteToChunkStream(xStream);

	xStream << static_cast<u_int>(m_eObjectiveState);
	xStream << m_uTextHash;
	xStream << m_uObjectiveNumber;
}

void Asura_ServerEntity_Objective::PostLoadInitialise()
{
	//if we start active, make sure the client is created
	if (IsEntityActive())
	{
		RequestClientUpdate();
	}
	PARENT::PostLoadInitialise();
}

#ifdef ASURA_TOOLS
void Asura_ServerEntity_Objective::ApplyChangesForTemplateInstance(class Tools_EntityTemplateInstance* pxInstance)
{
	PARENT::ApplyChangesForTemplateInstance(pxInstance);
}
#endif // #ifdef ASURA_TOOLS

void Asura_ServerEntity_Objective::MakeClientUpdateMessage()
{
	Asura_Message* pxMsg = Asura_Network::CreateMessage( ASURA_MSG_CLIENTUPDATE, sizeof(Asura_Message_Objective_ClientUpdate), GetClassification() );
	if( !pxMsg ) { return; }

	Asura_Message_Objective_ClientUpdate* pxMsgData = static_cast<Asura_Message_Objective_ClientUpdate*>( pxMsg->GetData() );
	pxMsgData->FillInObjective( this );

	Send( pxMsg, ASURA_GUID_MY_CLIENT_ENTITIES );
}

void Asura_ServerEntity_Objective::OnEntityActivateMessage()
{
	//update the client to current state. causes it to create if needed, and we don't update it while inactive
	RequestClientUpdate();


	PARENT::OnEntityActivateMessage();
}


//message pack/unpack functions
void Asura_Message_Objective_ClientUpdate::FillInObjective(const Asura_ServerEntity_Objective* pxObjective)
{
	m_eObjectiveState = pxObjective->GetObjectiveState();
	m_uTextHash = pxObjective->GetTextHash();
	m_uObjectiveNumber = pxObjective->GetObjectiveNumber();
}

void Asura_Message_Objective_ClientUpdate::FillOutObjective(Asura_ClientEntity_Objective* pxObjective) const
{
	pxObjective->SetStatus( m_eObjectiveState );
	pxObjective->SetTextHash( m_uTextHash );
	pxObjective->SetObjectiveNumber( m_uObjectiveNumber );
}

#ifdef ASURA_SUPPORT_NETMSG_SYSTEM
#include "Network/Asura_NetMsg.h"

void Asura_Message_Objective_ClientUpdate::RegisterFields(u_short usMsgID, u_short usEntityClassification)
{
	Asura_Assert(usMsgID == ASURA_MSG_CLIENTUPDATE, "invalid msg ID");
	Asura_Assert(!Asura_NetMsg::FindNetMsgObject(usMsgID, usEntityClassification), "already exists");

	u_char ucNumFields = GetNumFields();

	Asura_NetMsg* pxNetMsg = new Asura_NetMsg(usMsgID, usEntityClassification, ucNumFields, sizeof(Asura_Message_Objective_ClientUpdate));
	RegisterFields(pxNetMsg);
	pxNetMsg->EndRegistration();
}

void Asura_Message_Objective_ClientUpdate::RegisterFields(class Asura_NetMsg* pxNetMsg)
{
	Asura_Message_Objective_ClientUpdate xDummy;
	Asura_NetMsg_RegisterField(pxNetMsg, xDummy, Asura_FieldType_uint, m_eObjectiveState, 1, Asura_Threshold_AnyChange, 0.0f);
	Asura_NetMsg_RegisterField(pxNetMsg, xDummy, Asura_FieldType_uint, m_uTextHash, 1, Asura_Threshold_AnyChange, 0.0f);
	Asura_NetMsg_RegisterField(pxNetMsg, xDummy, Asura_FieldType_uint, m_uObjectiveNumber, 1, Asura_Threshold_AnyChange, 0.0f);
}
#endif//#ifdef ASURA_SUPPORT_NETMSG_SYSTEM
