#include <stdio.h>
#include "pico/stdlib.h"
#include "bsp/board_api.h"
#include "tusb.h"
#include "usb_descriptors.h"

#define INDICATOR_LED   PICO_DEFAULT_LED_PIN

#define A_SWITCH    16
#define B_SWITCH    17

#define KEYREPORT_SIZE  6
uint8_t keyreport[KEYREPORT_SIZE] = {0,0,0,0,0,0};

void add_key(uint8_t keycode)
{
    for(int i=0; i<KEYREPORT_SIZE; i++) {
        if(keyreport[i] == HID_KEY_NONE) {
            keyreport[i] = keycode;
            break;
        }
    }
}

void remove_key(uint8_t keycode)
{
    for(int i=0; i<KEYREPORT_SIZE; i++) {
        if(keyreport[i] == keycode) {
            keyreport[i] = HID_KEY_NONE;
        }
    } 
}

void clear_key(void)
{
    for(int i=0; i<KEYREPORT_SIZE; i++) {
        keyreport[i] = HID_KEY_NONE;
    } 
}

// キーボード状態に変化があったことを知らせるフラグ
volatile bool key_event_flag = false;

void keysw_event_handler(uint gpio, uint32_t events)
{
    uint8_t key = HID_KEY_NONE;

    if(gpio == A_SWITCH) key = HID_KEY_A;
    else if(gpio == B_SWITCH) key = HID_KEY_B;
    else return;

    if(events & GPIO_IRQ_EDGE_FALL){
        add_key(key);
    }
    if(events & GPIO_IRQ_EDGE_RISE) {
        remove_key(key);
    }
    key_event_flag = true;
}

// USBデバイスがマウントされた
void tud_mount_cb(void)
{
    clear_key();
}

// インジケーターLEDを制御する
void tud_hid_set_report_cb(uint8_t instance, uint8_t report_id, hid_report_type_t report_type, uint8_t const* buffer, uint16_t bufsize)
{
    if( bufsize < 1 ) return;   // バッファゼロなら何もしない
    // キーボードレポートか？
    if(report_type == HID_REPORT_TYPE_OUTPUT && report_id == REPORT_ID_KEYBOARD) {
        // CAPS LOCKでインジケーターを変更
        gpio_put(INDICATOR_LED, buffer[0] & KEYBOARD_LED_CAPSLOCK);
    }
}

// キーボードレポートを返す
uint16_t tud_hid_get_report_cb(uint8_t instance, uint8_t report_id, hid_report_type_t report_type, uint8_t* buffer, uint16_t reqlen)
{
    // キーボードレポートのサイズがなければ0を返す
    if(reqlen < sizeof(hid_keyboard_report_t) ) {
        buffer[0] = 0;
        return 1;       // とにかく何か返さないと駄目
    }
    // キーボードレポートを作成
    hid_keyboard_report_t   report;
    report.modifier = 0;        // 修飾キー情報
    report.reserved = 0;
    memcpy(report.keycode, keyreport, KEYREPORT_SIZE );
    memcpy(buffer, &report, sizeof(hid_keyboard_report_t));

    return sizeof(hid_keyboard_report_t);
}

int main()
{
    stdio_init_all();
    // LED
    gpio_init(INDICATOR_LED);
    gpio_set_dir(INDICATOR_LED, GPIO_OUT);
    gpio_put(INDICATOR_LED, 0);
    // A Switch = Pull Up
    gpio_init(A_SWITCH);
    gpio_set_dir(A_SWITCH, GPIO_IN);
    gpio_disable_pulls(A_SWITCH);
    gpio_pull_up(A_SWITCH);
    // B Switch = Pull Up
    gpio_init(B_SWITCH);
    gpio_set_dir(B_SWITCH, GPIO_IN);
    gpio_disable_pulls(B_SWITCH);
    gpio_pull_up(B_SWITCH);
    // GPIO割り込み
    gpio_set_irq_enabled_with_callback(A_SWITCH, GPIO_IRQ_EDGE_FALL|GPIO_IRQ_EDGE_RISE,true, keysw_event_handler);
    gpio_set_irq_enabled_with_callback(B_SWITCH, GPIO_IRQ_EDGE_FALL|GPIO_IRQ_EDGE_RISE,true, keysw_event_handler);
    // TinyUSB
    board_init();
    tud_init(BOARD_TUD_RHPORT);     // TinyUSB本体の初期化
    if (board_init_after_tusb) {    // マイコン依存の初期化後の処理
        board_init_after_tusb();
    }
    while (true) {
        tud_task();     // TinyUSBのジョブを定期的に呼び出す
        if(key_event_flag) {
            if(tud_hid_ready())
                tud_hid_keyboard_report(REPORT_ID_KEYBOARD, 0, keyreport);
            key_event_flag == false;
        }
    }
}
