// DWBS ------------------------------------------------------------------ DWBS
//
// PLATFORM		: ALL
// PRODUCT		: BIT_DEMON
// VISIBILITY	: PUBLIC
//
// DWBS ------------------------------------------------------------------ DWBS

#include <bdLobby/bdStats/bdStats.h>
#include <bdLobby/bdRemoteTaskManager/bdRemoteTaskManager.h>
#include <bdLobby/bdStats/bdStatsInfo.h>
#include <bdLobby/bdStats/bdStatsSubmission.h>

#define BD_LOG_LEVEL	("lobby/stats")

bdStats::bdStats(bdRemoteTaskManager *const remoteTaskManager)
: m_remoteTaskManager(remoteTaskManager),
  m_errorCode(BD_NO_ERROR)
{
}

bdStats::~bdStats()
{
	m_remoteTaskManager = BD_NULL;
}

bdRemoteTask* bdStats::writeStats(const bdUByte8 /*controllerIndex*/,
								  const bdLeaderBoardID leaderBoardID,
								  const bdWriteType writeType,
								  bdStatsInfo *stats,
								  const bdTeamID teamID)
{
	bdBitBuffer buffer(0, true);

	buffer.writeUByte8(0);

	bdUByte8 ucharWriteType = static_cast<bdUByte8>(writeType);
	buffer.writeUByte8(ucharWriteType);
	
	buffer.writeInt32(leaderBoardID);

	buffer.writeUInt64(teamID);
	
	// serialize additional params if present
	if (stats != BD_NULL)
	{
		stats->serialize(buffer);
	}

	// start task
	bdRemoteTask *task = BD_NULL;

	m_errorCode = m_remoteTaskManager->startTask(&task,
												 BD_STATS_SERVICE,
												 BD_STAT_WRITE,
												 buffer);
	return task;
}

bdRemoteTask* bdStats::writeMultipleStats(const bdUByte8 /*controllerIndex*/,
										  const bdLeaderBoardID leaderBoardID,
										  const bdWriteType writeType,
										  bdStatsInfo **stats,
										  const bdUInt32 numStats)
{
	bdBitBuffer buffer(0, true);
	
	buffer.writeUByte8(0);

	bdUByte8 ubyteWriteType = static_cast<bdUByte8>(writeType);
	buffer.writeUByte8(ubyteWriteType);
	
	buffer.writeInt32(leaderBoardID);
	buffer.writeInt32(numStats);

	// serialize additional params if present
	if ((stats != BD_NULL) && (numStats > 0))
	{
		// all stats sets have same number of fields, just write this variable once.
		// add 1 to numSerialisedFields as we're also serialiing the entityID for each user
		const bdUInt numSerialisedFields = stats[0]->getNumFields() + 1;
		buffer.writeInt32(numSerialisedFields);

		for (bdUInt32 i=0; i < numStats; i++)
		{
			const bdUInt numFields = stats[i]->getNumFields() + 1;
			BD_ASSERT(numSerialisedFields == numFields, "Different classes are being for the same write to a single leaderboard!");
			if(numSerialisedFields == numFields)
			{
				buffer.writeUInt64(stats[i]->getEntityID());
				stats[i]->serialize(buffer);
			}
		}
	}

	// start task
	bdRemoteTask *task = BD_NULL;

	m_errorCode = m_remoteTaskManager->startTask(&task,
												 BD_STATS_SERVICE,
												 BD_STAT_WRITE_MULTI_STATS,
												 buffer);
	return task;
}

bdRemoteTask* bdStats::writeMultipleStats(bdStatsSubmission *stats,
								 const bdUInt numStats)
{
	bdBitBuffer buffer(0, true);

	if ((stats != BD_NULL) && (numStats > 0))
	{
		for (bdUInt i=0; i < numStats; i++)
		{
			bdStatsSubmission *s = &(stats[i]);
			BD_ASSERT(s, "Stats array contains null elements");
			if (!s->serialize(buffer))
			{
				BD_WARN(BD_LOG_LEVEL, "bdStatsSubmission::serialize() failed.");
				m_errorCode = BD_PARAM_PARSE_ERROR;
				return BD_NULL;
			}
		}
	}

	bdRemoteTask* task = BD_NULL;

	m_errorCode = m_remoteTaskManager->startTask(&task,
												 BD_STATS_SERVICE,
												 BD_STAT_WRITE_MULTI_STATS_180,
												 buffer);
	return task;
}


bdRemoteTask* bdStats::deleteStats(const bdUByte8 /*controllerIndex*/,
								   const bdLeaderBoardID leaderBoardID,
								   const bdTeamID teamID)
{
	bdBitBuffer buffer(0, true);
	buffer.writeUByte8(0);
	buffer.writeInt32(leaderBoardID);
	buffer.writeUInt64(teamID);

	// start task
	bdRemoteTask *task = BD_NULL;

	m_errorCode = m_remoteTaskManager->startTask(&task,
												 BD_STATS_SERVICE,
												 BD_STAT_DELETE,
												 buffer);
	return task;
}

bdRemoteTask* bdStats::readStats(const bdLeaderBoardID leaderBoardID,
								 const bdEntityID entityID)
{
	BD_WARN(BD_LOG_LEVEL, "bdStats::readStats(2 param version..) has been deprecated. Use bdStats::readStats(3 param version instead) .");

	bdBitBuffer buffer(0, true);
	
	// write an empty controller index
	buffer.writeUByte8(0);

	buffer.writeInt32(leaderBoardID);
	buffer.writeUInt64(entityID);

	// start task
	bdRemoteTask *task = BD_NULL;

	m_errorCode = m_remoteTaskManager->startTask(&task,
												 BD_STATS_SERVICE,
												 BD_STAT_READ_BY_ENTITY_ID,
												 buffer);
	return task;
}

bdRemoteTask* bdStats::readStats(const bdLeaderBoardID leaderBoardID,
								 const bdEntityID *entityID,
								 const bdUInt numEntityIDs)
{
	// write interface params, and bdStatsInfo custom params
	bdBitBuffer buffer(0, true);

	// write fake controller index for LSG
	buffer.writeUByte8(0);

	buffer.writeInt32(leaderBoardID);

	// write the entityIDs
	buffer.writeUInt32(numEntityIDs);
	for (bdUInt i = 0; i < numEntityIDs; i++)
	{
		buffer.writeUInt64(entityID[i]);
	}
		
	// start task
	bdRemoteTask *task = BD_NULL;

	m_errorCode = m_remoteTaskManager->startTask(&task,
												 BD_STATS_SERVICE,
												 BD_STAT_READ_MULTIPLE_IDS,
												 buffer);

	return task;
}

bdRemoteTask* bdStats::readLeaderboardStats(const bdLeaderBoardID leaderBoardID, 
											const bdEntityID entityID,
											const bdUInt64 firstRank,
											const bdUInt32 maxResults)
{
	// write interface params, and bdStatsInfo custom params
	bdBitBuffer buffer(0, true);
	
	// write fake controller index for LSG
	buffer.writeUByte8(0);

	buffer.writeInt32(leaderBoardID);
	
	// write the entityID
	buffer.writeUInt64(entityID);

	buffer.writeUInt64(firstRank);
	buffer.writeInt64(static_cast<bdInt64>(maxResults));
	
	// start task
	bdRemoteTask *task = BD_NULL;

	m_errorCode = m_remoteTaskManager->startTask(&task,
												 BD_STATS_SERVICE,
												 BD_STAT_READ_LEADERBOARD,
												 buffer);

	return task;
}

bdRemoteTask* bdStats::startArbitratedSession(const bdSecurityID matchMakingSecID)
{
	// write interface params, and bdStatsInfo custom params
	bdBitBuffer buffer(0, true);

	// write an emtpy controller index
	buffer.writeUByte8(0);

	// write secID
	buffer.writeBlob(matchMakingSecID.ab, BD_SECURITY_ID_LENGTH);

	// start task
	bdRemoteTask *task = BD_NULL;

	m_errorCode = m_remoteTaskManager->startTask(&task,
												 BD_STATS_SERVICE,
												 BD_STAT_START_ARBITRATED_SESSION,
												 buffer);
	return task;
}
	
bdRemoteTask* bdStats::writeArbitratedStats(const bdUInt32 leaderBoardID, 
											const bdSecurityID secID, 
											const bdUInt64 nonce,
											const bdWriteType writeType,
											bdStatsInfo **stats,
											const bdUInt32 numStats)
{
	bdBitBuffer buffer(0, true);
	
	// write empty controller index
	// It doesn't actually matter to the Stats service who writes the arbitrated data, as long
	// as their logged in.
	buffer.writeUByte8(0);

	bdUByte8 ubyteWriteType = static_cast<bdUByte8>(writeType);
	buffer.writeUByte8(ubyteWriteType);
	
	buffer.writeInt32(leaderBoardID);
	buffer.writeBlob(secID.ab, BD_SECURITY_ID_LENGTH);
	buffer.writeUInt64(nonce);
	buffer.writeInt32(numStats);

	// serialize additional params if present
	if ((stats != BD_NULL) && (numStats > 0))
	{
		// all stats sets have same number of fields, just write this variable once.
		// add 1 to numSerialisedFields as we're also serialiing the entityID for each user
		const bdUInt numSerialisedFields = stats[0]->getNumFields() + 1;
		buffer.writeInt32(numSerialisedFields);

		for (bdUInt32 i=0; i < numStats; i++)
		{
			const bdUInt numFields = stats[i]->getNumFields() + 1;
			BD_ASSERT(numSerialisedFields == numFields, "Different classes are being for the same write to a single leaderboard!");
			if(numSerialisedFields == numFields)
			{
				buffer.writeUInt64(stats[i]->getEntityID());
				stats[i]->serialize(buffer);
			}
		}
	}

	// start task
	bdRemoteTask *task = BD_NULL;

	m_errorCode = m_remoteTaskManager->startTask(&task,
												 BD_STATS_SERVICE,
												 BD_STAT_WRITE_ARBITRATED_STATS,
												 buffer);
	return task;
}

bdBool bdStats::handleTask(const bdRemoteTask *remoteTask, 
						   bdQueryResult *queryResult)
{
	bdBool ok;
	bdBitBufferRef buffer = remoteTask->getResults();
	bdUInt32 taskErrorCode = BD_MAX_ERROR_CODE;
	ok = buffer->readUInt32(taskErrorCode);
	if (ok)
	{
		m_errorCode = static_cast<bdLobbyErrorCode>(taskErrorCode);
		if (m_errorCode == BD_NO_ERROR)
		{
			bdUByte8 operation;
			ok = buffer->readUByte8(operation);

			if(ok)
			{
				switch (operation)
				{
					case BD_STAT_WRITE:
					case BD_STAT_DELETE:
					case BD_STAT_WRITE_MULTI_STATS:
					case BD_STAT_WRITE_ARBITRATED_STATS:
					{
						// no results here, just task completed
					}
					break;
					case BD_STAT_READ_BY_ENTITY_ID:
					case BD_STAT_READ_MULTIPLE_IDS:
					case BD_STAT_READ_LEADERBOARD:
					case BD_STAT_CUSTOM_READ:
					{
						bdUInt32 numReplies = 0;
						ok = buffer->readUInt32(numReplies);
						if (ok 
							&& numReplies > 0 
							&& queryResult != BD_NULL)
						{
							ok = queryResult->handleResult(numReplies, buffer);
						}
					}
					break;
					case BD_STAT_START_ARBITRATED_SESSION:
					{
						if (ok
							&& queryResult != BD_NULL)
						{
							bdUInt numReplies = 1;
							ok = queryResult->handleResult(numReplies, buffer);
						}
					}
					break;
					case BD_STAT_WRITE_MULTI_STATS_180:
					{
						if (ok
							&& queryResult != BD_NULL)
						{
							ok = queryResult->handleResult(0, buffer);
						}

					}
					break;
					default:
					{
						BD_WARN("warn", "returned operations opID doesn't match existing opIDs");
					}
				}
			}
		}
#if defined BD_DEBUG
		else
		{

			bdUInt64 transactionID;
			bdUInt64 connectionID;

			connectionID = m_remoteTaskManager->getConnectionID();
			transactionID = remoteTask->getTransactionID();

			// here we originally called bdRemoteTaskManager::obtainTransactionID
			BD_WARN(BD_LOG_LEVEL, "Transaction ID " BD_UINT_64_FORMAT ":" BD_UINT_64_FORMAT " failed.",
				connectionID, transactionID);
		}
#endif // BD_DEBUG
	}
	return (m_errorCode == BD_NO_ERROR && ok);
}

bdLobbyErrorCode bdStats::getErrorCode() const
{
	return m_errorCode;
}
