#include "UsbActor.h"

#include "mhg/usbhhelp.hpp"
#include "mhg/show_desc.hpp"
#include "mhg/utils/logging.h"
#include "esp_err.h"
#include "esp_log.h"

extern usb_device_handle_t Device_Handle;      // 从 usbhhelp.cpp 引用
extern usb_host_client_handle_t Client_Handle; // 如需使用 claim API

bool isPrinter{false};
bool isBiDirectional{false};
bool USBH_Printer{false};
bool deviceConnected = false;
const size_t PRINTER_OUT_BUFFERS = 8;

usb_transfer_t *PrinterOut = NULL;
usb_transfer_t *PrinterIn = NULL;

// 记录哪些接口已成功 claim（假设最多 16 个接口）
static bool interface_claimed[16] = {false};

// 回调前置声明（prepare_endpoints 使用）
static void printer_transfer_cb(usb_transfer_t *transfer);

// prepare_endpoints: 只有当对应接口已成功 claim 才分配/提交 transfer
void prepare_endpoints(const void *p)
{
  if (p == NULL) return;

  const usb_intf_desc_t *intf = (const usb_intf_desc_t *)p;
  int if_num = intf->bInterfaceNumber;

  if (!interface_claimed[if_num]) {
    VLOG("prepare_endpoints(): interface %d not claimed, skip endpoint prepare", if_num);
    return;
  }

  // 解析并为端点分配 transfer（示例，保留你的原有逻辑）
  const uint8_t *desc = (const uint8_t *)intf + intf->bLength;
  int parsed = 0;
  for (; parsed < intf->bNumEndpoints && desc && desc[0] > 0; desc += desc[0]) {
    if (desc[1] == USB_B_DESCRIPTOR_TYPE_ENDPOINT) {
      const usb_ep_desc_t *endpoint = (const usb_ep_desc_t *)desc;
      if ((endpoint->bmAttributes & 0x03) != USB_TRANSFER_TYPE_BULK) {
        VLOG("Not bulk endpoint: 0x%02x", endpoint->bEndpointAddress);
        continue;
      }

      if (endpoint->bEndpointAddress & 0x80) {
        // IN endpoint
        if (PrinterIn == NULL) {
          esp_err_t err = usb_host_transfer_alloc(endpoint->wMaxPacketSize, 0, &PrinterIn);
          if (err != ESP_OK) {
            ESP_LOGI("", "usb_host_transfer_alloc In fail: %s", esp_err_to_name(err));
            PrinterIn = NULL;
            continue;
          }
          PrinterIn->device_handle = Device_Handle;
          PrinterIn->bEndpointAddress = endpoint->bEndpointAddress;
          PrinterIn->callback = printer_transfer_cb;
          PrinterIn->num_bytes = endpoint->wMaxPacketSize;
          // 提交 IN 接收（若需要）
          err = usb_host_transfer_submit(PrinterIn);
          if (err != ESP_OK) {
            ESP_LOGI("", "usb_host_transfer_submit In fail: %s", esp_err_to_name(err));
            usb_host_transfer_free(PrinterIn);
            PrinterIn = NULL;
            // 如果提交失败且是 NOT_FOUND/INVALID_STATE，不要继续对该接口进行 out submit
          }
        }
      } else {
        // OUT endpoint
        if (PrinterOut == NULL) {
          size_t out_buf_size = endpoint->wMaxPacketSize * PRINTER_OUT_BUFFERS;
          esp_err_t err = usb_host_transfer_alloc(out_buf_size, 0, &PrinterOut);
          if (err != ESP_OK) {
            ESP_LOGI("", "usb_host_transfer_alloc Out fail: %s", esp_err_to_name(err));
            PrinterOut = NULL;
            continue;
          }
          PrinterOut->device_handle = Device_Handle;
          PrinterOut->bEndpointAddress = endpoint->bEndpointAddress;
          PrinterOut->callback = printer_transfer_cb;
          PrinterOut->num_bytes = 0;
          VLOG("Out data_buffer_size: %d", PrinterOut->data_buffer_size);
          // 注意：不要在这里盲目提交 OUT，提交应在发送数据时进行（避免枚举期间 submit 导致问题）
        }
      }
    }
  }
}

static void printer_transfer_cb(usb_transfer_t *transfer)
{
  if (transfer == NULL) return;

  // 只在检测到确实断开或错误时标记 deviceConnected = false
  if (transfer->status == USB_TRANSFER_STATUS_NO_DEVICE) {
    ESP_LOGI("", "printer_transfer_cb: device gone");
    deviceConnected = false;
    return;
  }

  if (transfer->status == USB_TRANSFER_STATUS_COMPLETED) {
    // 正常完成
    VLOG("printer_transfer_cb: completed bytes=%d ep=0x%02x", transfer->actual_num_bytes, transfer->bEndpointAddress);
    if ((transfer->bEndpointAddress & USB_B_ENDPOINT_ADDRESS_EP_DIR_MASK) != 0) {
      // IN transfer: 读取数据并再次提交
      uint8_t *const p = transfer->data_buffer;
      if (transfer->actual_num_bytes > 0) {
        Serial0.write(p, transfer->actual_num_bytes);
      }
      // 重新提交 IN 接收（若需要）
      esp_err_t err = usb_host_transfer_submit(transfer);
      if (err != ESP_OK) {
        ESP_LOGI("", "usb_host_transfer_submit In fail: %s", esp_err_to_name(err));
        // 出错则释放该 transfer
        usb_host_transfer_free(transfer);
        if (transfer == PrinterIn) PrinterIn = NULL;
        deviceConnected = false;
      }
    } else {
      // OUT transfer completed，通常不需要再次提交
      VLOG("OUT transfer done");
    }
    return;
  }

  // 其它错误状态
  ESP_LOGI("", "printer_transfer_cb: transfer status %d", transfer->status);
  if (transfer->status != USB_TRANSFER_STATUS_COMPLETED) {
    // 释放并标记断开，防止后续使用已失效 transfer
    usb_host_transfer_free(transfer);
    if (transfer == PrinterIn) PrinterIn = NULL;
    if (transfer == PrinterOut) PrinterOut = NULL;
    deviceConnected = false;
  }
}

static void prepare_endpoints_and_claim(const usb_intf_desc_t *intf_desc)
{
  if (!intf_desc || Device_Handle == NULL) return;

  int if_num = intf_desc->bInterfaceNumber;
  int alt = intf_desc->bAlternateSetting;

  esp_err_t ret = usb_host_interface_claim(Client_Handle, Device_Handle, if_num, alt);
  if (ret != ESP_OK) {
    VLOG("usb_host_interface_claim failed: %s interface:%d", esp_err_to_name(ret), if_num);
    interface_claimed[if_num] = false;
    return;
  }

  VLOG("Claimed interface %d", if_num);
  interface_claimed[if_num] = true;
  deviceConnected = true;
}

void check_interface_desc_printer(const void *p)
{
  const usb_intf_desc_t *intf = (const usb_intf_desc_t *)p;
  deviceConnected = true;
  // USB Printer Class Specification 1.1
  if ((intf->bInterfaceClass == USB_CLASS_PRINTER) &&
      (intf->bInterfaceSubClass == 1))
  {
    /* Protocol
     * 00 Reserved, undefined.
     * 01 Unidirectional interface.
     * 02 Bi-directional interface.
     * 03 1284.4 compatible bi-directional interface.
     * 04-FEh Reserved for future use.
     * FFh Vendor-specific printers do not use class-specific protocols.
     */
    // No idea how to support 1284.4 so ignore it.
    if ((intf->bInterfaceProtocol == 1) || (intf->bInterfaceProtocol == 2))
    {
      isBiDirectional = (intf->bInterfaceProtocol == 2);
      if (isBiDirectional)
      {
        if (intf->bNumEndpoints < 2)
        {
          isPrinter = false;
          return;
        }
      }
      else
      {
        if (intf->bNumEndpoints < 1)
        {
          isPrinter = false;
          return;
        }
      }
      isPrinter = true;
      ESP_LOGI("", "Claiming a %s-directional printer!", (isBiDirectional) ? "bi" : "uni");
      esp_err_t err = usb_host_interface_claim(Client_Handle, Device_Handle,
                                               intf->bInterfaceNumber, intf->bAlternateSetting);
      if (err != ESP_OK)
        ESP_LOGI("", "usb_host_interface_claim failed: %x", err);
    }
  }
}

void show_config_desc_full(const usb_config_desc_t *config_desc)
{
  deviceConnected = true;
  // if (client.connected())
  // {
  //   String topic_str = topic_printer_online + client_unique_id;
  //   client.publish(topic_str.c_str(), mqtt_client_id.c_str());
  // }
  // digitalWrite(led_printer_pin, HIGH);

  const uint8_t *p = &config_desc->val[0];
  uint8_t bLength;
  for (int i = 0; i < config_desc->wTotalLength; i += bLength, p += bLength)
  {
    bLength = *p;
    if ((i + bLength) <= config_desc->wTotalLength)
    {
      const uint8_t bDescriptorType = *(p + 1);
      switch (bDescriptorType)
      {
      case USB_B_DESCRIPTOR_TYPE_DEVICE:
        ESP_LOGI("", "USB Device Descriptor should not appear in config");
        break;
      case USB_B_DESCRIPTOR_TYPE_CONFIGURATION:
        show_config_desc(p);
        break;
      case USB_B_DESCRIPTOR_TYPE_STRING:
        ESP_LOGI("", "USB string desc TBD");
        break;
      case USB_B_DESCRIPTOR_TYPE_INTERFACE:
        show_interface_desc(p);
        check_interface_desc_printer(p);
        prepare_endpoints_and_claim((const usb_intf_desc_t *)p);
        break;
      case USB_B_DESCRIPTOR_TYPE_ENDPOINT:
        show_endpoint_desc(p);
        // prepare_endpoints(p);
        break;
      default:
        ESP_LOGI("", "Unknown USB Descriptor Type: 0x%x", *p);
        break;
      }
    }
    else
    {
      ESP_LOGI("", "USB Descriptor invalid");
      return;
    }
  }
}

void usbh_setup()
{
  usbh_setup(show_config_desc_full);
}

namespace fz
{
  // void usb_task_handler(void *actor);
  // void UsbActor::start_up() {
  //     usbh_setup();
  //     // xTaskCreatePinnedToCore(usb_task_handler, get_info()->get_name().begin(), 1024 * 10, this, 1, &usb_task_t_,1);
  //     xTaskCreate(usb_task_handler, get_info()->get_name().begin(), 1024 * 5, this, 1, &usb_task_t_);
  // }

  // void UsbActor::online_usb()
  // {
  //     has_usb_ = true;
  //     send_closure(Mhg::instance()->light_actor_own_, &LightActor::printer_status_ex, has_usb_);
  //     send_closure(Mhg::instance()->at_manager_own_, &ATManagerActor::send_printer_status, false);
  //     VLOG("run in Usb Actor online usb");
  // }

  // void usb_task_handler(void *actor){
  //   auto actor_t = static_cast<fz::UsbActor *>(actor);
  //   while(1){
  //     auto status = usbh_task();
  //   }
  // }

  // usb_transfer_t * UsbActor::get_usb_printer()
  // {
  //     return PrinterOut;
  // }

  // bool UsbActor::has_usb_server()
  // {
  //     return false;
  // }

  // void UsbActor::lose_usb()
  // {
  //     has_usb_ = false;
  //     send_closure(Mhg::instance()->light_actor_own_, &LightActor::printer_status_ex, has_usb_);
  //     fz::send_closure(fz::Mhg::instance()->at_manager_own_, &fz::ATManagerActor::send_printer_status, true);
  // }

} // namespace fz
