// Asura_Xbox360_Profile_System.h
// Thomas Young - 09.10.08
// Copyright (C) 2008 Rebellion, All Rights Reserved.

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

#ifdef ASURA_USE_PRAGMA_ONCE
#pragma once
#endif

#ifndef ___ASURA_XBOX360_PROFILE_SYSTEM_H___
#define ___ASURA_XBOX360_PROFILE_SYSTEM_H___

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

#include "Core/Asura.h"

#include "Core/Asura_Multithread.h"
#include "Core/Threads/Asura_ProducerConsumerQueue.h"

// D E F I N E S /////////////////////////////////////////////////////////////

#define		ASURA_XBOX360_PROFILE_MAX_SAVE_SLOTS			8
#define		ASURA_XBOX360_PROFILE_SLOT_DISPLAY_TEXT_LENGTH	32

// E N U M E R A T I O N S ///////////////////////////////////////////////////

enum Asura_Xbox360_Profile_State
{
	ASURA_XBOX360_PROFILE_STATE_IDLE,						// doing nothing
	ASURA_XBOX360_PROFILE_STATE_PROCESSING,					// doing something, e.g. picking a device
	ASURA_XBOX360_PROFILE_STATE_LOAD_PROFILE,				// loading profile
	ASURA_XBOX360_PROFILE_STATE_SAVE_PROFILE,				// saving profile
	ASURA_XBOX360_PROFILE_STATE_LOAD_CHECKPOINT,			// loading checkpoint
	ASURA_XBOX360_PROFILE_STATE_SAVE_CHECKPOINT,			// saving checkpoint
	ASURA_XBOX360_PROFILE_STATE_ERROR,						// there was an error
	ASURA_XBOX360_PROFILE_STATE_QUERY						// a query is pending
};

enum Asura_Xbox360_Profile_Error_State
{
	ASURA_XBOX360_PROFILE_ERROR_NONE,						// No error
	ASURA_XBOX360_PROFILE_ERROR_NO_PROFILE_SELECTED,		// The user did not select a gamer profile
	ASURA_XBOX360_PROFILE_ERROR_NO_DEVICE_SELECTED,			// The user did not select a storage device
	ASURA_XBOX360_PROFILE_ERROR_GENERIC_DEVICE_FAILURE,		// Some sort of error accessing the device
	ASURA_XBOX360_PROFILE_ERROR_CORRUPT,					// The data is corrupt
	ASURA_XBOX360_PROFILE_ERROR_WRONG_USER,					// content was created by a different user
	ASURA_XBOX360_PROFILE_ERROR_LOGOUT,						// The user logged out of their profile
	ASURA_XBOX360_PROFILE_ERROR_DEVICE_REMOVED,				// The active storage device was removed
};

enum Asura_Xbox360_Profile_Query_State
{
	ASURA_XBOX360_PROFILE_QUERY_NONE,
	ASURA_XBOX360_PROFILE_QUERY_DEVICE_SELECT,				// There was a problem with the selected device, pick a new one?
	ASURA_XBOX360_PROFILE_QUERY_CORRUPT,					// The data on the device is corrupt
};

enum Asura_Xbox360_Profile_QueryResponse
{
	ASURA_XBOX360_PROFILE_QUERY_RESPONSE_NONE,

	ASURA_XBOX360_PROFILE_QUERY_DEVICE_SELECT_YES,			// Yes, select another device
	ASURA_XBOX360_PROFILE_QUERY_DEVICE_SELECT_NO,			// No, ignore this error

	ASURA_XBOX360_PROFILE_QUERY_CORRUPT_OVERWRITE,			// Overwrite the corrupt data
	ASURA_XBOX360_PROFILE_QUERY_CORRUPT_SELECT_DEVICE,		// Select a different device
	ASURA_XBOX360_PROFILE_QUERY_CORRUPT_IGNORE,				// Continue without a device
};

// C L A S S   D E C L A R A T I O N S ///////////////////////////////////////

class Asura_Xbox360_Profile_System
{
	public:
	
		//------------------ Functions -----------------------------

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

		static void									SetBaseFilename			( const char* const szFilename, const Asura_UnicodeChar* const szDisplayName );
		
		static void									BeginProfileFlow		( void* const pData, const u_int uDataSize );
		static void									SelectStorageDevice		( void* const pData, const u_int uDataSize );
			
		static Asura_Xbox360_Profile_State			GetState				( );
		static bool									HasValidGamerProfile	( );
		static bool									HasValidStorageDevice	( );
		static int									GetActiveStorageDevice();	// Some other systems may need to make use of the current device, so make it available. The outside system should check HasValidStorageDevice() first.

		static void									SaveProfileData			( const void* const pData, const u_int uDataSize );
		static void									LoadProfileData			( const void* const pData, const u_int uDataSize );
		
		static void 								SaveCheckpoint			( const int iSlot, const void* const pData, const u_int uDataSize, const Asura_UnicodeChar* const szDisplayName );
		static void 								LoadCheckpoint			( const int iSlot, void* const pData, const u_int uDataSize );
		static const Asura_UnicodeChar*				GetSlotDisplayText		( const int iSlot );
		static bool									SlotContainsData		( const int iSlot );

		static Asura_Xbox360_Profile_Query_State	GetPendingQuery			( );
		static void									SetQueryResponse		( const Asura_Xbox360_Profile_QueryResponse eResponse );

		static Asura_Xbox360_Profile_Error_State	GetErrorState			( );
		static void									ClearErrorState			( );

		static void									SetUIOpen				( const bool bOpen );

		static void									GetFullState			( Asura_Xbox360_Profile_State&			eSystemState
																			, Asura_Xbox360_Profile_Query_State&	eQueryState
																			, Asura_Xbox360_Profile_Error_State&	eErrorState );

		static void									StorageDevicesChanged	( );

		static bool									IsDeviceStillValid		( );

	private:

		//------------------ Structures ----------------------------

		struct LoadSaveThreadData
		{
			bool			bInUse;
			void*			pData;
			u_int			uDataSize;
			int				iSlot;
		};

		struct SaveDataSlot
		{
			char szFilename		[XCONTENT_MAX_FILENAME_LENGTH + 1];
			WCHAR szDisplayName	[XCONTENT_MAX_DISPLAYNAME_LENGTH];
		};
		
		//------------------ Functions -----------------------------

		static void									SetState					( const Asura_Xbox360_Profile_State			eState		);
		static void									SetErrorState				( const Asura_Xbox360_Profile_Error_State	eErrorState );
		static void									SetQueryState				( const Asura_Xbox360_Profile_Query_State	eQueryState );

		static void									Threaded_BeginProfileFlow	( void* pData, u_int uDataSize );
		static void									Threaded_SelectDevice		( void* pData, u_int uDataSize );

		static void									Threaded_LoadProfileData	( void* pData, u_int uDataSize );
		static void									Threaded_SaveProfileData	( void* pData, u_int uDataSize );

		static void									Threaded_LoadCheckpoint		( void* pData, u_int uDataSize );
		static void									Threaded_SaveCheckpoint		( void* pData, u_int uDataSize );

		static bool									PromptForUser				( );
		static bool									PromptForDevice				( int iFreeSpaceRequired = 0 );

		static void									EnumerateSaveSlots			( );

		static bool									IsUIOpen					( )							{	return s_bUIOpen;	}
		
		static void									IgnoreUINotification		( const bool bIgnore )		{ s_bIgnoreUINotification = bIgnore;	}
		static bool									ShouldIgnoreUINotification	( )							{ return s_bIgnoreUINotification;		}

		static void									WaitForOverlapped			( const XOVERLAPPED* const pxOverlap );
		static void									WaitUntilTime				( const float fServerTime );

		static void									SetActivePlayer				( int iPlayerIndex );
		static void									SetActiveDevice				( int iDevice );

		static bool									GetUserXUIDs				( XUID& xOffline, XUID& xOnline );

		//------------------ Variables -----------------------------

		static Asura_Mutex							s_xMutex;
		static Asura_Xbox360_Profile_Error_State	s_eErrorState;
		static Asura_Xbox360_Profile_State			s_eSystemState;
		static Asura_Xbox360_Profile_Query_State	s_eQueryState;

		static char*								s_szBaseFilename;
		static Asura_UnicodeChar*					s_szBaseDisplayName;

		static Asura_ProducerConsumerQueue			s_xThreadedOps;
		static LoadSaveThreadData					s_xThreadData;

		static volatile bool						s_bUIOpen;
		static volatile bool						s_bIgnoreUINotification;

		static XUID									s_xActivePlayerXUID[2];
		static volatile int							s_iActivePlayer;
		static volatile int							s_iActiveDevice;

		static SaveDataSlot							s_xSlotEntries[ASURA_XBOX360_PROFILE_MAX_SAVE_SLOTS];

		static void*								s_pDataPointerBackup;
		static u_int								s_uDataSizeBackup;
};

#endif	// ___ASURA_XBOX360_PROFILE_SYSTEM_H___

//eof
