#include "nrf_log.h"
#include "usb_cdc.h"
#include "boards.h"
#include "app_usbd_cdc_acm.h"

static char m_rx_buffer[NRF_DRV_USBD_EPSIZE * 16];
static usb_callback_t usb_callback;

/**
 * @brief USB cdc interfaces
 */
#define CDC_ACM_COMM_INTERFACE  2
#define CDC_ACM_DATA_INTERFACE  3

/**
 * @brief User event handler, CDC
 */
static void cdc_acm_user_ev_handler(app_usbd_class_inst_t const * p_inst,
                                    app_usbd_cdc_acm_user_event_t event);

/**
 * @brief CDC_ACM class instance
 * */
APP_USBD_CDC_ACM_GLOBAL_DEF(m_app_cdc_acm,
                            cdc_acm_user_ev_handler,
                            CDC_ACM_COMM_INTERFACE,
                            CDC_ACM_DATA_INTERFACE,
                            NRF_DRV_USBD_EPIN3,
                            NRF_DRV_USBD_EPIN2,
                            NRF_DRV_USBD_EPOUT1,
                            APP_USBD_CDC_COMM_PROTOCOL_AT_V250
);

uint32_t usb_cdc__init(usb_callback_t usb_cb)
{
    usb_callback.receive_from_cdc = usb_cb.receive_from_cdc;
    /* USB cdc class */
    app_usbd_class_inst_t const * class_cdc_acm;
    class_cdc_acm = app_usbd_cdc_acm_class_inst_get(&m_app_cdc_acm);

    return app_usbd_class_append(class_cdc_acm);
}

/**
 * @brief User event handler @ref app_usbd_cdc_acm_user_ev_handler_t (headphones)
 * */
static void cdc_acm_user_ev_handler(app_usbd_class_inst_t const * p_inst,
                                    app_usbd_cdc_acm_user_event_t event)
{
    app_usbd_cdc_acm_t const * p_cdc_acm = app_usbd_cdc_acm_class_get(p_inst);
    UNUSED_PARAMETER(p_inst);
    
    switch (event)
    {
        case APP_USBD_CDC_ACM_USER_EVT_PORT_OPEN:
            bsp_board_led_on(1);
            /*Setup first transfer*/
            app_usbd_cdc_acm_read(&m_app_cdc_acm,
                                   m_rx_buffer,
                                   sizeof(m_rx_buffer));
            //APP_ERROR_CHECK(ret);
            
            /* ABCDEFGH in hex, The two rows of code bellow is only for test purpose! */
            //const uint8_t data[8] = {0x41, 0x42, 0x43, 0x44, 0x45, 0x46, 0x47, 0x48}; 
            //usb_cdc__write((uint8_t*)data, sizeof(data));
            //uint8_t dummy = 'A';
            //app_usbd_cdc_acm_write(&m_app_cdc_acm, &dummy, 1);
            break;
        
        case APP_USBD_CDC_ACM_USER_EVT_PORT_CLOSE:
            bsp_board_led_off(1);
            break;

        case APP_USBD_CDC_ACM_USER_EVT_TX_DONE:
            bsp_board_led_invert(2);
            break;

        case APP_USBD_CDC_ACM_USER_EVT_RX_DONE:
        {
            
            size_t size = app_usbd_cdc_acm_bytes_stored(p_cdc_acm);
            for(int i = 0; i < size; i++)
            {
                NRF_LOG_INFO("RX(%i): char: %.2X", i, m_rx_buffer[i]);
            }
            usb_callback.receive_from_cdc((uint8_t*)m_rx_buffer, (uint32_t)size);
            app_usbd_cdc_acm_read(&m_app_cdc_acm,
                                  m_rx_buffer,
                                  sizeof(m_rx_buffer));
            /* Fetch data until internal buffer is empty */
            //ASSERT(ret == NRF_SUCCESS); /*Should not happen*/
            break;
        }

        default:
            break;
    }
}

uint32_t usb_cdc__write(const void *p_buf, size_t length)
{
    ret_code_t ret = 0;

    //while (app_usbd_event_queue_process());
    
    ret = app_usbd_cdc_acm_write(&m_app_cdc_acm, p_buf, length);

    return ret;
}