//////////////////////////////////////////////////////////////////////////////
//
// Asura_PS3_MeritHelper.cpp
//
// Copyright (c) 2009 Rebellion. All rights reserved.
//
// Description:
//
//     helper classes for reading/writing merit from/to TUS
//
//////////////////////////////////////////////////////////////////////////////

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

#include "Core/Asura.h"

#ifdef ASURA_USE_MERIT_SYSTEM

// This file's header.
#include "Network/Online/Asura_PS3_MeritHelper.h"

// Asura Independent
#include "Core/Asura_Maths.h"
#include "Network/Asura_Network.h"
#include "Network/Online/Asura_Merit.h"
#include "Network/Online/Asura_Lobby.h"

// Asura PS3
#include "Network/Online/Asura_PS3_NP.h"
#include "Network/Online/Asura_PS3_NP_TUS.h"

const u_int uASURA_PS3_TUS_MAX_TRANSACTIONS = 32;

//#define TEST_WRITING_LOTS_OF_MERIT_VARS = 1; // used to write data for 32 players

// C L A S S   I M P L E M E N T A T I O N ///////////////////////////////////
Asura_PS3_MeritWriter::Asura_PS3_MeritWriter()
: m_uNumSent(0)
, m_uNumTUSWriters(0)
, m_pxMeritTUSWriters(NULL)
{
	// should be able to get the new computed merit now using our player guid.

	const Asura_Merit_Calculator* pxMeritResults = Asura_Merit_System::GetCalculator();
	Asura_Assert(pxMeritResults, "no merit results, cannot write to TUS");
	if (!pxMeritResults)
	{
		return;
	}

	
#ifdef TEST_WRITING_LOTS_OF_MERIT_VARS
	const u_int uNumPlayers = 32;
#else
	const u_int uNumPlayers = pxMeritResults->GetNumPlayers();
#endif

	// start the TUS transaction
	Asura_PS3_NP_TUS::Initialise();

	// each player uses 2 transactions, and there is a limit of 32 active transactions
	u_int uNumToSend = Asura_Maths::Min(uNumPlayers, uASURA_PS3_TUS_MAX_TRANSACTIONS / 2);

	SendRequest(0, uNumToSend);

	// remember how many we've done.
	m_uNumSent = uNumToSend;
}

Asura_PS3_MeritWriter::~Asura_PS3_MeritWriter()
{
	if (m_pxMeritTUSWriters)
	{
		delete[] m_pxMeritTUSWriters;
		Asura_PS3_NP_TUS::Shutdown();
	}
}

void Asura_PS3_MeritWriter::SendRequest( u_int uFirstPlayerIndex, u_int uNum )
{
	const Asura_Merit_Calculator* pxMeritResults = Asura_Merit_System::GetCalculator();
	Asura_Assert(pxMeritResults, "no merit results, cannot write to TUS");
	if (!pxMeritResults)
	{
		return;
	}

	// allocate the transaction managers
	Asura_Assert(!m_uNumTUSWriters && !m_pxMeritTUSWriters, "already allocated" );
	m_uNumTUSWriters = 2 * uNum; // 2 for each player.
	m_pxMeritTUSWriters = new Asura_PS3_TUSTransactionManagerAddToVar[m_uNumTUSWriters];

	u_int uCurWriter = 0;
	for (u_int u = 0; u < uNum; ++u)
	{
		u_int uPlayer = uFirstPlayerIndex + u;
#ifdef TEST_WRITING_LOTS_OF_MERIT_VARS
		uPlayer = uPlayer % pxMeritResults->GetNumPlayers();
#endif

		Asura_Assert(uCurWriter + 1 < m_uNumTUSWriters, "miscalculated num TUS writers required");

		const void* pExtraData = pxMeritResults->GetPlayerExtraData(uPlayer);
		Asura_Assert(pExtraData, "no data");
		if (pExtraData)
		{
			int iMeritLevelChange = 0;
			int iMeritPointsChange = 0;
			pxMeritResults->GetPlayerMeritChange(uPlayer, iMeritLevelChange, iMeritPointsChange);

			// cast the extra data pointer to the lobby player data type
			const Asura_Platform_Lobby_PlayerData* pxLobbyPlayerData = reinterpret_cast<const Asura_Platform_Lobby_PlayerData*>(pExtraData);
			m_pxMeritTUSWriters[uCurWriter++].AddToVarAsync(pxLobbyPlayerData->m_xNpID, ASURA_PS3_TUS_VAR_SLOT_MERIT_LEVEL, iMeritLevelChange);
			m_pxMeritTUSWriters[uCurWriter++].AddToVarAsync(pxLobbyPlayerData->m_xNpID, ASURA_PS3_TUS_VAR_SLOT_MERIT_POINTS, iMeritPointsChange);
		}
	}

	Asura_Assert(uCurWriter == m_uNumTUSWriters, "unexpected number of TUS Writers");
}

bool Asura_PS3_MeritWriter::Update()
{
	if (m_pxMeritTUSWriters)
	{
		// see if the writes all finished yet
		bool bFinished = true;
		for (u_int u = 0; u < m_uNumTUSWriters; ++u)
		{
			bFinished = m_pxMeritTUSWriters[u].Update() && bFinished;
		}

		if (bFinished)
		{
			// check for errors
			for (u_int u = 0; u < m_uNumTUSWriters; ++u)
			{
				Asura_Assert(!m_pxMeritTUSWriters->HasErrorOccurred(), "failed to write player merit to TUS");
			}

			// clean up current allocations
			delete[] m_pxMeritTUSWriters;
			m_pxMeritTUSWriters = NULL;
			m_uNumTUSWriters = 0;

			// check if we've any more to write
			const Asura_Merit_Calculator* pxMeritResults = Asura_Merit_System::GetCalculator();
			Asura_Assert(pxMeritResults, "no merit results, cannot write to TUS");
			if (pxMeritResults)
			{
#ifdef TEST_WRITING_LOTS_OF_MERIT_VARS
				const u_int uTotalToWrite = 32;
#else
				const u_int uTotalToWrite = pxMeritResults->GetNumPlayers();
#endif
				if (m_uNumSent < uTotalToWrite)
				{
					// some left, send the next batch
					const u_int uNumToSend = Asura_Maths::Min(uTotalToWrite - m_uNumSent, uASURA_PS3_TUS_MAX_TRANSACTIONS / 2);
					SendRequest(m_uNumSent, uNumToSend);

					// remember how many we've done.
					m_uNumSent += uNumToSend;
					bFinished = false;
				}
			}
		}

		if (bFinished)
		{
			Asura_PS3_NP_TUS::Shutdown();
		}
		else
		{
			return false;
		}
	}
	return true;
}

#endif // #ifdef ASURA_USE_MERIT_SYSTEM
