#include "cdc_vcom.h"
#include <stdio.h>
#include <stdarg.h>

#define CDC_MAX_PACKET_SIZE     512
#define CDC_VCOM_DATA_BUFSIZE   512	//    0xff //255

#define VCOM_DEVICE             "vcom"
#define VCOM_INTF_STR_INDEX 5

#define _SER_NO  "32021919830108"

typedef enum
{
    VCOM_FREE = 0,
    VCOM_SENDING = 1,
}vcom_tx_status;

struct vcom
{
    uep_t ep_out;
    uep_t ep_in;
    uep_t ep_cmd;
    rt_bool_t connected;
    rt_bool_t in_sending;

    struct ucdc_line_coding vcom_setting;
    volatile uint8_t vcom_send_status;
    uint8_t vcom_tx_buffer[CDC_VCOM_DATA_BUFSIZE];
    uint8_t vcom_rx_buffer[CDC_VCOM_DATA_BUFSIZE];

};
struct vcom _vcom = 
{
    RT_NULL,
    RT_NULL,
    RT_NULL,
    RT_FALSE,
    RT_FALSE,
    {
        115200,
        0,
        0,
        8,
    },
};

const static char *_ustring[] =
{
    "Language",
    "RT-Thread Team.",
    "RTT Virtual Serial",
    _SER_NO,
    "Configuration",
    "Interface",
};

static struct udevice_descriptor dev_desc =
{
    USB_DESC_LENGTH_DEVICE,     //bLength;
    USB_DESC_TYPE_DEVICE,       //type;
    USB_BCD_VERSION,            //bcdUSB;
    USB_CLASS_CDC,              //bDeviceClass;
    0x00,                       //bDeviceSubClass;
    0x00,                       //bDeviceProtocol;
    CDC_MAX_PACKET_SIZE,          //bMaxPacketSize0;
    _VENDOR_ID,                 //idVendor;
    _PRODUCT_ID,                //idProduct;
    USB_BCD_DEVICE,             //bcdDevice;
    USB_STRING_MANU_INDEX,      //iManufacturer;
    USB_STRING_PRODUCT_INDEX,   //iProduct;
    USB_STRING_SERIAL_INDEX,    //iSerialNumber;
    USB_DYNAMIC,                //bNumConfigurations;
};

static struct usb_qualifier_descriptor dev_qualifier =
{
    sizeof(dev_qualifier),          //bLength
    USB_DESC_TYPE_DEVICEQUALIFIER,  //bDescriptorType
    0x0200,                         //bcdUSB
    USB_CLASS_CDC,                  //bDeviceClass
    0x00,                           //bDeviceSubClass
    0x00,                           //bDeviceProtocol
    64,                             //bMaxPacketSize0
    0x01,                           //bNumConfigurations
    0,
};

const static struct ucdc_comm_descriptor _comm_desc =
{
#ifdef RT_USB_DEVICE_COMPOSITE
    /* Interface Association Descriptor */
    {
        USB_DESC_LENGTH_IAD,
        USB_DESC_TYPE_IAD,
        USB_DYNAMIC,
        0x02,
        USB_CDC_CLASS_COMM,
        USB_CDC_SUBCLASS_ACM,
        USB_CDC_PROTOCOL_V25TER,
        0x00,
    },
#endif
    /* Interface Descriptor */
    {
        USB_DESC_LENGTH_INTERFACE,
        USB_DESC_TYPE_INTERFACE,
        USB_DYNAMIC,
        0x00,
        0x01,
        USB_CDC_CLASS_COMM,
        USB_CDC_SUBCLASS_ACM,
        USB_CDC_PROTOCOL_V25TER,
#ifdef RT_USB_DEVICE_COMPOSITE
        VCOM_INTF_STR_INDEX,
#else
        0,
#endif
    },
    /* Header Functional Descriptor */
    {
        0x05,
        USB_CDC_CS_INTERFACE,
        USB_CDC_SCS_HEADER,
        0x0110,
    },
    /* Call Management Functional Descriptor */
    {
        0x05,
        USB_CDC_CS_INTERFACE,
        USB_CDC_SCS_CALL_MGMT,
        0x00,
        USB_DYNAMIC,
    },
    /* Abstract Control Management Functional Descriptor */
    {
        0x04,
        USB_CDC_CS_INTERFACE,
        USB_CDC_SCS_ACM,
        0x02,
    },
    /* Union Functional Descriptor */
    {
        0x05,
        USB_CDC_CS_INTERFACE,
        USB_CDC_SCS_UNION,
        USB_DYNAMIC,
        USB_DYNAMIC,
    },
    /* Endpoint Descriptor */
    {
        USB_DESC_LENGTH_ENDPOINT,
        USB_DESC_TYPE_ENDPOINT,
        USB_DYNAMIC | USB_DIR_IN,
        USB_EP_ATTR_INT,
        0x08,
        0xFF,
    },
};

const static struct ucdc_data_descriptor _data_desc =
{
    /* interface descriptor */
    {
        USB_DESC_LENGTH_INTERFACE,
        USB_DESC_TYPE_INTERFACE,
        USB_DYNAMIC,
        0x00,
        0x02,
        USB_CDC_CLASS_DATA,
        0x00,
        0x00,
        0x00,
    },
    /* endpoint, bulk out */
    {
        USB_DESC_LENGTH_ENDPOINT,
        USB_DESC_TYPE_ENDPOINT,
        USB_DYNAMIC | USB_DIR_OUT,
        USB_EP_ATTR_BULK,
        CDC_VCOM_DATA_BUFSIZE,
        0x00,
    },
    /* endpoint, bulk in */
    {
        USB_DESC_LENGTH_ENDPOINT,
        USB_DESC_TYPE_ENDPOINT,
        USB_DYNAMIC | USB_DIR_IN,
        USB_EP_ATTR_BULK,
        CDC_VCOM_DATA_BUFSIZE,
        0x00,
    },
};

static rt_err_t _cdc_descriptor_config(ucdc_comm_desc_t comm,
    rt_uint8_t cintf_nr, ucdc_data_desc_t data, rt_uint8_t dintf_nr)
{
comm->call_mgmt_desc.data_interface = dintf_nr;
comm->union_desc.master_interface = cintf_nr;
comm->union_desc.slave_interface0 = dintf_nr;
#ifdef RT_USB_DEVICE_COMPOSITE
comm->iad_desc.bFirstInterface = cintf_nr;
#endif
return RT_EOK;
}

static rt_err_t _function_enable(ufunction_t func)
{
    return RT_EOK;
}
static rt_err_t _function_disable(ufunction_t func)
{
    return RT_EOK;
}
static struct ufunction_ops ops =
{
    _function_enable,
    _function_disable,
    RT_NULL,
};
uint8_t temp_buff[100] = {0};

static rt_err_t _cdc_set_line_coding_callback(udevice_t device, rt_size_t size)
{
    dcd_ep_read(device->dcd, EP0_OUT_ADDR, (void *)&_vcom.vcom_setting);
    //debug("_cdc_set_line_coding_callback\n\r");
    return RT_EOK;
}

static rt_err_t _cdc_set_line_coding(udevice_t device, ureq_t setup)
{
    uint16_t count = 0;
    RT_ASSERT(device != RT_NULL);
    RT_ASSERT(setup != RT_NULL);
    //debug("_cdc_set_line_coding\n\r");

    rt_usbd_ep0_read(device, (void *)&_vcom.vcom_setting, setup->wLength,
                     _cdc_set_line_coding_callback);

    // rt_usbd_ep0_read(device, (void *)&_vcom.vcom_setting, setup->wLength,
    //                  _cdc_set_line_coding_callback);

    // debug("111 0x%08x\n\r",(void*)&_vcom.vcom_setting);
    // debug("222 0x%08x\n\r",(void*)&_vcom.vcom_setting.dwDTERate);
    // debug("333 0x%08x\n\r",(void*)&(_vcom.vcom_setting));
    return RT_EOK;
}

static rt_err_t _cdc_get_line_coding(udevice_t device, ureq_t setup)
{
    rt_uint16_t size;
    RT_ASSERT(device != RT_NULL);
    RT_ASSERT(setup != RT_NULL);
    RT_DEBUG_LOG(RT_DEBUG_USB, ("_cdc_get_line_coding\n"));

    size = setup->wLength > 7 ? 7 : setup->wLength;
    rt_usbd_ep0_write(device, (void *)&_vcom.vcom_setting, size);
    return RT_EOK;
}


static rt_err_t _interface_handler(ufunction_t func, ureq_t setup)
{
    RT_ASSERT(func != RT_NULL);
    RT_ASSERT(func->device != RT_NULL);
    RT_ASSERT(setup != RT_NULL);
    //debug("cdc vcom _interface_handler \n\r");
    switch (setup->bRequest)
    {
    case CDC_SEND_ENCAPSULATED_COMMAND:
        break;

    case CDC_GET_ENCAPSULATED_RESPONSE:
        break;

    case CDC_SET_COMM_FEATURE:
        break;

    case CDC_GET_COMM_FEATURE:
        break;

    case CDC_CLEAR_COMM_FEATURE:
        break;

    case CDC_SET_LINE_CODING:
        _cdc_set_line_coding(func->device, setup);
        break;

    case CDC_GET_LINE_CODING:
        _cdc_get_line_coding(func->device, setup);
        break;

    case CDC_SET_CONTROL_LINE_STATE:
        //debug("CDC_SET_CONTROL_LINE_STATE\n\r");
        //_vcom.connected = (setup->wValue & 0x03) > 0 ? RT_TRUE : RT_FALSE;
        if((_vcom.connected == RT_TRUE) && ((setup->wValue & 0x01) == 0))
        {
            _vcom.connected = RT_FALSE;
            //debug("vcom connect ! \n\r");
        }
        else if((_vcom.connected ==  RT_FALSE) && ((setup->wValue & 0x03) > 0))
        {
            _vcom.connected = RT_TRUE;
        }
        
        dcd_ep0_send_status(func->device->dcd);
        break;

    case CDC_SEND_BREAK:
        break;

    default:
        debug("unknown cdc request\n", setup->request_type);
        return -RT_ERROR;
    }

    return RT_EOK;
}
static rt_err_t _ep_cmd_handler(ufunction_t func, rt_size_t size)
{
    RT_ASSERT(func != RT_NULL);

    return RT_EOK;
}

static rt_err_t _ep_out_handler(ufunction_t func, rt_size_t size)
{
    // rt_uint32_t level;
    // struct vcom *data;
    // RT_ASSERT(func != RT_NULL);
    // RT_DEBUG_LOG(RT_DEBUG_USB, ("_ep_out_handler %d\n", size));
    // data = (struct vcom *)func->user_data;

    // /* ensure serial is active */
    // if ((data->serial.parent.flag & RT_DEVICE_FLAG_ACTIVATED)
    //         && (data->serial.parent.open_flag & RT_DEVICE_OFLAG_OPEN))
    // {
    //     /* receive data from USB VCOM */
    //     level = rt_hw_interrupt_disable();
    //     rt_ringbuffer_put(&data->rx_ringbuffer, data->ep_out->buffer, size);
    //     rt_hw_interrupt_enable(level);
    //     /* notify receive data */
    //     rt_hw_serial_isr(&data->serial, RT_SERIAL_EVENT_RX_IND);
    // }

    // data->ep_out->request.buffer = data->ep_out->buffer;
    // data->ep_out->request.size = EP_MAXPACKET(data->ep_out);
    // data->ep_out->request.req_type = UIO_REQUEST_READ_BEST;
    // rt_usbd_io_request(func->device, data->ep_out, &data->ep_out->request);
    return RT_EOK;
}

static rt_err_t _ep_in_handler(ufunction_t func, rt_size_t size)
{
    // struct vcom *data;
    // rt_size_t request_size;
    // RT_ASSERT(func != RT_NULL);
    // data = (struct vcom *)func->user_data;
    // request_size = data->ep_in->request.size;
    // RT_DEBUG_LOG(RT_DEBUG_USB, ("_ep_in_handler %d\n", request_size));

    // if ((request_size != 0) && ((request_size % EP_MAXPACKET(data->ep_in)) == 0))
    // {
    //     /* don't have data right now. Send a zero-length-packet to
    //      * terminate the transaction.
    //      *
    //      * FIXME: actually, this might not be the right place to send zlp.
    //      * Only the rt_device_write could know how much data is sending. */
    //     data->in_sending = RT_TRUE;
    //     data->ep_in->request.buffer = RT_NULL;
    //     data->ep_in->request.size = 0;
    //     data->ep_in->request.req_type = UIO_REQUEST_WRITE;
    //     rt_usbd_io_request(func->device, data->ep_in, &data->ep_in->request);
    //     return RT_EOK;
    // }

    // rt_completion_done(&data->wait);
    _vcom.vcom_send_status = VCOM_FREE;

    return RT_EOK;
}

//void usbd_cdc_vcom_puts_hex(uint8_t *addr, uint32_t len)
//{
//#ifndef	USING_BOARD_USB_CDC
//	return;
//#endif
//    uint16_t size = 0;
//    if(_vcom.connected == RT_FALSE)
//    {
//       return;
//    }
//    while(_vcom.vcom_send_status == VCOM_SENDING);
//    _vcom.vcom_send_status = VCOM_SENDING;
//    hal_usbd_send(_vcom.ep_in->id->addr & 0x0f, addr,len);
//    // _vcom.vcom_send_status = VCOM_FREE;
//}
void  usbd_cdc_vcom_puts_hex(uint8_t *addr, uint32_t len)
{
#ifndef	USING_BOARD_USB_CDC
	return;
#endif
    uint16_t size = 0;
    uint32_t sent_data = 0;
    if(_vcom.connected == RT_FALSE)
    {
        //debug("_vcom.connected == RT_FALSE\n\r");
        return;
    }
    while(len)
    {
        if(len > CDC_VCOM_DATA_BUFSIZE)
        {
            size = CDC_VCOM_DATA_BUFSIZE;
            len -= size;
        }
        else
        {
            size = len;
            len -= size;
        }
        while(_vcom.vcom_send_status == VCOM_SENDING);
        _vcom.vcom_send_status = VCOM_SENDING;
        hal_usbd_send(_vcom.ep_in->id->addr & 0x0f, addr,size);
    }

    debug("before sending\n\r");
    hal_usbd_send(_vcom.ep_in->id->addr & 0x0f, addr,len);
    // _vcom.vcom_send_status = VCOM_FREE;
}


void cdc_show_uart_wave(uint16_t ch0, uint16_t ch1, uint16_t ch2, uint16_t ch3, uint16_t ch4)
{
#ifndef	USING_BOARD_USB_CDC
	return;
#endif
    uint16_t sum = 0;
    uint8_t send_arry[20] = {0};
    send_arry[0] = 0XAA;
    send_arry[1] = ch0;
    send_arry[2] = ch0 >> 8;
    send_arry[3] = ch1;
    send_arry[4] = ch1 >> 8;
    send_arry[5] = ch2;
    send_arry[6] = ch2 >> 8;
    send_arry[7] = ch3;
    send_arry[8] = ch3 >> 8;
    send_arry[9] = ch4;
    send_arry[10] = ch4 >> 8;

    for (uint16_t i = 1; i < 17; i++)
    {
        sum += send_arry[i];
    }

    send_arry[17] = sum;
    send_arry[18] = sum >> 8;
    send_arry[19] = 0xBB;
    usbd_cdc_vcom_puts_hex((uint8_t *)send_arry, 20);
}


void usbd_cdc_vcom_send(char* buf)
{
#ifndef USING_BOARD_USB_CDC
	return;
#endif
    uint16_t size = 0;
    size = CDC_VCOM_DATA_BUFSIZE  >strlen(buf) ? strlen(buf):CDC_VCOM_DATA_BUFSIZE;
    brom_memcpy(_vcom.vcom_tx_buffer,buf,size);
    //while(_vcom.vcom_send_status == VCOM_SENDING);
    // _vcom.vcom_send_status = VCOM_SENDING;
    usbd_cdc_vcom_puts_hex(_vcom.vcom_tx_buffer,size);
    //dcd_ep_write(rt_usbd_get_curr_device()->dcd, EP0_IN_ADDR, _vcom.vcom_tx_buffer, size);
    
    //hal_usbd_send(_vcom.ep_in->id->addr & 0x0f, _vcom.vcom_tx_buffer,size);
    //_vcom.vcom_send_status = VCOM_FREE;
    //debug("%d\n\r",_vcom.ep_out->ep_desc->bEndpointAddress&0x0f);
}



void usbd_cdc_vcom_print(const char *fmt, ...)
{
#ifndef USING_BOARD_USB_CDC
	return;
#endif
    char buffer[CDC_VCOM_DATA_BUFSIZE];
    va_list args;
    int len;
    va_start(args, fmt);
    // 使用 vsnprintf 格式化字符串到缓冲区中
    len = vsnprintf(buffer, CDC_VCOM_DATA_BUFSIZE, fmt, args);
    va_end(args);
    // 若格式化结果超过缓冲区大小，则截断输出
    if (len < 0)
    {
        // 格式化失败，直接返回
        return;
    }
    usbd_cdc_vcom_send(buffer);

}

void free_debug(const char *fmt, ...)
{
;
}

#ifdef USING_BOARD_USB_CDC
ufunction_t rt_usbd_function_cdc_vcom_create(udevice_t device)
{
    ufunction_t func;
    //struct vcom *data;

    uintf_t intf_comm, intf_data;

    ualtsetting_t comm_setting, data_setting;
    ucdc_data_desc_t data_desc;
    ucdc_comm_desc_t comm_desc;

    #ifdef RT_USB_DEVICE_COMPOSITE
    rt_usbd_device_set_interface_string(device, VCOM_INTF_STR_INDEX, _ustring[2]);
#else
    /* set usb device string description */
    rt_usbd_device_set_string(device, _ustring);
#endif

    func = rt_usbd_function_new(device, &dev_desc, &ops);
    intf_comm = rt_usbd_interface_new(device, _interface_handler);
    intf_data = rt_usbd_interface_new(device, _interface_handler);

    comm_setting = rt_usbd_altsetting_new(sizeof(struct ucdc_comm_descriptor));
    data_setting = rt_usbd_altsetting_new(sizeof(struct ucdc_data_descriptor));

    rt_usbd_altsetting_config_descriptor(comm_setting, &_comm_desc,
            (rt_off_t) & ((ucdc_comm_desc_t)0)->intf_desc);
    rt_usbd_altsetting_config_descriptor(data_setting, &_data_desc, 0);

    //初始化union中的interface
    _cdc_descriptor_config(comm_setting->desc, intf_comm->intf_num, data_setting->desc, intf_data->intf_num);
    comm_desc = (ucdc_comm_desc_t)comm_setting->desc;
    _vcom.ep_cmd= rt_usbd_endpoint_new(&comm_desc->ep_desc, _ep_cmd_handler);

    rt_usbd_altsetting_add_endpoint(comm_setting, _vcom.ep_cmd);
    rt_usbd_interface_add_altsetting(intf_comm, comm_setting);
    rt_usbd_set_altsetting(intf_comm, 0);

    rt_usbd_function_add_interface(func, intf_comm);
    data_desc = (ucdc_data_desc_t)data_setting->desc;
    _vcom.ep_out = rt_usbd_endpoint_new(&data_desc->ep_out_desc, _ep_out_handler);
    _vcom.ep_in = rt_usbd_endpoint_new(&data_desc->ep_in_desc, _ep_in_handler);

    rt_usbd_altsetting_add_endpoint(data_setting, _vcom.ep_in);
    rt_usbd_altsetting_add_endpoint(data_setting, _vcom.ep_out);

    rt_usbd_interface_add_altsetting(intf_data, data_setting);
    rt_usbd_set_altsetting(intf_data, 0);

    rt_usbd_function_add_interface(func, intf_data);

    return func;
}
#else
ufunction_t rt_usbd_function_cdc_vcom_create(udevice_t device)
{
	;
}
#endif
