/*
 ============================================================================
 Name        : ad-hoc-emulation-example.c
 Author      : d-logic - https://www.d-logic.net/nfc-rfid-reader-sdk/
 Description : Example for uFR Ad-Hoc Emulation Mode
 Dependencies: uFR firmware - min. version 3.9.35
               uFRCoder library - min. version 4.0.24
 ============================================================================
 */
#define APP_VERSION				"1.4"
#define MIN_FW_VER_MAJOR		3
#define MIN_FW_VER_MINOR		9
#define MIN_FW_VER_BUILD		35

#include <stdio.h>
#include <stdlib.h>
#include <stdbool.h>
#include <string.h>
#include <unistd.h>
#include <windows.h>
#include <conio.h>
#ifndef __linux
#   define DL_uFC_GCC_DLL_IMPORT
#endif
#include "uFCoder.h"
typedef const char * chr_ptr;
#include "main.h"

bool ad_hoc_emul_active = false, automatic_emul_active = false,
				params_changed = false, ad_hoc_emul_permanent = false;
uint32_t  ad_hoc_emul_time;

#define BITS4_MAX_VAL		15
#define BITS3_MAX_VAL		 7
uint8_t ThresholdMinLevel = 4, ThresholdCollLevel = 4;
uint8_t RFLevelAmp = 0, RxGain = 7, RFLevel = 9;
//==============================================================================
int app_loop(void);
UFR_STATUS NewCardInField(uint8_t sak, uint8_t *uid, uint8_t uid_size);
chr_ptr GetDlTypeName(uint8_t dl_type_code);

int main(int argc, char *argv[]) {

	int result = app_loop();

	return result;
}
//==============================================================================
int app_loop(void) {
	UFR_STATUS status;
	bool card_in_field = false, wrong_version = false;
	uint8_t version_major, version_minor, build;
	uint8_t old_sak = 0, old_uid_size = 0, old_uid[10];
	uint8_t sak, uid_size, uid[10], isExternalFieldPresent;
//	uint32_t temp;
	char key;

	printf( "-----------------------------------------\n"
			"  Example for uFR Ad-Hoc Emulation Mode  \n"
			"             version "APP_VERSION"\n"
			"-----------------------------------------\n"
			"Please wait while opening uFR NFC reader.\n"
			"-----------------------------------------\n");
	status = ReaderOpen();
	if (status != UFR_OK) {
		printf("Error while opening device, status is: 0x%08X\n", status);
		getchar();
		return EXIT_FAILURE;
	}
	status = ReaderReset();
	if (status != UFR_OK) {
		printf("Error while opening device, status is: 0x%08X\n", status);
		getchar();
		return EXIT_FAILURE;
	}

	status = GetReaderFirmwareVersion(&version_major, &version_minor);
	if (status != UFR_OK) {
		ReaderClose();
		printf("Error while opening device, status is: 0x%08X\n", status);
		getchar();
		return EXIT_FAILURE;
	}
	status = GetBuildNumber(&build);
	if (status != UFR_OK) {
		ReaderClose();
		printf("Error while opening device, status is: 0x%08X\n", status);
		getchar();
		return EXIT_FAILURE;
	}
	if (version_major < MIN_FW_VER_MAJOR) {
		wrong_version = true;
	} else if (version_major == MIN_FW_VER_MAJOR && version_minor < MIN_FW_VER_MINOR) {
		wrong_version = true;
	} else if (version_minor == MIN_FW_VER_MINOR && build < MIN_FW_VER_BUILD) {
		wrong_version = true;
	} else {
		printf("uFR NFC reader successfully opened.\n");
	}

	if (wrong_version) {
		ReaderClose();
		printf("Wrong uFR NFC reader firmware version (%d.%d.%d).\n"
			   "Please update uFR firmware to at last %d.%d.%d version.\n",
			   version_major, version_minor, build,
			   MIN_FW_VER_MAJOR, MIN_FW_VER_MINOR, MIN_FW_VER_BUILD);
		getchar();
		return EXIT_FAILURE;
	}

	ad_hoc_emul_time = GetTickCount();

	help();

	do {
		while (!_kbhit()) {
			if (!ad_hoc_emul_permanent) {
				if (ad_hoc_emul_active) {
					status = GetExternalFieldState(&isExternalFieldPresent);
					if (!isExternalFieldPresent && (GetTickCount() - ad_hoc_emul_time) > MAX_WAIT_TIME) {
						status = AdHocEmulationStop();
						ad_hoc_emul_active = false;
						automatic_emul_active = false;
						ad_hoc_emul_time = GetTickCount();
					}
					continue;
				}
				else if (automatic_emul_active) {
					status = GetExternalFieldState(&isExternalFieldPresent);
					if (!isExternalFieldPresent && (GetTickCount() - ad_hoc_emul_time) > EMULATION_MODE_TIME) {
						automatic_emul_active = false;
						status = AdHocEmulationStop();
						ad_hoc_emul_time = GetTickCount();
					}
					continue;
				}
				else if ((GetTickCount() - ad_hoc_emul_time) > READER_MODE_TIME) {
					automatic_emul_active = true;
					status = AdHocEmulationStart();
					ad_hoc_emul_time = GetTickCount();
				}
				if (!automatic_emul_active) {
					status = GetCardIdEx(&sak, uid, &uid_size);
					switch(status)
					{
					case UFR_OK:
						if (card_in_field) {
							if (old_sak != sak || old_uid_size != uid_size ||
									memcmp(old_uid, uid, uid_size)) {
								old_sak = sak;
								old_uid_size = uid_size;
								memcpy(old_uid, uid, uid_size);
								NewCardInField(sak, uid, uid_size);
							}
						} else {
							old_sak = sak;
							old_uid_size = uid_size;
							memcpy(old_uid, uid, uid_size);
							NewCardInField(sak, uid, uid_size);
							card_in_field = true;
						}
						break;
					case UFR_NO_CARD:
						card_in_field = false;
						status = UFR_OK;
						break;
					default:
						ReaderReset();
						ReaderClose();
						printf("Fatal error while trying to read card, status is: 0x%08X\n", status);
						getchar();
						return EXIT_FAILURE;
					}
//				usleep(APP_LOOP_CYCLE_PERIOD);
				}
			}
		}

		key = _getch();
		switch (key) {
			case 'a':
				// Turn off RFLevelAmp (SHOULD BE OFF all the time):
				if (RFLevelAmp) {
					RFLevelAmp = 0;
					params_changed = true;
				}
				printf("RFLevelAmp is off.\n");
				break;
			case 'A':
				// Turn on RFLevelAmp (SHOULD BE OFF all the time):
				if (!RFLevelAmp) {
					RFLevelAmp = 1;
					params_changed = true;
				}
				printf("RFLevelAmp is on.\n");
				break;
			case 'g':
				// Decrement RxGain (0..7 I.e. 0 means 18 dB and 7 means 48 dB):
				if (RxGain > 0) {
					RxGain--;
					params_changed = true;
				}
				printf("RxGain is %d\n", RxGain);
				break;
			case 'G':
				// Increment RxGain (0..7 I.e. 0 means 18 dB and 7 means 48 dB):
				if (RxGain < BITS3_MAX_VAL) {
					RxGain++;
					params_changed = true;
				}
				printf("RxGain is %d\n", RxGain);
				break;
			case 'l':
				// Decrement RFLevel (0..15 I.e. sensitivity of the RF level detector):
				if (RFLevel > 0) {
					RFLevel--;
					params_changed = true;
				}
				printf("RFLevel is %d\n", RFLevel);
				break;
			case 'L':
				// Increment RFLevel (0..15 I.e. sensitivity of the RF level detector):
				if (RFLevel < BITS4_MAX_VAL) {
					RFLevel++;
					params_changed = true;
				}
				printf("RFLevel is %d\n", RFLevel);
				break;
			case 'm':
				// Decrement Threshold MinLevel (0..15 I.e. defines the minimum signal strength at the decoder input that shall be accepted.):
				if (ThresholdMinLevel > 0) {
					ThresholdMinLevel--;
					params_changed = true;
				}
				printf("Threshold MinLevel is %d\n", ThresholdMinLevel);
				break;
			case 'M':
				// Increment Threshold MinLevel (0..15 I.e. defines the minimum signal strength at the decoder input that shall be accepted.):
				if (ThresholdMinLevel < BITS4_MAX_VAL) {
					ThresholdMinLevel++;
					params_changed = true;
				}
				printf("Threshold MinLevel is %d\n", ThresholdMinLevel);
				break;
			case 'c':
				// Decrement Threshold CollLevel (0..7 I.e. defines the minimum signal strength at the decoder input that has to be
				//                                reached by the weaker half-bit of the Manchester-coded signal to
				//                                generate a bit-collision relatively to the amplitude of the stronger half-bit.):
				if (ThresholdCollLevel > 0) {
					ThresholdCollLevel--;
					params_changed = true;
				}
				printf("Threshold CollLevel is %d\n", ThresholdCollLevel);
				break;
			case 'C':
				// Increment Threshold CollLevel (0..7 I.e. defines the minimum signal strength at the decoder input that has to be
				//                                reached by the weaker half-bit of the Manchester-coded signal to
				//                                generate a bit-collision relatively to the amplitude of the stronger half-bit.):
				if (ThresholdCollLevel < BITS4_MAX_VAL) {
					ThresholdCollLevel++;
					params_changed = true;
				}
				printf("Threshold CollLevel is %d\n", ThresholdCollLevel);
				break;
			case 'p':
			case 'P':
				status = GetAdHocEmulationParams(&ThresholdMinLevel, &ThresholdCollLevel, &RFLevelAmp, &RxGain, &RFLevel);
				if (status != UFR_OK) {
					printf("Error while reading ad-hoc emulation parameters, status is: 0x%08X\n", status);
				} else {
					print_ln('_');
					printf("GetAdHocEmulationParamsHnd() returns:\n");
					print_ln('_');
					printf("Threshold MinLevel is %d\n", ThresholdMinLevel);
					printf("Threshold CollLevel is %d\n", ThresholdCollLevel);
					if (RFLevelAmp)
						printf("RFLevelAmp is on.\n");
					else
						printf("RFLevelAmp is off.\n");
					printf("RxGain is %d\n", RxGain);
					printf("RFLevel is %d\n", RFLevel);
					print_ln('_');
				}
				break;
			case '1':
				ad_hoc_emul_permanent = true;
				ad_hoc_emul_active = true;
				status = AdHocEmulationStart();
				printf("uFR is in permanent emulation mode now.\n");
				break;
			case '0':
				ad_hoc_emul_permanent = false;
				ad_hoc_emul_active = false;
				status = AdHocEmulationStop();
				ad_hoc_emul_time = GetTickCount();
				printf("Permanent emulation mode has been terminated.\n");
				break;
			case 'r':
			case 'R':
				ad_hoc_emul_permanent = false;
				ad_hoc_emul_active = false;
				ReaderReset();
				ad_hoc_emul_time = GetTickCount();
				break;
			case '\x1b':
				break;
			default:
				help();
				break;
		}
		if (params_changed) {
			params_changed = false;
			status = SetAdHocEmulationParams(ThresholdMinLevel, ThresholdCollLevel, RFLevelAmp, RxGain, RFLevel);
			if (status != UFR_OK) {
				printf("Error while setting up new ad-hoc emulation parameters, status is: 0x%08X\n", status);
			}
			else if (ad_hoc_emul_permanent)
			{
				status = AdHocEmulationStart();
			}
		}
	} while (key != '\x1b');

	printf("Cleanup and closing uFR device.\n");
	ReaderReset();
	ReaderClose();
	return EXIT_SUCCESS;
}

//------------------------------------------------------------------------------
UFR_STATUS NewCardInField(uint8_t sak, uint8_t *uid, uint8_t uid_size) {
	UFR_STATUS status;
	uint8_t dl_card_type;

	status = GetDlogicCardType(&dl_card_type);
	if (status != UFR_OK)
		return status;

	if (dl_card_type == DL_UNKNOWN_ISO_14443_4 && uid_size == 4 &&
					(!memcmp("\001\002\003\004", uid, uid_size)
					 || uid[0] == 8)) {
		printf("\a-------------------------------------------------------------\n");
		printf("Device with NFC Support detected. Switch into emulation mode.\n");
		printf("-------------------------------------------------------------\n");
		ad_hoc_emul_active = true;
		status = AdHocEmulationStart();
		ad_hoc_emul_time = GetTickCount();
		return UFR_OK;
	}

	printf("\a-----------------------------------------------------\n");
	printf("Card type: %s, uid[%d] = ", GetDlTypeName(dl_card_type), uid_size);
	for (int i = 0; i < uid_size; i++) {
		printf("%02X", uid[i]);
		if (i < uid_size - 1) printf(":");
	}
	printf("\n-----------------------------------------------------\n");
	return UFR_OK;
}

//------------------------------------------------------------------------------
chr_ptr GetDlTypeName(uint8_t dl_type_code) {
	static char s[50];

	switch (dl_type_code) {
	case DL_MIFARE_ULTRALIGHT:
		strcpy(s, "DL_MIFARE_ULTRALIGHT");
		break;
	case DL_MIFARE_ULTRALIGHT_EV1_11:
		strcpy(s, "DL_MIFARE_ULTRALIGHT_EV1_11");
		break;
	case DL_MIFARE_ULTRALIGHT_EV1_21:
		strcpy(s, "DL_MIFARE_ULTRALIGHT_EV1_21");
		break;
	case DL_MIFARE_ULTRALIGHT_C:
		strcpy(s, "DL_MIFARE_ULTRALIGHT_C");
		break;
	case DL_NTAG_203:
		strcpy(s, "DL_NTAG_203");
		break;
	case DL_NTAG_210:
		strcpy(s, "DL_NTAG_210");
		break;
	case DL_NTAG_212:
		strcpy(s, "DL_NTAG_212");
		break;
	case DL_NTAG_213:
		strcpy(s, "DL_NTAG_213");
		break;
	case DL_NTAG_215:
		strcpy(s, "DL_NTAG_215");
		break;
	case DL_NTAG_216:
		strcpy(s, "DL_NTAG_216");
		break;
	case DL_MIKRON_MIK640D:
		strcpy(s, "DL_MIKRON_MIK640D");
		break;
	case DL_MIFARE_MINI:
		strcpy(s, "DL_MIFARE_MINI");
		break;
	case DL_MIFARE_CLASSIC_1K:
		strcpy(s, "DL_MIFARE_CLASSIC_1K");
		break;
	case DL_MIFARE_CLASSIC_4K:
		strcpy(s, "DL_MIFARE_CLASSIC_4K");
		break;
	case DL_MIFARE_PLUS_S_2K:
		strcpy(s, "DL_MIFARE_PLUS_S_2K");
		break;
	case DL_MIFARE_PLUS_S_4K:
		strcpy(s, "DL_MIFARE_PLUS_S_4K");
		break;
	case DL_MIFARE_PLUS_X_2K:
		strcpy(s, "DL_MIFARE_PLUS_X_2K");
		break;
	case DL_MIFARE_PLUS_X_4K:
		strcpy(s, "DL_MIFARE_PLUS_X_4K");
		break;
	case DL_MIFARE_DESFIRE:
		strcpy(s, "DL_MIFARE_DESFIRE");
		break;
	case DL_MIFARE_DESFIRE_EV1_2K:
		strcpy(s, "DL_MIFARE_DESFIRE_EV1_2K");
		break;
	case DL_MIFARE_DESFIRE_EV1_4K:
		strcpy(s, "DL_MIFARE_DESFIRE_EV1_4K");
		break;
	case DL_MIFARE_DESFIRE_EV1_8K:
		strcpy(s, "DL_MIFARE_DESFIRE_EV1_8K");
		break;
	case DL_UNKNOWN_ISO_14443_4:
		strcpy(s, "DL_UNKNOWN_ISO_14443_4");
		break;
	case DL_IMEI_UID:
		strcpy(s, "DL_IMEI_UID");
		break;
	default:
		strcpy(s, "UNSUPPORTED CARD");
	}

	return s;
}
//==============================================================================
void help(void)
{
	print_ln('_');
	printf("Options:\n");
	print_ln('_');
	printf(" [a] - Turn off RFLevelAmp (SHOULD BE OFF all the time).\n");
	printf(" [A] + Turn on  RFLevelAmp (SHOULD BE OFF all the time).\n");
	printf(" [g] - Decrement RxGain (min.val=0 I.e. 0 means 18 dB).\n");
	printf(" [G] + Increment RxGain (max.val=7 I.e. 7 means 48 dB).\n");
	printf(" [l] - Decrement RFLevel (min.val=0) (sensitivity of the RF level detector).\n");
	printf(" [L] + Increment RFLevel (max.val=15 (sensitivity of the RF level detector).\n");
	printf(" [m] - Decrement Threshold MinLevel (min.val=0) (defines the minimum signal strength\n"
		   "       at the decoder input that shall be accepted).\n");
	printf(" [M] + Increment Threshold MinLevel (max.val=15) (defines the minimum signal strength\n"
		   "       at the decoder input that shall be accepted).\n");
	printf(" [c] - Decrement Threshold CollLevel (min.val=0) (defines the minimum signal strength\n"
		   "       at the decoder input that has to be reached by the weaker half-bit\n"
		   "       of the Manchester-coded signal to generate a bit-collision relatively\n"
		   "       to the amplitude of the stronger half-bit).\n");
	printf(" [C] + Increment Threshold CollLevel (max.val=7) (defines the minimum signal strength\n"
		   "       at the decoder input that has to be reached by the weaker half-bit\n"
		   "       of the Manchester-coded signal to generate a bit-collision relatively\n"
		   "       to the amplitude of the stronger half-bit).\n");
	printf("[esc]- exit\n");
	printf("[p, P] Get and print device settings on the screen.\n");
	printf("[r, R] Restart device.\n");
	printf(" [1] + Put device in the permanent emulation mode.\n");
	printf(" [0] + Exit from the permanent emulation mode.\n");
	print_ln('_');
}
//------------------------------------------------------------------------------
void print_ln_len(char symbol, uint8_t cnt) {

	for (int i = 0; i < cnt; i++)
		printf("%c", symbol);

	printf("\n");
}
//------------------------------------------------------------------------------
void print_ln(char symbol) {

	print_ln_len(symbol, DEFAULT_LINE_LEN);
}
//------------------------------------------------------------------------------
void print_hex(const uint8_t *data, uint32_t len, const char *delimiter) {

	for (int i = 0; i < len; i++) {
		printf("%02X", data[i]);
		if ((delimiter != NULL) && (i < (len - 1)))
			printf("%c", *delimiter);
	}
}
//------------------------------------------------------------------------------
void print_hex_ln(const uint8_t *data, uint32_t len, const char *delimiter) {

	print_hex(data, len, delimiter);
	printf("\n");
}
//==============================================================================
