//////////////////////////////////////////////////////////////////////////////
//
// Asura_Xbox360_Achievements_System.cpp
//
// Copyright (c) 2009 Rebellion. All rights reserved.
//
// Description:
//
//     Asura wrapper class for 360's achievements SDK
//
//////////////////////////////////////////////////////////////////////////////

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

#include "Core/Asura.h"

#ifdef ASURA_USE_REWARDS
// This file's header.
#include "Rewards/Asura_Xbox360_Achievements_System.h"

#include "Core/Asura_Multithread.h"
#include "Core/Memory/Asura_Mem.h"
#include "Core/Threads/Asura_ProducerConsumerQueue.h"
#include "Debugging/Asura_Logfile.h"

#include "Core/Asura_Xbox360_OS.h"

#include <Xbox.h>

Asura_ProducerConsumerQueue* Asura_Xbox360_Achievements_System::s_pxWorkerThread = 0;
bool Asura_Xbox360_Achievements_System::s_bInitComplete(false);
u_int Asura_Xbox360_Achievements_System::s_uNumAchievements(0);
char* Asura_Xbox360_Achievements_System::s_pxAchievementData(0);


void Asura_Xbox360_Achievements_System::Initialise()
{
	s_pxAchievementData = new char[ XACHIEVEMENT_SIZE_FULL * Project_GetNumAchievements()];
	s_pxWorkerThread = new Asura_ProducerConsumerQueue(8, 1, ASURA_MULTITHREAD_STACK_SIZE_SMALL);
	Reset();
}

void Asura_Xbox360_Achievements_System::Shutdown()
{
	// NY - 19 March 2009 - do this first, as it won't return until the thread is killed, meaning we're safe to shut the sdk stuff down.
	delete s_pxWorkerThread;
	s_pxWorkerThread = 0;

	delete [] s_pxAchievementData;
	s_pxAchievementData = 0;
}

void Asura_Xbox360_Achievements_System::Reset()
{
	s_bInitComplete = false;

	//Check if the user is signed in, and retrieve the current achievements
	//First we need to check if the user is signed in
	 XUSER_SIGNIN_STATE eState = XUserGetSigninState( Asura_Xbox360_OS::GetActivePlayerIndex() );
	
	if(eState == eXUserSigninState_NotSignedIn)
	{
		//The user is not signed in so we cannot proceed
		//dont prompt to login since that isnt the responsibility of this class
		return;
	}

	HANDLE xEnum	= 0;
	DWORD uBuffer	= 0;

	// Create enumerator for the default device
	DWORD uStatus;
	uStatus = XUserCreateAchievementEnumerator(	0,												// title
												Asura_Xbox360_OS::GetActivePlayerIndex(),						// user
												INVALID_XUID,									// use local signed in user
												XACHIEVEMENT_DETAILS_ALL,						// details flag
												0,												// starting achievement index
												Project_GetNumAchievements(),					// number of achievements to return
												&uBuffer,										// bytes needed
												&xEnum );										// handle to ach's

	if(uStatus != ERROR_SUCCESS)
	{
		Asura_Assert(false,"Failed to create achievement enumerator");
		return;
	}
	
	// Enumerate achievements
	if(s_pxAchievementData)
	{
		DWORD ulNumAchievements = 0;
		uStatus = XEnumerate( xEnum, s_pxAchievementData, XACHIEVEMENT_SIZE_FULL * Project_GetNumAchievements(), &ulNumAchievements, NULL );
		
		if(uStatus != ERROR_SUCCESS)
		{
			Asura_Assert(false,"Failed to enumerate achievements");
			return;
		}
		s_uNumAchievements = ulNumAchievements;
		Asura_Assert(Project_GetNumAchievements() == s_uNumAchievements, "unexpected number of achievements reported by sdk - is xlast in sync with code?");
		s_bInitComplete = true;
	}
	
	CloseHandle( xEnum );
}

bool Asura_Xbox360_Achievements_System::IsInitComplete()
{
	return s_bInitComplete;
}

void Asura_Xbox360_Achievements_System::UnlockAchievement(u_int uAchievement)
{
	if (s_bInitComplete && uAchievement != s_uInvalid_Achievement)
	{
		s_pxWorkerThread->AddToQueue(Threaded_Unlock, 0, uAchievement);
	}
}

bool Asura_Xbox360_Achievements_System::IsAchievementUnlocked(u_int uAchievement)
{
	if(s_pxAchievementData)
	{
		//Find this achievement in our data
		XACHIEVEMENT_DETAILS* pxEnumeratedAchievement = reinterpret_cast < XACHIEVEMENT_DETAILS* > (s_pxAchievementData);
		for(u_int u = 0; u < s_uNumAchievements; ++u )
		{
			if(pxEnumeratedAchievement[u].dwId == uAchievement)
			{
				//Found the achievement
				return (pxEnumeratedAchievement[u].dwFlags & XACHIEVEMENT_DETAILS_ACHIEVED) != 0;
			}
		}
	}
	return false;
}

void Asura_Xbox360_Achievements_System::Threaded_Unlock(void* pData, u_int uAchievement)
{
	XUSER_ACHIEVEMENT xNewAchievement;
	xNewAchievement.dwAchievementId = uAchievement;
	xNewAchievement.dwUserIndex = Asura_Xbox360_OS::GetActivePlayerIndex();

	// prepare for writing achievements
	HANDLE xEventComplete = CreateEvent( NULL, FALSE, FALSE, NULL );

	if( xEventComplete == NULL )
	{
		Asura_Assert(false,"Failed to create event");
		return;
	}

	XOVERLAPPED xOverlap;

	Asura_Mem::Set( &xOverlap, 0, sizeof(XOVERLAPPED) );
	xOverlap.hEvent = xEventComplete;

	// Write achievements
	DWORD uStatus = XUserWriteAchievements( 1, &xNewAchievement, &xOverlap );
	Asura_Assert( uStatus == ERROR_IO_PENDING, "XUserWriteAchievements should be pending" );

	uStatus = XGetOverlappedResult( &xOverlap, NULL, TRUE );
	Asura_Assert( uStatus == ERROR_SUCCESS, "Failed to write user achievements" );

	CloseHandle( xEventComplete );
}

#endif // #ifdef ASURA_USE_REWARDS
