#include "bsp/board_api.h"
#include "tusb.h"

#define _PID_MAP(itf, n) ((CFG_TUD_##itf) << (n))
#define USB_PID (0x4000 | _PID_MAP(VENDOR, 4))

const tusb_desc_device_t desc_device
  = { .bLength = sizeof(tusb_desc_device_t),
      .bDescriptorType = TUSB_DESC_DEVICE,
      .bcdUSB = 0x0200,
      .bDeviceClass = TUSB_CLASS_VENDOR_SPECIFIC,
      .bDeviceSubClass = 0x00,
      .bDeviceProtocol = 0x00,
      .bMaxPacketSize0 = CFG_TUD_ENDPOINT0_SIZE,
      .idVendor = 0xCAFE,
      .idProduct = 0xCA6D,
      .bcdDevice = 0x0100,
      .iManufacturer = 0x01,
      .iProduct = 0x02,
      .iSerialNumber = 0x03,
      .bNumConfigurations = 0x01 };

const uint8_t* tud_descriptor_device_cb(void)
{
  return (const uint8_t*)&desc_device;
}

#define CONFIG_TOTAL_LEN (TUD_CONFIG_DESC_LEN + TUD_VENDOR_DESC_LEN)

const uint8_t desc_configuration[]
  = { TUD_CONFIG_DESCRIPTOR(1, 1, 0, CONFIG_TOTAL_LEN, 0x00, 100),
      TUD_VENDOR_DESCRIPTOR(1, 0, 0x01, 0x81, 64) };

const uint8_t* tud_descriptor_configuration_cb(uint8_t index)
{
  (void)index;
  return desc_configuration;
}

enum
{
  STRID_LANGID = 0,
  STRID_MANUFACTURER,
  STRID_PRODUCT,
  STRID_SERIAL
};

const char* string_desc_arr[]
  = { [STRID_LANGID] = (const char[]){ 0x09, 0x04 },
      [STRID_MANUFACTURER] = "shylie.info",
      [STRID_PRODUCT] = "card device",
      [STRID_SERIAL] = NULL };

const uint16_t* tud_descriptor_string_cb(uint8_t index, uint16_t langid)
{
  static uint16_t _desc_str[32 + 1];

  (void)langid;
  size_t chr_count;

  switch (index)
  {
  case STRID_LANGID:
    memcpy(_desc_str + 1, string_desc_arr[0], 2);
    chr_count = 1;
    break;

  case STRID_SERIAL:
    chr_count = board_usb_get_serial(_desc_str + 1, 32);
    break;

  default:
    if (!(index < sizeof(string_desc_arr) / sizeof(*string_desc_arr)))
    {
      return NULL;
    }
    const char* str = string_desc_arr[index];
    chr_count = strlen(str);
    const size_t max_count = sizeof(_desc_str) / sizeof(*_desc_str) - 1;
    if (chr_count > max_count)
    {
      chr_count = max_count;
    }

    for (size_t i = 0; i < chr_count; i++)
    {
      _desc_str[i + 1] = str[i];
    }

    break;
  }

  _desc_str[0] = (uint16_t)((TUSB_DESC_STRING << 8) | (2 * chr_count + 2));

  return _desc_str;
}
