//////////////////////////////////////////////////////////////////////////////
//
// Asura_PC_SteamworksLeaderboards.cpp
//
// Copyright (c) 2009 Rebellion. All rights reserved.
//
// Description:
//
//     Asura wrapper class for steam leaderboards
//
//////////////////////////////////////////////////////////////////////////////

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

#include "Core/Asura.h"

#ifdef ASURA_SUPPORT_LEADERBOARDS
#ifdef ASURA_PC_SUPPORT_STEAMWORKS

// This file's header.
#include "Network/Steamworks/Asura_PC_SteamworksLeaderboards.h"

#include "Network/Online/Asura_Leaderboards.h"
#include "Network/Steamworks/Asura_PC_SteamworksStats.h"

Asura_PC_SteamworksLeaderboard_Writer** Asura_PC_SteamworksLeaderboards::s_ppxLeaderboardWriters = 0;
int** Asura_PC_SteamworksLeaderboards::s_ppiScores = 0;
u_int Asura_PC_SteamworksLeaderboards::s_uNumLeaderboardsToWrite(0);

void Asura_PC_SteamworksLeaderboards::StartWrite(bool bDisconnectedEarly, u_int uMyGuid)
{
	if (!Asura_PC_SteamworksStats::IsInitComplete())
	{
		return;
	}
	s_uNumLeaderboardsToWrite = Asura_Leaderboard_System::Project_GetNumLeaderboards();
	s_ppxLeaderboardWriters = new Asura_PC_SteamworksLeaderboard_Writer* [s_uNumLeaderboardsToWrite];
	s_ppiScores = new int* [s_uNumLeaderboardsToWrite];
	for (u_int u = 0; u < s_uNumLeaderboardsToWrite; ++u)
	{
		u_int uColumns = Asura_Leaderboard_System::Project_GetNumLeaderboardColumns(u);
		s_ppiScores[u] = new int[uColumns];
		for (u_int v = 0; v < uColumns; ++v)
		{
			s_ppiScores[u][v] = Project_GetScore(bDisconnectedEarly, uMyGuid, u, v);
		}

		if (uColumns == 1)
		{
			s_ppxLeaderboardWriters[u] = new Asura_PC_SteamworksLeaderboard_Writer(u, s_ppiScores[u][0], 0, 0);
		}
		else if (uColumns > 1)
		{
			s_ppxLeaderboardWriters[u] = new Asura_PC_SteamworksLeaderboard_Writer(u, s_ppiScores[u][0], &s_ppiScores[u][1], uColumns - 1);
		}
	}
}

bool Asura_PC_SteamworksLeaderboards::UpdateWrite()
{
	bool bWorking = false;
	if (s_ppxLeaderboardWriters)
	{
		for (u_int u = 0; u < s_uNumLeaderboardsToWrite; ++u)
		{
			if (s_ppxLeaderboardWriters[u])
			{
				if (s_ppxLeaderboardWriters[u]->IsWorking())
				{
					bWorking = true;
				}
			}
		}
		if (!bWorking)
		{
			//finished, clean up
			for (u_int u = 0; u < s_uNumLeaderboardsToWrite; ++u)
			{
				delete s_ppxLeaderboardWriters[u];
				delete [] s_ppiScores[u];
			}
			delete [] s_ppxLeaderboardWriters;
			delete [] s_ppiScores;
			s_ppxLeaderboardWriters = 0;
			s_ppiScores = 0;
			s_uNumLeaderboardsToWrite = 0;
		}
	}
	return !bWorking;
}

Asura_PC_SteamworksLeaderboard_Writer::Asura_PC_SteamworksLeaderboard_Writer(u_int uLeaderboardIndex, int iScore, const int* piData, u_int uDataSize)
: m_uLeaderboardIndex(uLeaderboardIndex)
, m_ulSteamLeaderboard(0)
, m_iScore(iScore)
, m_piData(piData)
, m_uDataSize(uDataSize)
, m_bSuccess(false)
, m_bError(false)
{
	const char* szLeaderboardName = Asura_PC_SteamworksLeaderboards::Project_GetBoardName(m_uLeaderboardIndex);
	if (!szLeaderboardName)
	{
		m_bError = true;
		return;
	}
	ELeaderboardSortMethod eLeaderboardSortMethod = Asura_PC_SteamworksLeaderboards::Project_GetBoardSortType(m_uLeaderboardIndex);
	ELeaderboardDisplayType eLeaderboardDisplayType = Asura_PC_SteamworksLeaderboards::Project_GetBoardDisplayType(m_uLeaderboardIndex);

	SteamAPICall_t ulSteamAPICall = SteamUserStats()->FindOrCreateLeaderboard( szLeaderboardName,
		eLeaderboardSortMethod, eLeaderboardDisplayType );

	if ( ulSteamAPICall != 0 )
	{
		// set the function to call when this API call has completed
		m_xSteamCBFindLeaderboard.Set( ulSteamAPICall, this, &Asura_PC_SteamworksLeaderboard_Writer::OnFindLeaderboard );
	}
	else
	{
		m_bError = true;
	}
}

Asura_PC_SteamworksLeaderboard_Writer::~Asura_PC_SteamworksLeaderboard_Writer()
{
}

bool Asura_PC_SteamworksLeaderboard_Writer::IsWorking() const
{
	return !(m_bError || m_bSuccess);
}

bool Asura_PC_SteamworksLeaderboard_Writer::IsErrored() const
{
	return m_bError;
}

void Asura_PC_SteamworksLeaderboard_Writer::OnFindLeaderboard( LeaderboardFindResult_t* pxFindLearderboardResult, bool bIOFailure )
{
	// see if we encountered an error during the call
	if (bIOFailure || !pxFindLearderboardResult || !pxFindLearderboardResult->m_bLeaderboardFound)
	{
		m_bError = true;
		return;
	}

	m_ulSteamLeaderboard = pxFindLearderboardResult->m_hSteamLeaderboard;

	SteamAPICall_t ulSteamAPICall = SteamUserStats()->UploadLeaderboardScore( m_ulSteamLeaderboard, m_iScore, const_cast<int*>(m_piData), static_cast<int>(m_uDataSize) );
	m_xSteamCBUploadScore.Set( ulSteamAPICall, this, &Asura_PC_SteamworksLeaderboard_Writer::OnUploadScore );
}

void Asura_PC_SteamworksLeaderboard_Writer::OnUploadScore( LeaderboardScoreUploaded_t *pxUploadResult, bool bIOFailure )
{
	if (bIOFailure || !pxUploadResult || !pxUploadResult->m_bSuccess )
	{
		m_bError = true;
		return;
	}

	m_bSuccess = true;

	if ( pxUploadResult->m_bScoreChanged )
	{
		// could display new rank
	}
}


Asura_PC_SteamworksLeaderboard_Reader::Asura_PC_SteamworksLeaderboard_Reader(u_int uLeaderboardIndex, bool bFriendsOnly)
: m_uLeaderboardIndex(uLeaderboardIndex)
, m_ulSteamLeaderboard(0)
, m_eState(STATE_VOID)
, m_bFriendsOnly(bFriendsOnly)
, m_uFirstRow(0)
, m_pxScores(0)
, m_ppiData(0)
, m_uNumRows(0)
, m_uNumColumns(0)
, m_uTotalRows(0)
{
}

Asura_PC_SteamworksLeaderboard_Reader::~Asura_PC_SteamworksLeaderboard_Reader()
{
	ClearLoadedData();
}

bool Asura_PC_SteamworksLeaderboard_Reader::IsWorking() const
{
	if (m_eState == STATE_INITIALISING
		|| m_eState == STATE_LOADING)
	{
		return true;
	}
	return false;
}

bool Asura_PC_SteamworksLeaderboard_Reader::IsErrored() const
{
	if (m_eState == STATE_ERROR)
	{
		return true;
	}
	return false;
}

bool Asura_PC_SteamworksLeaderboard_Reader::FetchRows(u_int uFirstRow)
{
	if (m_eState == STATE_FOUND)
	{
		Fetch_Internal();
		return m_eState == STATE_LOADING;
	}
	if (m_eState == STATE_VOID || m_eState == STATE_ERROR)
	{
		FindLeaderboard();
		return m_eState == STATE_INITIALISING;
	}
	return false;
}

u_int Asura_PC_SteamworksLeaderboard_Reader::GetNumRows() const
{
	if (m_eState == STATE_LOADED)
	{
		return m_uNumRows;
	}
	return 0;
}

int Asura_PC_SteamworksLeaderboard_Reader::GetRowRank(u_int uRow) const
{
	if (uRow < m_uNumRows && m_pxScores)
	{
		return m_pxScores[uRow].m_nGlobalRank;
	}
	return -1;
}

const char* Asura_PC_SteamworksLeaderboard_Reader::GetRowName(u_int uRow) const
{
	if (uRow < m_uNumRows && m_pxScores)
	{
		return SteamFriends()->GetFriendPersonaName( m_pxScores[uRow].m_steamIDUser);
	}
	return "";
}

int Asura_PC_SteamworksLeaderboard_Reader::GetRowScore(u_int uRow) const
{
	if (uRow < m_uNumRows && m_pxScores)
	{
		return m_pxScores[uRow].m_nScore;
	}
	return 0;
}

int Asura_PC_SteamworksLeaderboard_Reader::GetRowData(u_int uRow, u_int uColumn) const
{
	if (uRow < m_uNumRows
		&& uColumn < m_uNumColumns
		&& m_ppiData)
	{
		return m_ppiData[uRow][uColumn];
	}
	return 0;
}

void Asura_PC_SteamworksLeaderboard_Reader::ClearLoadedData()
{
	if (m_eState == STATE_LOADED)
	{
		delete [] m_pxScores;
		m_pxScores = 0;
		for (u_int u = 0; u < m_uNumRows; ++u)
		{
			delete [] m_ppiData[u];
		}
		delete [] m_ppiData;
		m_ppiData = 0;
		m_uNumRows = 0;
		m_uNumColumns = 0;
		m_eState = STATE_FOUND;
	}
}

u_int Asura_PC_SteamworksLeaderboard_Reader::GetTotalRows() const
{
	return m_uTotalRows;
}

void Asura_PC_SteamworksLeaderboard_Reader::OnFindLeaderboard( LeaderboardFindResult_t* pxFindLearderboardResult, bool bIOFailure )
{
	// see if we encountered an error during the call
	if (bIOFailure || !pxFindLearderboardResult || !pxFindLearderboardResult->m_bLeaderboardFound)
	{
		m_eState = STATE_ERROR;
		return;
	}

	m_ulSteamLeaderboard = pxFindLearderboardResult->m_hSteamLeaderboard;

	Fetch_Internal();
}

void Asura_PC_SteamworksLeaderboard_Reader::OnScoresDownloaded( LeaderboardScoresDownloaded_t* pxLeaderboardScoresDownloaded, bool bIOFailure )
{
	if (bIOFailure || !pxLeaderboardScoresDownloaded)
	{
		m_eState = STATE_ERROR;
		return;
	}

	m_uTotalRows = SteamUserStats()->GetLeaderboardEntryCount(m_ulSteamLeaderboard);
	
	// leaderboard entries handle will be invalid once we return from this function. Copy all data now.
	m_uNumRows = Asura_Maths::Min( static_cast<u_int>(pxLeaderboardScoresDownloaded->m_cEntryCount), uASURA_LEADERBOARD_MAX_ROWS_IN_MEMORY );

	m_pxScores = new LeaderboardEntry_t[m_uNumRows];
	m_uNumColumns = Asura_Leaderboard_System::Project_GetNumLeaderboardColumns(m_uLeaderboardIndex) - 1;
	m_ppiData = new int*[m_uNumRows];

	for (u_int u = 0; u < m_uNumRows; ++u )
	{
		m_ppiData[u] = new int[m_uNumColumns];
		SteamUserStats()->GetDownloadedLeaderboardEntry( pxLeaderboardScoresDownloaded->m_hSteamLeaderboardEntries, 
			u, &m_pxScores[ u ], m_ppiData[u], m_uNumColumns );
		Asura_Assert(static_cast<u_int>(m_pxScores[u].m_cDetails) == m_uNumColumns, "wrong number of columns on steam");
	}
	m_eState = STATE_LOADED;
}

void Asura_PC_SteamworksLeaderboard_Reader::FindLeaderboard()
{
	const char* szLeaderboardName = Asura_PC_SteamworksLeaderboards::Project_GetBoardName(m_uLeaderboardIndex);
	if (!szLeaderboardName)
	{
		m_eState = STATE_ERROR;
		return;
	}
	ELeaderboardSortMethod eLeaderboardSortMethod = Asura_PC_SteamworksLeaderboards::Project_GetBoardSortType(m_uLeaderboardIndex);
	ELeaderboardDisplayType eLeaderboardDisplayType = Asura_PC_SteamworksLeaderboards::Project_GetBoardDisplayType(m_uLeaderboardIndex);

	SteamAPICall_t ulSteamAPICall = SteamUserStats()->FindOrCreateLeaderboard( szLeaderboardName,
		eLeaderboardSortMethod, eLeaderboardDisplayType );

	if ( ulSteamAPICall != 0 )
	{
		// set the function to call when this API call has completed
		m_xSteamCBFindLeaderboard.Set( ulSteamAPICall, this, &Asura_PC_SteamworksLeaderboard_Reader::OnFindLeaderboard );
		m_eState = STATE_INITIALISING;
	}
	else
	{
		m_eState = STATE_ERROR;
	}
}

void Asura_PC_SteamworksLeaderboard_Reader::Fetch_Internal()
{
	SteamAPICall_t ulSteamAPICall = 0;
	if (m_bFriendsOnly)
	{
		ulSteamAPICall = SteamUserStats()->DownloadLeaderboardEntries( m_ulSteamLeaderboard, k_ELeaderboardDataRequestFriends, 
			1, uASURA_LEADERBOARD_MAX_ROWS_IN_MEMORY);
	}
	else
	{
		ulSteamAPICall = SteamUserStats()->DownloadLeaderboardEntries( m_ulSteamLeaderboard, k_ELeaderboardDataRequestGlobal, 
			m_uFirstRow + 1, m_uFirstRow + uASURA_LEADERBOARD_MAX_ROWS_IN_MEMORY);
	}

	if ( ulSteamAPICall != 0 )
	{
		// Register for the async callback
		m_xSteamCBScoresDownloaded.Set( ulSteamAPICall, this, &Asura_PC_SteamworksLeaderboard_Reader::OnScoresDownloaded );
		m_eState = STATE_LOADING;
	}
	else
	{
		m_eState = STATE_ERROR;
	}
}


#endif // #ifdef ASURA_PC_SUPPORT_STEAMWORKS
#endif // #ifdef ASURA_SUPPORT_LEADERBOARDS
