#include <stdio.h>
#include <M5Unified.h>
#include "freertos/task.h"
#include "esp_err.h"
#include "tinyusb.h"

#define TUSB_DESC_TOTAL_LEN (TUD_CONFIG_DESC_LEN + CFG_TUD_VENDOR * TUD_VENDOR_DESC_LEN)
#define EPNUM_VENDOR_OUT    (0x01)
#define EPNUM_VENDOR_IN     (0x81)

uint8_t const tusb_configuration_descriptor[] = {
    // Config Header
    TUD_CONFIG_DESCRIPTOR(1, 1, 0, TUSB_DESC_TOTAL_LEN, 0x00, 100),

    // Vendor Interface
    TUD_VENDOR_DESCRIPTOR(0, 4, EPNUM_VENDOR_OUT, EPNUM_VENDOR_IN, 512)
};

tinyusb_config_t tusb_install_cfg = {
    .fs_configuration_descriptor = tusb_configuration_descriptor,
    .hs_configuration_descriptor = tusb_configuration_descriptor,
};

extern "C" void app_main(void) {
    M5.begin();
    M5.Display.setFont(&fonts::lgfxJapanGothicP_40);
    M5.Display.println("M5Unified initialized.");

    esp_err_t err = tinyusb_driver_install(&tusb_install_cfg);
    if (err) {
        M5.Display.print("TinyUSB Driver Install Failed: ");
        M5.Display.println(esp_err_to_name(err));
    }
    M5.Display.println("TinyUSB Driver Installed.");

    bool mounted = false;
    while (true) {
        bool device_mounted = tud_mounted();
        if (device_mounted && mounted != device_mounted) {
            M5.Display.println("Device connected.");
        }
        mounted = device_mounted;
        if (!device_mounted) {
            vTaskDelay(100 / portTICK_PERIOD_MS);
            continue;
        }

        uint32_t data_available = tud_vendor_available();
        if (!data_available) {
            vTaskDelay(100 / portTICK_PERIOD_MS);
            continue;
        }

        static uint8_t data_buffer[256];
        while (data_available > 0) {
            uint32_t read_size = data_available;
            if (read_size > sizeof(data_buffer) - 1) read_size = sizeof(data_buffer) - 1;
            read_size = tud_vendor_read(data_buffer, read_size);
            data_available -= read_size;
            M5.Display.print((char*)data_buffer);
        }
        M5.Display.println("");
    }
}
