//////////////////////////////////////////////////////////////////////////////
//
// Asura_PS3_Stats.h
//
// Copyright (c) 2008 Rebellion. All rights reserved.
//
// Description:
//
//     PS3 classes to manage leaderboard & stats reading/writing
//
//     Since the number of rows in a PSN leaderboard is limited stats data is
//     duplicated in Title User Storage (TUS). Each leaderboard requires 
//     8 bytes for the ranking stat (int64) and 64 bytes for the GameInfo
//     which is used on the leaderboards to represent the other columns. 
//     TUS slot 1 must therefore be configured to hold 72*num_leaderboards 
//     bytes. 
//
//     The StatsWriter class is responsible for storing the latest data in 
//     both places. A StatsReader will only read from the leaderboard version 
//     and a StatsPreGameReader will only read from the TUS version (since 
//     that is used for computed stats). 
//
//     Ideally, friends-only stats would be read from the TUS version since 
//     some of your friends might not have qualified for the leaderboards. 
//     However there is no convenient function to do this - you have to fetch 
//     each user's data individually which could be slow if you have 100 
//     friends.
//
//////////////////////////////////////////////////////////////////////////////

// B E G I N   F I L E   W R A P P E R ///////////////////////////////////////

#ifdef ASURA_USE_PRAGMA_ONCE
    #pragma once
#endif

#ifndef ASURA_PS3_STATS_H___
#define ASURA_PS3_STATS_H___ 1

#ifdef ASURA_SUPPORT_LEADERBOARDS

#include "Network/Online/Asura_Leaderboards.h"
#include "Network/Online/Asura_PS3_NP_TUS.h"
#include <np.h>

// C O N S T A N T S /////////////////////////////////////////////////////////
static const u_int uASURA_PS3_MAX_STAT_COLS = SCE_NP_SCORE_GAMEINFO_SIZE/sizeof(u_int);
static const u_int uASURA_PS3_INVALID_BOARD_ID = 0;

// C L A S S   D E C L A R A T I O N S ///////////////////////////////////////
union Asura_PS3_StatsColumn
{
	u_int m_uData;
	float m_fData;
};

union Asura_PS3_Stats_ExtraColumns
{
	Asura_PS3_StatsColumn m_xColumns[uASURA_PS3_MAX_STAT_COLS];
	SceNpScoreGameInfo m_xGameInfo;
};

class Asura_PS3_NP_Score
{
public:

	static void Initialise();
	static void Shutdown();

	static int GetTitleContext() { return s_iTitleContext; }

private:

	static int s_iTitleContext;
};

class Asura_PS3_Stats
{
public:

	// Project_GetBoardID() returns the BoardID for the leaderboard index as set in PS3 SMT on devnet
	// 0 is used for an invalid board.
	static u_int Project_GetBoardID( u_int uLeaderboardIndex );

	// Project_GetNumValidBoardIDs() should return the number of leaderboards we actually use.
	static u_int Project_GetNumValidBoardIDs();

	// get project code to fill out the stats for the logged-in player
	static void Project_GetMyStats( bool bDisconnectedEarly, u_int uGuid, 
		u_int uLeaderboardIndex, SceNpScoreValue& xScore, Asura_PS3_Stats_ExtraColumns& xColumns);
};

// Asura_PS3_StatsTransactionManager was used as the basis for Asura_PS3_TUSTransactionManagerBase
class Asura_PS3_StatsTransactionManager
{
public:

	Asura_PS3_StatsTransactionManager();
	~Asura_PS3_StatsTransactionManager();

	bool Update();
	int GetOperationResult() const { return m_iResult; }
	bool HasErrorOccurred() const { return m_bError; }

	int CreateTransactionID();
	void SetError() { m_bError = true; }

private:
	bool m_bError;
	int m_iTransactionID;
	int m_iResult;
};

class Asura_PS3_StatsReader
{
public:
	Asura_PS3_StatsReader();
	~Asura_PS3_StatsReader();

	bool Update();			// returns true on completion of a pending request.
	bool HasErrorOccurred() const { return m_bError; }

	bool FetchStats( u_int uBoardID, u_int uStart, u_int uNumRows );
	bool FetchStatsForNpIds( u_int uBoardID, const SceNpId* pxNpIds, u_int uNumNpIds );

	// access functions for when the stats have arrived
	u_int GetTotalEntries() const { return m_uTotalEntries; }
	u_int GetNumValidRows() const { return m_uValidRows; }
	const SceNpScoreRankData* GetStatsDataArray() const { return m_axStatsData; }
	const SceNpScoreGameInfo* GetExtraColumnDataArray() const { return m_axExtraColumnData; }

private:

	Asura_PS3_StatsTransactionManager m_xTransactionManager;
	SceNpScoreRankData m_axStatsData[uASURA_LEADERBOARD_MAX_ROWS_IN_MEMORY];
	SceNpScoreGameInfo m_axExtraColumnData[uASURA_LEADERBOARD_MAX_ROWS_IN_MEMORY];
	CellRtcTick m_xLastSortDate;
	u_int m_uTotalEntries;
	u_int m_uValidRows;
	bool m_bError;
};

class Asura_PS3_StatsPreGameReader
{
public:

	static void Initialise();
	static bool IsInitialised() { return s_pxInstance != NULL; }
	static bool Update();	// returns true on completion
	static void Shutdown();

	static bool HasErrorOccurred();
	static const SceNpScoreValue* GetRankingStat(u_int uLeaderboard);
	static const Asura_PS3_Stats_ExtraColumns* GetExtraColumnDataArray(u_int uLeaderboard);

private:

	// private ctor/dtor
	Asura_PS3_StatsPreGameReader();
	~Asura_PS3_StatsPreGameReader();

	// private Internal member functions
	bool InitialiseInternal();
	bool InternalUpdate();
	bool HasErrorOccurredInternal() const { return m_bError; }
	const SceNpScoreValue* GetRankingStatInternal(u_int uLeaderboard) const;
	const SceNpScoreGameInfo* GetExtraColumnDataArrayInternal(u_int uLeaderboard) const;

	// private member data
	Asura_PS3_TUSTransactionManagerData m_xTransactionManager;
	char* m_pcData;
	bool m_bDataValid;
	bool m_bError;

	// static class data
	static Asura_PS3_StatsPreGameReader* s_pxInstance;
};

class Asura_PS3_StatsWriter
{
	enum PHASE
	{
		PHASE_NONE,
		PHASE_WRITING_TO_SCOREBOARDS,
		PHASE_WRITING_TO_TUS,
	};

public:
	Asura_PS3_StatsWriter();
	~Asura_PS3_StatsWriter();

	void WriteStats( bool bDisconnectedEarly, u_int uMyGuid );
	bool Update();	// returns true on completion/error
	bool HasErrorOccurred() const { return m_xTransactionManager.HasErrorOccurred(); }

private:

	bool UpdateWritingToScoreboards();
	bool UpdateWritingToTUS();
	void RecordScoresForCurrentIndex();
	void RecordScoresInTUS();

	Asura_PS3_StatsTransactionManager m_xTransactionManager;
	Asura_PS3_TUSTransactionManagerData m_xTransactionManagerTUS;
	// arrays for the ranking scores and for the columns.
	u_int* m_puBoardIndices;
	SceNpScoreValue* m_pxScores;
	Asura_PS3_Stats_ExtraColumns* m_pxColumns;
	void* m_pTUSData;
	SceNpScoreRankNumber m_xTempRank;
	u_int m_uCurrentLeaderboardIndex;
	u_int m_uNumLeaderboards;
	PHASE m_ePhase;
};

#endif // #ifdef ASURA_SUPPORT_LEADERBOARDS

// E N D   F I L E   W R A P P E R ///////////////////////////////////////////

#endif // #ifndef ASURA_PS3_STATS_H___
