#if defined WIN32
	#include <windows.h>
	#include <stdio.h>
	#define	DEFAULT_LIB_NAME	"FNMT_P11.dll"

	#define _CRTDBG_MAP_ALLOC
	#include <stdlib.h>
	#include <crtdbg.h>
#else
	#include <stdio.h>
	#include <dlfcn.h>
	#include <stdlib.h>
	#include <ctype.h>

	#define HINSTANCE		void * 
	#define	DEFAULT_LIB_NAME	"libpkcs11-fnmt.so"
#endif

#if defined STATIC_P11
	#define CK_IMPORT_SPEC
#endif

#include "cryptoki.h"

void	sintax(void)
{
	printf( "test_c3po [options] [<p11_library_filename>]\n"
			"\t-v\t\tShow message of diferents test.\n"
			"\t-a\t\tCheck all test\n"
			"\t-c\t\tTest1: Check p11 is compiled with a correct TokenType.\n"
			"\t-i\t\tTest2: Get info of Slots and Tokens.\n"
			"\t-d\t\tTest3: Check dialog message.\n"
			"\t-s\t\tTest4: Check session card.\n" );
	exit(-1);
}

int main(int argc, char **argv)
{
	HINSTANCE lib_handle = 0;
	CK_RV (*pC_GetFunctionList)(CK_FUNCTION_LIST_PTR_PTR);
	long rv;

	bool	optCompile = false;
	bool	optInfo = false;
	bool	optDialog = false;
	bool	optSession = false;
	bool	optVerbose = false;
	char	* libName = NULL;
	int	slotIdx = -1;

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

	for( int idx = 1; idx<argc; ++idx ){
		if( argv[idx][0] == '-' ){
			switch( tolower(argv[idx][1]) )	{
			case 'a':
				optCompile = true;
				optInfo = true;
				optDialog = true;
				optSession = true;
				break;
			case 'c':
				optCompile = true;
				break;
			case 'i':
				optInfo = true;
				break;
			case 'd':
				optDialog = true;
				break;
			case 's':
				optSession = true;
				break;
			case 'v':
				optVerbose = true;
				break;
			default:
				sintax();
				exit(1);
			}
		}else if( !libName ){
			libName = argv[idx];
		}else{
			sintax();
			exit(1);
		}
	}
	
	if( !libName )
		libName = (char*) DEFAULT_LIB_NAME;

	if( optCompile ){
		//if( optVerbose ) printf("------------- Test correct P11 compilation ----------------------\n");
		printf("\nStep 1: Check p11 is compiled with a correct TokenType ... ");
		if( optVerbose ) printf("\nP11 selected: %s\n", libName);
		#if defined _CFG_DNI_
			if( optVerbose ) printf("Load cfg_DNIe.h ... ");
			char * name = "dnie.";
			if( (strstr(libName, name) == NULL) ){
				if( optVerbose ) printf("\nError on p11 compilation.\n");
				exit(1);
			}else{
				if( optVerbose ) printf("Ok\n");
			}
		#elif defined _CFG_TIF_REAL_ 
			if( optVerbose ) printf("Load cfg_TIF_real.h ... ");
			char * name = "tif";
			if( (strstr(libName, name) == NULL) ){
				if( optVerbose ) printf("\nError on p11 compilation.\n");
				exit(1);
			}else{
				if( optVerbose ) printf("Ok\n");
			}

		#elif defined _CFG_FNMT_CERES_
			if( optVerbose ) printf("Load cfg_FNMT_Ceres ... ");
			char * name = "fnmt.";
			if( (strstr(libName, name) == NULL) ){
				if( optVerbose ) printf("\nError on p11 compilation.\n");
				exit(1);
			}else{
				if( optVerbose ) printf("Ok\n");
			}
			optDialog = false;
		#else
			if( optVerbose ) printf("Load cfg_FNMT_Real ... ");
			char * name = "fnmtdnie.";
			if( (strstr(libName, name) == NULL) ){
				if( optVerbose ) printf("\nError on p11 compilation.\n");
				exit(1);
			}else{
				if( optVerbose ) printf("Ok\n");
			}
		#endif
		printf("Ok\n\n");
		//if( optVerbose ) printf("----------------------------------------------------------------\n\n");
	}

	if(!optInfo && !optDialog && !optSession) exit(1);

#if defined STATIC_P11
	pC_GetFunctionList = &C_GetFunctionList;
#elif defined WIN32
	if( optVerbose ) printf( "Loading library %s ... ", libName);
	lib_handle = LoadLibrary(libName);

	if (!lib_handle){
		printf( "\nLibrary %s not found.\n", libName);
		exit(1);
	}

	pC_GetFunctionList = (CK_RV (*)(CK_FUNCTION_LIST_PTR_PTR))GetProcAddress( lib_handle, "C_GetFunctionList" );
	if( pC_GetFunctionList == NULL ){
		printf( "\nFunction C_GetFunctionList not found in library %s\n\n", libName);
		exit(1);
	}

	if( optVerbose ) printf( "Ok\n", libName);
#else		// Linux
	if( optVerbose ) printf( "Loading library %s ... ", libName);
	lib_handle = dlopen(libName, RTLD_LAZY);

	if (!lib_handle){
		printf( "\n%s\n", dlerror());
		exit(1);
	}

	pC_GetFunctionList = (CK_RV (*)(CK_FUNCTION_LIST_PTR_PTR))dlsym(lib_handle, "C_GetFunctionList");

	char *error;
	if ((error = dlerror()) != NULL){
		printf( "\n%s\n", error);
		exit(1);
	}

	CK_RV (*pAskUserAuth)();
	pAskUserAuth = (CK_RV (*)())dlsym(lib_handle, "AskUserAuth");

	//char *error2;
	if ((error = dlerror()) != NULL){
		printf( "\n%s\n", error);
		exit(1);
	}

	if( optVerbose ) printf( "Ok\n", libName);
#endif


	if( optVerbose ) printf( "Call C_GetFunctionList() ... " );

	CK_FUNCTION_LIST_PTR	pFunctionList;

	rv = (*pC_GetFunctionList)(&pFunctionList);

	if( rv != CKR_OK ){
		printf( "\nError 0x%08x when calling C_GetFunctionList()\n\n", rv);
		exit(1);
	}
	if( optVerbose ) printf( "Ok\n" );


	if( optVerbose ) printf( "Call C_Initialize() ... " );
	rv = pFunctionList->C_Initialize(NULL);
	if( rv != CKR_OK ){
		printf( "\nError 0x%08x when calling C_Initialize()\n\n", rv);
		exit(1);
	}
	if( optVerbose ) printf( "Ok\n" );


	if( optVerbose ) printf( "Call C_GetSlotList() ... " );
	CK_ULONG	numSlots;
	rv = pFunctionList->C_GetSlotList(false,NULL,&numSlots);
	if( rv != CKR_OK ){
		printf( "\nError 0x%08x when calling C_GetSlotList()\n\n", rv);
		exit(1);
	}
	if( optVerbose ) printf( "Ok\n" );
	if( optVerbose ) printf( "Found %d slots ...\n", numSlots );

	CK_SLOT_ID	*	pSlotsID = new CK_SLOT_ID[numSlots];
	rv = pFunctionList->C_GetSlotList(false,pSlotsID,&numSlots);
	if( rv != CKR_OK ){
		printf( "\nError 0x%08x when calling C_GetSlotList()\n\n", rv);
		exit(1);
	}
	for( CK_ULONG idx = 0; idx<numSlots; ++idx ){
		if( optVerbose ) printf( "\tSlot %2d - slotID = 0x%08x\n", idx, pSlotsID[idx] );
	}
	if( optVerbose ) printf( "\n" );

	//
	// Read info and open session of slots and tokens
	//

	for( CK_ULONG idx = 0; idx<numSlots; ++idx ){
		char		tmp[100];
		CK_SLOT_INFO	slotInfo;
		CK_TOKEN_INFO	tokenInfo;

		if( optInfo ){
		//if( optVerbose ) printf("------------- Test of GetSlotInfo and GetTokenInfo -------------\n");
		printf( "\nStep 2: Get info of Slot and Info ... " );
		if( optVerbose ) printf( "\nCall C_GetSlotInfo() for slotID 0x%08x...\n", pSlotsID[idx] );

		rv = pFunctionList->C_GetSlotInfo(pSlotsID[idx],&slotInfo);
		if( rv != CKR_OK ){
			printf( "\nError 0x%08x when calling C_GetSlotInfo()\n\n", rv);
			continue;
		}

		memcpy( tmp, &slotInfo.slotDescription, sizeof(slotInfo.slotDescription) );
		tmp[sizeof(slotInfo.slotDescription)] = 0;
		if( optVerbose ) printf( "\tslotDescription = '%s'\n", tmp );

		memcpy( tmp, &slotInfo.manufacturerID, sizeof(slotInfo.manufacturerID) );
		tmp[sizeof(slotInfo.manufacturerID)] = 0;
		if( optVerbose ) printf( "\tmanufacturerID   = '%s'\n", tmp );

		if( optVerbose ) printf( "\thardwareVersion  = %d.%d\n", slotInfo.hardwareVersion.major, slotInfo.hardwareVersion.minor );
		if( optVerbose ) printf( "\tfirmwareVersion  = %d.%d\n", slotInfo.firmwareVersion.major, slotInfo.firmwareVersion.minor );
		if( optVerbose ) printf( "\tflags            = 0x%08x\n", slotInfo.flags );

		if( optVerbose ) printf( "Ok\n" );

		if(!(slotInfo.flags & CKF_TOKEN_PRESENT)){
			printf( "\nToken not present ... \n\n" );
			continue;
		}


		if( optVerbose ) printf( "Call C_GetTokenInfo() for slotID 0x%08x...\n", pSlotsID[idx] );

		rv = pFunctionList->C_GetTokenInfo(pSlotsID[idx],&tokenInfo);
		if( rv != CKR_OK ){
			printf( "\nError 0x%08x when calling C_GetTokenInfo()\n\n", rv);
			continue;
		}

		memcpy( tmp, &tokenInfo.label, sizeof(tokenInfo.label) );
		tmp[sizeof(tokenInfo.label)] = 0;
		if( optVerbose ) printf( "\tlabel          = '%s'\n", tmp );

		memcpy( tmp, &tokenInfo.manufacturerID, sizeof(tokenInfo.manufacturerID) );
		tmp[sizeof(tokenInfo.manufacturerID)] = 0;
		if( optVerbose ) printf( "\tmanufacturerID = '%s'\n", tmp );

		memcpy( tmp, &tokenInfo.model, sizeof(tokenInfo.model) );
		tmp[sizeof(tokenInfo.model)] = 0;
		if( optVerbose ) printf( "\tmodel          = '%s'\n", tmp );

		memcpy( tmp, &tokenInfo.serialNumber, sizeof(tokenInfo.serialNumber) );
		tmp[sizeof(tokenInfo.serialNumber)] = 0;
		if( optVerbose ) printf( "\tserialNumber   = '%s'\n", tmp );

		if( optVerbose ) printf( "\tflags          = 0x%08x\n", tokenInfo.flags );
		printf( "Ok\n\n" );
		//if( optVerbose ) printf("----------------------------------------------------------------\n\n");
		}

#ifndef WIN32
		if( optDialog ){
		//if( optVerbose ) printf("------------- Test of AskUserAuth ----------------------------\n");
		printf( "\nStep 3: Try to open Dialog Message ... " );
		if( optVerbose ) printf( "\nCall AskUserAuth() ... " );
		rv = (*pAskUserAuth)();

		if(rv == CKR_CANCEL){
			if( optVerbose ) printf("Process are canceled.\n");
		}else if(rv == CKR_OK){
			if( optVerbose ) printf("Process are accepted.\n");
		}else{
			printf( "\nError 0x%08x when calling &pAskUserAuth()\n\n", rv);
		}
		printf("OK\n\n");
		//if( optVerbose ) printf("----------------------------------------------------------------\n\n");
		}
#endif

		if( optSession ){
		//if( optVerbose ) printf("------------- Test of OpenSession ------------------------------\n");
		CK_SESSION_HANDLE	hSession = 0;
		printf( "\nStep 4: Try to open session ... " );
		if( optVerbose ) printf( "\nCall C_OpenSession() for slotID 0x%08x ... ", pSlotsID[idx] );

		rv = pFunctionList->C_OpenSession(pSlotsID[idx],CKF_SERIAL_SESSION,NULL,NULL,&hSession);
		if( rv != CKR_OK ){
			printf( "\nError 0x%08x when calling C_OpenSession()\n\n", rv);
			continue;
		}
		if( optVerbose ) printf( "Ok\n" );

		if( hSession ){
			if( optVerbose ) printf( "Call C_CloseSession() for hSession 0x%08x ... ", hSession );
			rv = pFunctionList->C_CloseSession(hSession);
			if( rv != CKR_OK ){
				printf( "\nError 0x%08x when calling C_CloseSession()\n\n", rv);
				continue;
			}

			if( optVerbose ) printf( "Ok\n" );
		}
		printf("Ok\n\n");
		//if( optVerbose ) printf("----------------------------------------------------------------\n\n");
		}

	}


	if( optVerbose ) printf( "Call C_Finalize() ..." );
	rv = pFunctionList->C_Finalize(NULL);
	if( rv != CKR_OK ){
		printf( "\nError 0x%08x when calling C_Finalize()\n\n", rv);
	}
	if( optVerbose ) printf( "Ok\n" );

	if( pSlotsID )
		delete pSlotsID;

	#if defined STATIC_P11
	#elif defined WIN32
		FreeLibrary(lib_handle);
	#else		// Linux
		dlclose(lib_handle);
	#endif

	printf( "\nTest Ok\n\n" );

	return 0;
}
