///////////////////////////////////////////////////////////////////////////////
///////////////////////////////////////////////////////////////////////////////
#ifndef __SC_H__
#define __SC_H__


///////////////////////////////////////////////////////////////////////////////
///////////////////////////////////////////////////////////////////////////////
#include "../common/gsCommon.h"
#include "../common/gsRC4.h"
#include "../common/gsAvailable.h"
#include "../ghttp/ghttp.h"
#include "../webservices/AuthService.h"


#ifdef __cplusplus
extern "C" {
#endif // __cplusplus

// optional to explicitly use __stdcall, __cdecl, __fastcall
#define SC_CALL


///////////////////////////////////////////////////////////////////////////////
///////////////////////////////////////////////////////////////////////////////
// Set this to define memory settings for the SDK
#define SC_STATIC_MEM

// The initial (or fixed, for static memory) report buffer size
#define SC_REPORT_BUFFER_BYTES 65536

// URL for sc services.
#define SC_SERVICE_MAX_URL_LEN 128
extern char scServiceURL[SC_SERVICE_MAX_URL_LEN];

// Session GUID size - must match backend
//#define SC_SESSION_GUID_SIZE 16
#define SC_AUTHDATA_SIZE 16
#define SC_SESSION_GUID_SIZE 40
#define SC_CONNECTION_GUID_SIZE 40

#define SC_GUID_BINARY_SIZE 16 // convert the 40 byte string guid into an int, 2 shorts and 8 bytes

// Limit to the number of teams
#define SC_MAX_NUM_TEAMS	64

// OPTIONS flags - first two bits reserved for authoritative / final flags
#define SC_OPTIONS_NONE					0


///////////////////////////////////////////////////////////////////////////////
///////////////////////////////////////////////////////////////////////////////
// Result codes
typedef enum
{
	SCResult_NO_ERROR = 0,
	SCResult_NO_AVAILABILITY_CHECK,
	SCResult_INVALID_PARAMETERS,
	SCResult_NOT_INITIALIZED,
	SCResult_CORE_NOT_INITIALIZED,
	SCResult_OUT_OF_MEMORY,
	SCResult_CALLBACK_PENDING,

	SCResult_HTTP_ERROR,
	SCResult_UNKNOWN_RESPONSE, // server reported an error which is unknown to us
	SCResult_RESPONSE_INVALID,

	SCResult_REPORT_INCOMPLETE,
	SCResult_REPORT_INVALID,
	SCResult_SUBMISSION_FAILED,

	SCResult_UNKNOWN_ERROR,

	SCResultMax
} SCResult;


// Game Results
typedef enum
{
	SCGameResult_WIN,
	SCGameResult_LOSS,
	SCGameResult_DRAW,
	SCGameResult_DISCONNECT,
	SCGameResult_DESYNC,
	SCGameResult_NONE,

	SCGameResultMax
} SCGameResult;


// Game Status
typedef enum
{
	SCGameStatus_COMPLETE,
	SCGameStatus_PARTIAL,
	SCGameStatus_BROKEN,

	SCGameStatusMax
} SCGameStatus;


///////////////////////////////////////////////////////////////////////////////
///////////////////////////////////////////////////////////////////////////////
// Data types
typedef void*   SCInterfacePtr;
typedef void*   SCReportPtr;


//typedef gsi_u32 SCTeamCount;
//typedef gsi_u32 SCTeamIndex;

//typedef gsi_u32 SCPlayerCount;
//typedef gsi_u32 SCPlayerIndex;

//typedef gsi_u16 SCKey;

typedef char    SCHiddenData[64];

//typedef enum 
//{
//	SCReportKeyType_SERVER,
//	SCReportKeyType_TEAM,
//	SCReportKeyType_PLAYER
//} SCReportKeyType;


///////////////////////////////////////////////////////////////////////////////
///////////////////////////////////////////////////////////////////////////////
// Callbacks
typedef void (*SCCreateSessionCallback)(const SCInterfacePtr theInterface,
										GHTTPResult          theHttpResult,
										SCResult             theResult,
										void *               theUserData);
typedef void (*SCSetReportIntentionCallback)(const SCInterfacePtr theInterface,
									         GHTTPResult          theHttpResult,
                                             SCResult             theResult,
											 void *	              theUserData);
typedef void (*SCSubmitReportCallback)(const SCInterfacePtr theInterface,
									   GHTTPResult          theHttpResult,
									   SCResult             theResult,
									   void *               theUserData);

/*
typedef void (*SCKeyCallback) (SCReportPtr theReport,
							   SCReportKeyType theKeyType,
							   gsi_u32 theIndex, // how many times this has been called
							   void* theUserParam);
*/

///////////////////////////////////////////////////////////////////////////////
///////////////////////////////////////////////////////////////////////////////
// Main interface functions
SCResult SC_CALL scInitialize(int theGameId,
							  SCInterfacePtr * theInterfaceOut);
SCResult SC_CALL scShutdown  (SCInterfacePtr theInterface);
SCResult SC_CALL scThink     (SCInterfacePtr theInterface);

SCResult SC_CALL scCreateSession(SCInterfacePtr             theInterface,
                                 const GSLoginCertificate * theCertificate,
								 const GSLoginPrivateData * thePrivateData,
								 SCCreateSessionCallback    theCallback,
								 gsi_time                   theTimeoutMs,
								 void *                     theUserData);

// This is a variation of scCreateSession that creates a "matchless" session.
// "matchless" means incoming data will be scrutinized less, and applied to stats immediately instead of when the match is over.
SCResult SC_CALL scCreateMatchlessSession(SCInterfacePtr    theInterface,
								 const GSLoginCertificate * theCertificate,
								 const GSLoginPrivateData * thePrivateData,
								 SCCreateSessionCallback    theCallback,
								 gsi_time                   theTimeoutMs,
								 void *                     theUserData);

SCResult SC_CALL scSetReportIntention(const SCInterfacePtr         theInterface,
									  gsi_bool                     isAuthoritative,
  							          const GSLoginCertificate *   theCertificate,
							          const GSLoginPrivateData *   thePrivateData,
							          SCSetReportIntentionCallback theCallback,
							          gsi_time                     theTimeoutMs,
									  void *                 theUserData);

SCResult SC_CALL scSubmitReport      (const SCInterfacePtr       theInterface,
									  const SCReportPtr          theReport,
									  gsi_bool                   isAuthoritative,
									  const GSLoginCertificate * theCertificate,
									  const GSLoginPrivateData * thePrivateData,
                                      SCSubmitReportCallback     theCallback,
                                      gsi_time                   theTimeoutMs,
									  void *                     theUserData);

//SCResult SC_CALL sc
SCResult SC_CALL scSetSessionId(const SCInterfacePtr theInterface, const gsi_u8 theSessionId[SC_SESSION_GUID_SIZE]);

const char * scGetSessionId   (const SCInterfacePtr theInterface);
const char * scGetConnectionId(const SCInterfacePtr theInterface);


///////////////////////////////////////////////////////////////////////////////
///////////////////////////////////////////////////////////////////////////////
// Report generation functions

// Create a new (empty) report
//    - Specify player and team count so we can allocate memory
//      and later sanity check against reported data
SCResult SC_CALL scCreateReport(const SCInterfacePtr theInterface, 
								gsi_u32              theHeaderVersion, 
								gsi_u32              thePlayerCount,
								gsi_u32              theTeamCount,
								SCReportPtr *  theReportOut);

//    - Write global data key/values
SCResult SC_CALL scReportBeginGlobalData(SCReportPtr theReportData);
SCResult SC_CALL scReportBeginPlayerData(SCReportPtr theReportData);
SCResult SC_CALL scReportBeginTeamData  (SCReportPtr theReportData);


//    - Write player auth info and key/values
SCResult SC_CALL scReportBeginNewPlayer(SCReportPtr  theReportData);
SCResult SC_CALL scReportSetPlayerData (SCReportPtr  theReport,
									    gsi_u32            thePlayerIndex,
									    const gsi_u8       thePlayerConnectionId[SC_CONNECTION_GUID_SIZE],
									    gsi_u32            thePlayerTeamId,
									    SCGameResult       theResult,
									    gsi_u32            theProfileId,
									    const GSLoginCertificate * theCertificate,
									    const gsi_u8       theAuthData[16]);

//     - Write team info and key/values
SCResult SC_CALL scReportBeginNewTeam(SCReportPtr theReportData);
SCResult SC_CALL scReportSetTeamData (SCReportPtr theReport,
									  gsi_u32           theTeamId,
									  SCGameResult      theResult);

//     - Call this when you're finished writing the report
SCResult SC_CALL scReportEnd(SCReportPtr theReport, 
							 gsi_bool    isAuth, 
							 SCGameStatus theStatus);

// Call this to set the report as "matchless".
// This is needed if the report is being submitted to a "matchless" game session.
SCResult SC_CALL scReportSetAsMatchless(SCReportPtr theReport);


// Utility to record key value pairs
SCResult SC_CALL scReportAddIntValue(SCReportPtr theReportData,
									 gsi_u16           theKeyId,
									 gsi_i32           theValue);
SCResult SC_CALL scReportAddShortValue(SCReportPtr theReportData,
									 gsi_u16           theKeyId,
									 gsi_i16           theValue);
SCResult SC_CALL scReportAddByteValue(SCReportPtr theReportData,
									 gsi_u16           theKeyId,
									 gsi_i8            theValue);
SCResult SC_CALL scReportAddFloatValue(SCReportPtr theReportData,
									 gsi_u16           theKeyId,
									 float             theValue);
SCResult SC_CALL scReportAddStringValue(SCReportPtr theReportData,
										gsi_u16           theKeyId,
										const gsi_char *  theValue);
SCResult SC_CALL scDestroyReport(SCReportPtr theReport);

///////////////////////////////////////////////////////////////////////////////
///////////////////////////////////////////////////////////////////////////////
// Peer to peer encryption utilities (Will probably be moved to common code)

//     A symmetric cipher key for peer-to-peer communication
//     Will usually have one key for sending and a second key for receiving
typedef struct SCPeerCipher
{
	RC4Context mRC4;
	gsi_u8  mKey[GS_CRYPT_RSA_BYTE_SIZE];
	gsi_u32 mKeyLen;
	gsi_bool mInitialized;
} SCPeerCipher;

typedef char SCPeerKeyExchangeMsg[GS_CRYPT_RSA_BYTE_SIZE];

SCResult SC_CALL scPeerCipherInit(const GSLoginCertificate * theLocalCert, SCPeerCipher * theCipher);

SCResult SC_CALL scPeerCipherCreateKeyExchangeMsg(const GSLoginCertificate * theRemoteCert,
												  const SCPeerCipher *       theCipher, 
												  SCPeerKeyExchangeMsg       theMsgOut);

SCResult SC_CALL scPeerCipherParseKeyExchangeMsg (const GSLoginCertificate * theLocalCert,  
												  const GSLoginPrivateData * theCertPrivateData, 
												  const SCPeerKeyExchangeMsg theMsg, 
												  SCPeerCipher *             theCipherOut);

// Encrypt/Decrypt in place, also the RC4 context is modified everytime encryption/decryption take place
SCResult SC_CALL scPeerCipherEncryptBuffer(SCPeerCipher * theCipher, gsi_u8 * theData, gsi_u32 theLen);
SCResult SC_CALL scPeerCipherDecryptBuffer(SCPeerCipher * theCipher, gsi_u8 * theData, gsi_u32 theLen);

// When using UDP (non-ordered) you must supply a message num
//    - This is less efficient then ecrypting an ordered stream
SCResult SC_CALL scPeerCipherEncryptBufferIV(SCPeerCipher * theCipher, gsi_u32 theMessageNum, gsi_u8 * theData, gsi_u32 theLen);
SCResult SC_CALL scPeerCipherDecryptBufferIV(SCPeerCipher * theCipher, gsi_u32 theMessageNum, gsi_u8 * theData, gsi_u32 theLen);


///////////////////////////////////////////////////////////////////////////////
///////////////////////////////////////////////////////////////////////////////

#ifdef __cplusplus
} // extern "C"
#endif // __cplusplus


///////////////////////////////////////////////////////////////////////////////
///////////////////////////////////////////////////////////////////////////////
#endif // __SC_H__
