#include "test_common.h"

CK_ULONG user_pin_len;

#ifdef WIN32
DWORD WINAPI
#else
int
#endif
thread_wait_func(void *thid)
{
	CK_RV  rv;
	CK_SLOT_ID SlotId;

	fprintf(stderr, "thread_wait_func started\n");
  
	while (1) {
		fprintf(stderr, 
			"Call C_WaitForSlotEvent in thread_wait_func:\n"
			"waiting for slot events with blocking...\n");
		rv = funcs->C_WaitForSlotEvent(0, &SlotId, NULL);
		fprintf(stderr, "C_WaitForSlotEvent returned 0x%X\n", rv);
		switch (rv) {
		case CKR_CRYPTOKI_NOT_INITIALIZED:
			fprintf(stderr, 
				"C_Finalize already called "
				"or token license error "
				"or C_Initialize not called yet\n");
			fprintf(stderr, "thread_wait_func finished\n");
			return rv;
		case CKR_OK:
			fprintf(stderr, 
				"The event occured on slot %d. "
				"Token removed or inserted\n", 
				SlotId);
			continue;
		default:
			fprintf(stderr, "Ignore that error...\n");
			continue;
		}
	}
}

int
main(int argc, char *argv[]){
	CK_RV rc;
	CK_SLOT_INFO sinfo;
	CK_FLAGS flags = 0;
	CK_ULONG i;
	CK_UTF8CHAR userpin[80];
#ifdef WIN32
	HANDLE			wid;
	DWORD	rv;
#else   
	pthread_t		wid;
#endif
	int wthid;
	char c = '\0';

	printf("C_WaitForSlotEvent test\n");
	for (i=1; i<(CK_ULONG)argc; i++) {
		if (strcmp("-api", argv[i]) == 0) {
			++i;
			api_path = argv[i];
		} else if (strcmp("-user_pin", argv[i]) == 0) {
			++i;
			memcpy(userpin, argv[i], strlen(argv[i]));
			user_pin = userpin;
			user_pin_len = strlen(argv[i]);
		}
	}
	fprintf(stderr, "==>> init...\n");
	// Use application-supplied multithreading callbacks for locking.
	rc = init(api_path, &funcs, pinit_args_app_locking);
	if (rc != CKR_OK) {
		fprintf(stderr, "==>> init failed\n\n");
		return rc;
	}
	fprintf(stderr, "==>> init Ok\n\n");

	rc = get_slot_list(funcs, 
                    &SlotList, 
                    &SlotCount);
	if (rc != CKR_OK) {
		printf("get_slot_list failed, rc = 0x%x\n", rc );
		return rc;
	}

	fprintf(stderr, "==>> creating thread for C_WaitForSlotEvent\n");
#ifdef WIN32
	wid = CreateThread(NULL, 0, 
		(LPTHREAD_START_ROUTINE)thread_wait_func, 
		(void *)&wthid, 0, NULL);
#else	   
	pthread_create(&wid, NULL, 
		(void*(*)(void *))thread_wait_func, (void *)&wthid);
#endif	

	// В основном потоке изображаем прикладную работу,
	// демонстрируя текущее состояние слотов и токенов.
	while (1) {
		for (i = 0; i<SlotCount; i++) {
			SlotId = SlotList[i];

			rc = display_slot_info(funcs, SlotId);
			if (rc != CKR_OK) {
				printf("display slot info failed\n");
				goto done;
			}

			rc = funcs->C_GetSlotInfo(i, &sinfo);
			if (rc != CKR_OK) {
				printf("C_GetSlotInfo failed\n");
				goto done;
			}
			if (sinfo.flags & CKF_TOKEN_PRESENT) {
				rc = display_token_info(funcs, SlotId);
				if (rc != CKR_OK) {
					printf("display_token_info failed\n");
					goto done;
				}
			}
		}
		printf("Press CTRL-C for exit, any other key to continue\n");
#ifndef WIN32    
		echo(FALSE);
		c = getc(stdin);
		echo(TRUE);
#else      
		c = _getch();
#endif	  
		if (c == END_OF_TEXT) 
		{
			// Input terminated (Ctrl-C)
			break;
		}
	}	// while(1)
done:
	// Здесь проверяется принудительное завершение блокировки  
	// в функции C_WaitForSlotEvent при финализации библиотеки.
	// Функция C_Finalize приказывает C_WaitForSlotEvent закончить 
	// работу и сама не не завершится, пока это не произойдет.
	fprintf(stderr, 
		"Finalizing library and killing C_WaitForSlotEvent...\n");
	funcs->C_Finalize(NULL);
#ifdef WIN32
	fprintf(stderr, "Waiting for thread_wait_func thread\n");
	rv = WaitForSingleObject(wid, INFINITE); 
	if (rv == WAIT_TIMEOUT) {
		fprintf(stderr, "Stop waiting by timeout\n");
	} else {
		fprintf(stderr, "Waiting thread completed\n");
	}
#else
	fprintf(stderr, "Joining thread_wait_func thread\n");
	pthread_join(wid, NULL);
	wthid = 0;
#endif
	printf("C_WaitForSlotEvent test SUCCESS\n");
	return CKR_OK;
}
