/*
 * Copyright (c) 2018 Nordic Semiconductor ASA
 *
 * SPDX-License-Identifier: LicenseRef-Nordic-5-Clause
 */

#include <zephyr/kernel.h>
#include <zephyr/sys/reboot.h>

#include <nrfx_nfct.h>
#include <nfc_t4t_lib.h>

#define MAXLEN_APDU_CMD (512)
static struct k_work _worker;
static bool _cmd_buffer_in_use = false;
static uint8_t _cmd_buffer[MAXLEN_APDU_CMD];
static uint32_t _cmd_len=0;
static int64_t _cmd_rx_ts = 0;
static int64_t _cmd_proc_ts = 0;
static int64_t _resp_tx_ts = 0;
static uint8_t _resp_buffer[MAXLEN_APDU_CMD];
static uint32_t _resp_len=0;

// Process ADPU cmd off the ISR callback
static void _process_apdu_cmd(struct k_work* work) {
	_cmd_proc_ts = k_uptime_get();
	// Wait a little to simulate card processing delay
	// - 0 : no issues seen
	// - 10 : fails with HID reader, android TagInfo shows the ATR but no other info?

	k_msleep(0);
	// Generate a 'ok' reply
	_resp_buffer[0]=0x6a;
	_resp_buffer[1]=0x82;
	_resp_len = 2;
	// Response ready, make debug string to print before 'releasing' cmd buffer
	char cmd[128];
	sprintf(cmd, "%02X:%02X:%02X:%02X lc=%d, %d bytes", _cmd_buffer[0],_cmd_buffer[1],_cmd_buffer[2],_cmd_buffer[3], _cmd_buffer[4], _cmd_len);

    // Command processed one way or the other, buffer can receive another one on ISR
    _cmd_len = 0;
    _cmd_buffer_in_use=false;       // can receive another command
	int ret;
	// So how long since rx of cmd until we send response?
	_resp_tx_ts = k_uptime_get();
	int32_t dt = (int32_t)(_resp_tx_ts - _cmd_rx_ts);
	if ((ret=nfc_t4t_response_pdu_send(_resp_buffer, _resp_len))!=0) {
		printk("nfcmgr: t4t apdu cmd [%s] error (%d) sending response len %d after %dms\n", cmd, ret, _resp_len, dt);
	} else {
		printk("nfcmgr: t4t apdu cmd [%s] sent response len %d after %dms\n", cmd, _resp_len, dt);		
	}
}

// nfc_t4t callback for APDU operation (pdus are handled by specific registered APDU hanlding layer)
// This callback is called directly from the nfc lib's ISR, so must assume its in IRQ context
static void _nfc_t4t_apdu_cb(void *context, nfc_t4t_event_t event, const uint8_t *data, size_t data_length, uint32_t flags) {
    switch(event) {        
        case NFC_T4T_EVENT_FIELD_ON: {
            ///< External Reader polling detected.
			//printk("FIELD ON\n");
            break;
        }
        case NFC_T4T_EVENT_FIELD_OFF: {
            ///< External Reader polling stopped
			//printk("FIELD OFF\n");
            break;
        }
        case NFC_T4T_EVENT_DATA_IND: {
			// Check buffer is not in use already
			if (_cmd_buffer_in_use==false) {
				// New APDU fragment received. Add to buffer if not at max
				if ((_cmd_len+data_length)<MAXLEN_APDU_CMD) {
					memcpy(&(_cmd_buffer[_cmd_len]), data, data_length);
				} else {
					// oopsie
					printk("CMD TOO BIG!!!\n");
				}
				_cmd_len += data_length;
				if ((flags & NFC_T4T_DI_FLAG_MORE)==0) {
					//printk("CMD rxd, schedule processing\n");
					// all of the command received, schedule to process this msg
					_cmd_rx_ts = k_uptime_get();
					_cmd_buffer_in_use=true;        // Only 1 command at a time
					k_work_init(&_worker, &_process_apdu_cmd);
					k_work_submit(&_worker);
				}
			} else {
				printk("CMD when buffer in use!!!\n");
			}
			break;
        }
        case NFC_T4T_EVENT_DATA_TRANSMITTED: {
            // So now we can release the response buffer... but not reliable        
            // just to log
			//printk("response sent!\n");
            break;
        }
        default: {
			printk("unhandled event %d!\n", event);
            break;
        }
    }
}

bool _nfc_t4t_apdu_start() {
	int ret;
	//uint8_t selres = 0x00;      // TBD do we need to play with SEL_RES (aka SAK)?
	//nfc_t4t_parameter_set(NFC_T4T_PARAM_SELRES, &selres, 1);

	// Increase the wait time for any NFC readers as APDU processing may be slower (especially if talking to ISO7816 card)
	uint8_t fwi = 8;   // NFC_T4T_FWI_MAX_VAL_EMV;     // ==7 for EMV spec. 8 is the max the lib will accept.
/* Fails if done before emulation_start()???
	if ((ret=nfc_t4t_parameter_set(NFC_T4T_PARAM_FWI, &fwi, sizeof(fwi)))!=0) {
		printk("nfcmgr: t4t fwi set fails (%d)\n",ret);
	}
*/
	// register with nfc_t4t
	if ((ret = nfc_t4t_setup(_nfc_t4t_apdu_cb, NULL))!=0) {
//                if ((ret = nfc_t4t_setup(_nfc_t4t_test_cb_from_irq, &_ctx))!=0) {
		printk("nfcmgr : t4t fail setup for APDU (%d)\n", ret);
		// This is fatal error
		return false;
	}
	if ((ret = nfc_t4t_emulation_start())!=0) {
		printk("nfcmgr: t4t apdu emul start fails (%d)\n",ret);
	}
	// Try again
	if ((ret=nfc_t4t_parameter_set(NFC_T4T_PARAM_FWI, &fwi, sizeof(fwi)))!=0) {
		printk("nfcmgr: t4t fwi set after start fails (%d)\n",ret);
	}
	size_t plen=sizeof(fwi);
	if ((ret = nfc_t4t_parameter_get(NFC_T4T_PARAM_FWI, &fwi, &plen))!=0) {
		printk("nfcmgr: t4t fwi get fails (%d)\n",ret);
	}
	printk("nfcmgr : type 4 APDU started, fwi=%d\n", fwi);
	return true;
}

int main(void)
{
	if (_nfc_t4t_apdu_start()) {
		printk("NFC configuration done\n");
	} else {
		printk("NFC configuration FAILED\n");
	}
	for(;;) {
		k_msleep(1000);
	}
}
