#define CRYPTOKI_EXPORTS

#include <winscard.h>
#include <vector>
#include <iostream>

#include "cryptoki.h"
#include "TokenTypeList.h"
#include "SlotList.h"

#ifndef WIN32
#ifndef Sleep
#include <time.h>

void Sleep(unsigned int miliseconds)
{
        struct timespec time = { miliseconds/1000 , miliseconds%1000 };
        nanosleep(&time,&time);
}
#endif
#endif

extern CK_ULONG g_ulGlobalFlags;
extern CK_ULONG g_ulThreadsWaiting;

CK_FUNCTION_LIST g_ckFunctionList =
{
	{2,20},
	C_Initialize,
	C_Finalize,
	C_GetInfo,
	C_GetFunctionList,
	C_GetSlotList,
	C_GetSlotInfo,
	C_GetTokenInfo,
	C_GetMechanismList,
	C_GetMechanismInfo,
	C_InitToken,
	C_InitPIN,
	C_SetPIN,
	C_OpenSession,
	C_CloseSession,
	C_CloseAllSessions,
	C_GetSessionInfo,
	C_GetOperationState,
	C_SetOperationState,
	C_Login,
	C_Logout,
	C_CreateObject,
	C_CopyObject,
	C_DestroyObject,
	C_GetObjectSize,
	C_GetAttributeValue,
	C_SetAttributeValue,
	C_FindObjectsInit,
	C_FindObjects,
	C_FindObjectsFinal,
	C_EncryptInit,
	C_Encrypt,
	C_EncryptUpdate,
	C_EncryptFinal,
	C_DecryptInit,
	C_Decrypt,
	C_DecryptUpdate,
	C_DecryptFinal,
	C_DigestInit,
	C_Digest,
	C_DigestUpdate,
	C_DigestKey,
	C_DigestFinal,
	C_SignInit,
	C_Sign,
	C_SignUpdate,
	C_SignFinal,
	C_SignRecoverInit,
	C_SignRecover,
	C_VerifyInit,
	C_Verify,
	C_VerifyUpdate,
	C_VerifyFinal,
	C_VerifyRecoverInit,
	C_VerifyRecover,
	C_DigestEncryptUpdate,
	C_DecryptDigestUpdate,
	C_SignEncryptUpdate,
	C_DecryptVerifyUpdate,
	C_GenerateKey,
	C_GenerateKeyPair,
	C_WrapKey,
	C_UnwrapKey,
	C_DeriveKey,
	C_SeedRandom,
	C_GenerateRandom,
	C_GetFunctionStatus,
	C_CancelFunction,
	C_WaitForSlotEvent,
	C_UnblockPIN
};

SCARDCONTEXT g_hContext = NULL;

CTokenTypeList & g_rTokenTypeList = CTokenTypeList::getInstance();

/* C_Initialize initializes the Cryptoki library. */
CK_DEFINE_FUNCTION(CK_RV, C_Initialize)
(
  CK_VOID_PTR   pInitArgs  /* if this is not NULL_PTR, it gets
                            * cast to CK_C_INITIALIZE_ARGS_PTR
                            * and dereferenced */
)
{
	CK_RV lReturn = CKR_OK;
	LONG lResult;
	DBG_TRACE(("C_Initialize(0x%08x)...\n",pInitArgs));

	if ((g_ulGlobalFlags & FLAG_LIBRERIA_INICIALIZADA))
		return (CKR_CRYPTOKI_ALREADY_INITIALIZED);

	#if defined WIN32 && defined _DEBUG
		_CrtSetDbgFlag ( _CRTDBG_ALLOC_MEM_DF | _CRTDBG_LEAK_CHECK_DF );
	#endif

	lResult = SCardEstablishContext(SCARD_SCOPE_USER, NULL, NULL, &g_hContext);

	if (SCARD_S_SUCCESS != lResult)
	{
	    DBG_TRACE(("ERROR: SCardEstablishContext()=0x%08x\n",lResult));
		lReturn = CSlot::PCSCErr2P11Err(lResult);
	}

	g_ulGlobalFlags |= 1;

	DBG_TRACE(("C_Initialize()=0x%08x\n",lReturn));

	return lReturn;
}


/* C_Finalize indicates that an application is done with the
 * Cryptoki library. */
CK_DEFINE_FUNCTION(CK_RV, C_Finalize)
(
  CK_VOID_PTR   pReserved  /* reserved.  Should be NULL_PTR */
)
{
	DBG_TRACE(("C_Finalize(0x%08x)...\n",pReserved));

	if (!(g_ulGlobalFlags & FLAG_LIBRERIA_INICIALIZADA))
		return (CKR_CRYPTOKI_NOT_INITIALIZED);

	// Dejamos indicado que la librería no ha sido inicializada
	g_ulGlobalFlags &= ~(FLAG_LIBRERIA_INICIALIZADA);

	// Si hay algún WaitForSlotEvent en espera, le daremos tiempo para finalizar
	if(g_ulThreadsWaiting>0)
		Sleep(1100);

	return CKR_OK;
}


/* C_GetInfo returns general information about Cryptoki. */
CK_DEFINE_FUNCTION(CK_RV, C_GetInfo)
(
  CK_INFO_PTR   pInfo  /* location that receives information */
)
{
	DBG_TRACE(("C_GetInfo(0x%08x)...\n",pInfo));

	if (!(g_ulGlobalFlags & FLAG_LIBRERIA_INICIALIZADA))
		return (CKR_CRYPTOKI_NOT_INITIALIZED);

	pInfo->cryptokiVersion.major = 2;
	pInfo->cryptokiVersion.minor = 20;
	memset(&pInfo->manufacturerID,0x20,sizeof(pInfo->manufacturerID));
	memcpy(&pInfo->manufacturerID,"FNMT-RCM",8);
	pInfo->flags = 0;
	memset(&pInfo->libraryDescription,0x20,sizeof(pInfo->libraryDescription));
	memcpy(&pInfo->libraryDescription,"FNMT-RCM",8);
	pInfo->libraryVersion.major = 1;
	pInfo->libraryVersion.minor = 30;

	DBG_TRACE(("C_GetInfo()=0x%08x\n",CKR_OK));
	return CKR_OK;
}


/* C_GetFunctionList returns the function list. */
CK_DEFINE_FUNCTION(CK_RV, C_GetFunctionList)
(
  CK_FUNCTION_LIST_PTR_PTR ppFunctionList  /* receives pointer to
                                            * function list */
)
{
	DBG_TRACE(("C_GetFunctionList(0x%08x)...\n",ppFunctionList));

	* ppFunctionList = &g_ckFunctionList;

	DBG_TRACE(("C_GetFunctionList()=0x%08x\n",CKR_OK));
	return CKR_OK;
}

