/*
 * Copyright 2022 MindMotion Microelectronics Co., Ltd.
 * All rights reserved.
 *
 * SPDX-License-Identifier: BSD-3-Clause
 */

#include "board_init.h"
#include "tusb.h"

/*
 * Macros.
 */

/*
 * Variables.
 */

const uint8_t keycode2ascii[128u][2u] =  { HID_KEYCODE_TO_ASCII }; /* key code -> ascii. */
hid_keyboard_report_t prev_report = {0u}; /* previous report to check key released. */

/*
 * Declerations.
 */
void process_keyboard_report(hid_keyboard_report_t *report);

/*
 * Functions.
 */
int main(void)
{
    BOARD_Init();

    printf("tuh_hid_keyboard example.\r\n");

    tuh_init(BOARD_TUH_RHPORT); /* init tinyusb with host mode. */

    while (1)
    {
        tuh_task(); /* tinyusb host task. */
    }
}

/* Invoked when device attached & recognised. */
void tuh_mount_cb(uint8_t dev_addr)
{
    printf("a device with address %d is mounted\r\n", dev_addr);
}

/* Invoked when device detached. */
void tuh_umount_cb(uint8_t dev_addr)
{
    printf("a device with address %d is unmounted \r\n", dev_addr);
}

/*
 * Invoked when device with hid interface is mounted
 * Report descriptor is also available for use. tuh_hid_parse_report_descriptor()
 * can be used to parse common/simple enough descriptor.
 * Note: if report descriptor length > CFG_TUH_ENUMERATION_BUFSIZE, it will be skipped
 * therefore report_desc = NULL, desc_len = 0
 */
void tuh_hid_mount_cb(uint8_t dev_addr, uint8_t instance, uint8_t const* desc_report, uint16_t desc_len)
{
    printf("hid device address = %d, instance = %d is mounted\r\n", dev_addr, instance);
    uint8_t const itf_protocol = tuh_hid_interface_protocol(dev_addr, instance);

    if (HID_ITF_PROTOCOL_MOUSE == itf_protocol)
    {
        printf("the instance is a keyboard.\r\n");
    }
    else
    {
        printf("the instance is not a keyboard.\r\n");
    }

    /* start recv hid report. */
    if ( !tuh_hid_receive_report(dev_addr, instance) )
    {
        printf("error: cannot request to receive report\r\n");
    }
}

/* Invoked when device with hid interface is un-mounted. */
void tuh_hid_umount_cb(uint8_t dev_addr, uint8_t instance)
{
  printf("hid device address = %d, instance = %d is unmounted\r\n", dev_addr, instance);
}

/* Invoked when received report from device via interrupt endpoint. */
void tuh_hid_report_received_cb(uint8_t dev_addr, uint8_t instance, uint8_t const* report, uint16_t len)
{
  uint8_t const itf_protocol = tuh_hid_interface_protocol(dev_addr, instance);

    switch (itf_protocol)
    {
        case HID_ITF_PROTOCOL_KEYBOARD:
            process_keyboard_report( (hid_keyboard_report_t*) report );
            break;
        default:
            break;
    }

    /* start recv next hid report. */
    if ( !tuh_hid_receive_report(dev_addr, instance) )
    {
        printf("error: cannot request to receive report\r\n");
    }
}

/* look up new key in previous keys. */
bool find_key_in_report(hid_keyboard_report_t const *report, uint8_t keycode)
{
    for(uint8_t i=0; i<6; i++)
    {
        if (report->keycode[i] == keycode)
        {
            return true;
        }
    }

    return false;
}

/* keyboard report. */
void process_keyboard_report(hid_keyboard_report_t *report)
{
    for(uint8_t i=0; i<6; i++)
    {
        if ( report->keycode[i] != 0u )
        {
            if ( find_key_in_report(&prev_report, report->keycode[i]) )
            {
                // exist in previous report means the current key is holding
            }
            else
            {
                // not existed in previous report means the current key is pressed
                uint32_t is_shift = 0u;
                if ((report->modifier & (KEYBOARD_MODIFIER_LEFTSHIFT | KEYBOARD_MODIFIER_RIGHTSHIFT)) != 0)
                {
                    is_shift = 1u;
                }
                uint8_t ch = keycode2ascii[report->keycode[i]][is_shift];
                putchar(ch);
                if ( ch == '\r' )
                {
                    putchar('\n'); // added new line for enter key
                }
            }
        }
    }
    memcpy(&prev_report, report, sizeof(hid_keyboard_report_t));
}

/* EOF. */
