/*
 *  Routines to access hardware
 *
 *  Copyright (c) 2015 Realtek Semiconductor Corp.
 *
 */

#include "cmsis_os.h"

#include "diag.h"
#include "main.h"

#include "nfc_api.h"
#include "flash_api.h"

#define NFC_RESTORE_DEFAULT (0)

#define NFC_MAX_PAGE_NUM 36
nfctag_t nfctag;
unsigned int  nfc_tag_content[NFC_MAX_PAGE_NUM];
unsigned char nfc_tag_dirty[NFC_MAX_PAGE_NUM];

#define RTK_NFC_UID 0x58
unsigned char nfc_default_uid[7] = {
    RTK_NFC_UID, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06
};

osThreadId nfc_tid = 0;

#define FLASH_APP_NFC_BASE   0x85000
flash_t flash_nfc;

void nfc_event_listener(void *arg, unsigned int event) {
    switch(event) {
        case NFC_EV_READER_PRESENT:
            DiagPrintf("NFC_EV_READER_PRESENT\r\n");
            break;
        case NFC_EV_READ:
            DiagPrintf("NFC_EV_READ\r\n");
            break;
        case NFC_EV_WRITE:
            DiagPrintf("NFC_EV_WRITE\r\n");
            break;
        case NFC_EV_ERR:
            DiagPrintf("NFC_EV_ERR\r\n");
            break;
        case NFC_EV_CACHE_READ:
            DiagPrintf("NFC_EV_CACHE_READ\r\n");
            break;
    }
}

/**
 *  This callback function is called several times if tag is being written multiple pages.
 *  DO NOT put heavy task here otherwise it will block tag write and cause timeout failure.
 **/
void nfc_write_listener(void *arg, unsigned int page, unsigned int pgdat) {
    nfc_tag_content[page] = pgdat;
    nfc_tag_dirty[page] = 1;
    if (nfc_tid) {
        osSignalSet(nfc_tid, NFC_EV_WRITE);
    }
}

int is_valid_nfc_uid() {
    int valid_content = 1;

    unsigned char uid[7];
    unsigned char bcc[2];

    uid[0] = (unsigned char)((nfc_tag_content[0] & 0x000000FF) >>  0);
    uid[1] = (unsigned char)((nfc_tag_content[0] & 0x0000FF00) >>  8);
    uid[2] = (unsigned char)((nfc_tag_content[0] & 0x00FF0000) >> 16);
    bcc[0] = (unsigned char)((nfc_tag_content[0] & 0xFF000000) >> 24);
    uid[3] = (unsigned char)((nfc_tag_content[1] & 0x000000FF) >>  0);
    uid[4] = (unsigned char)((nfc_tag_content[1] & 0x0000FF00) >>  8);
    uid[5] = (unsigned char)((nfc_tag_content[1] & 0x00FF0000) >> 16);
    uid[6] = (unsigned char)((nfc_tag_content[1] & 0xFF000000) >> 24);
    bcc[1] = (unsigned char)((nfc_tag_content[2] & 0x000000FF) >>  0);

    // verify Block Check Character
    if (bcc[0] != (0x88 ^ uid[0] ^ uid[1] ^ uid[2])) {
        valid_content = 0;
    }
    if (bcc[1] != (uid[3] ^ uid[4] ^ uid[5] ^ uid[6])) {
        valid_content = 0;
    }

    return valid_content;
}

unsigned int generate_default_tag_content() {
    unsigned int page_size = 0;

    memset(nfc_tag_content, 0, NFC_MAX_PAGE_NUM * sizeof(unsigned int));

    // calculate Block Check Character
    unsigned char bcc[2];
    bcc[0] = 0x88 ^ nfc_default_uid[0] ^ nfc_default_uid[1] ^ nfc_default_uid[2];
    bcc[1] = nfc_default_uid[3] ^ nfc_default_uid[4] ^ nfc_default_uid[5] ^ nfc_default_uid[6];

    // generate header
    nfc_tag_content[page_size++] = ((unsigned int)nfc_default_uid[0]) <<  0 |
                                   ((unsigned int)nfc_default_uid[1]) <<  8 |
                                   ((unsigned int)nfc_default_uid[2]) << 16 |
                                   ((unsigned int)            bcc[0]) << 24;
    nfc_tag_content[page_size++] = ((unsigned int)nfc_default_uid[3]) <<  0 |
                                   ((unsigned int)nfc_default_uid[4]) <<  8 |
                                   ((unsigned int)nfc_default_uid[5]) << 16 |
                                   ((unsigned int)nfc_default_uid[6]) << 24;
    nfc_tag_content[page_size++] = ((unsigned int)            bcc[1]) <<  0;
    nfc_tag_content[page_size++] = 0x001211E1;

    // Init tag content as NDEF will-known text message "HELLO WORLD!" in little endian
    nfc_tag_content[page_size++] = 0x01d11303;
    nfc_tag_content[page_size++] = 0x6502540f;
    nfc_tag_content[page_size++] = 0x4c45486e;
    nfc_tag_content[page_size++] = 0x57204f4c;
    nfc_tag_content[page_size++] = 0x444c524f;
    nfc_tag_content[page_size++] = 0x0000fe21;

    return page_size;
}

void nfc_load_tag_content_from_flash() {
    int i, address, page_size;

    memset(nfc_tag_content, 0, NFC_MAX_PAGE_NUM * sizeof(unsigned int));
    memset(nfc_tag_dirty, 0, NFC_MAX_PAGE_NUM);

    for (i = 0, address = FLASH_APP_NFC_BASE; i < NFC_MAX_PAGE_NUM; i++, address+=4) {
        flash_read_word(&flash_nfc, address, &nfc_tag_content[i]);
    }

    if (!is_valid_nfc_uid() || NFC_RESTORE_DEFAULT) {
        DiagPrintf("Invalid tag content, restore to default value\r\n");
        page_size = generate_default_tag_content();

        // update to flash
        flash_erase_sector(&flash_nfc, FLASH_APP_NFC_BASE);
        for (i = 0, address = FLASH_APP_NFC_BASE; i < page_size; i++, address += 4) {
            flash_write_word(&flash_nfc, address, nfc_tag_content[i]);
        }
    }

}

void nfc_store_tag_content() {
    int i, address;
    int modified_page_count;

    // dump the modified tag content
    modified_page_count = 0;
    for (i = 4; i < NFC_MAX_PAGE_NUM && nfc_tag_dirty[i]; i++) {
        modified_page_count++;
        DiagPrintf("page:%02d data:%08x\r\n", i, nfc_tag_content[i]);
    }

    // update to cache from page 4
    nfc_cache_write(&nfctag, &(nfc_tag_content[4]), 4, modified_page_count);

    modified_page_count += 4; // we also need update tag header to flash which has size 4
    flash_erase_sector(&flash_nfc, FLASH_APP_NFC_BASE);
    for (i = 0, address = FLASH_APP_NFC_BASE; i < modified_page_count; i++, address += 4) {
        flash_write_word(&flash_nfc, address, nfc_tag_content[i]);
    }

}

void nfc_task(void const *arg) {
    int i;
    osEvent evt;

    nfc_load_tag_content_from_flash();

    nfc_init(&nfctag, nfc_tag_content);
    nfc_event(&nfctag, nfc_event_listener, NULL, 0xFF);
    nfc_write(&nfctag, nfc_write_listener, NULL);

    osSignalClear(nfc_tid, NFC_EV_WRITE);

    while(1) {
        evt = osSignalWait (0, 0xFFFFFFFF); // wait for any signal with max timeout
        if (evt.status == osEventSignal && (evt.value.signals & NFC_EV_WRITE)) {
            osDelay(300);

            nfc_store_tag_content();

            memset(nfc_tag_dirty, 0, NFC_MAX_PAGE_NUM);
            osSignalClear(nfc_tid, NFC_EV_WRITE);
        }
    }
}

/**
  * @brief  Main program.
  * @param  None
  * @retval None
  */
void main(void)
{
    osThreadDef(nfc_task, osPriorityRealtime, 1, 1024);
    nfc_tid = osThreadCreate (osThread (nfc_task), NULL);

    DBG_INFO_MSG_OFF(_DBG_SPI_FLASH_);

    //3 3)Enable Schedule, Start Kernel
#if defined(CONFIG_KERNEL) && !TASK_SCHEDULER_DISABLED
    #ifdef PLATFORM_FREERTOS
    vTaskStartScheduler();
    #endif
#else
    RtlConsolTaskRom(NULL);
#endif

    while(1);
}

