#include "terminal_usbbridge.h"
#include "terminal_sbs.h"
#include "usbd_cdc.h" 
#include "usbd_desc.h"
#include "cmsis_os.h"
#include "usbd_cdc_interface.h"
#include "fpga_blaster.h"
#include "_config.h"
#include <string.h>
#include <stdio.h>



osThreadId usbBridgeHandle;

osThreadId usbRxHandle;
osThreadId usbTxHandle;

//Usart bridge object for global terminal
UsbBridgeTerminal terminalUsbBridge;

//Pointer to global terminal object
extern Terminal* pTerminal;


//Print callback for terminal object
void print_usbbridge(const char * str) {
    terminalUsbBridge.print(str);
}



UsbBridgeTerminal::UsbBridgeTerminal()
{
    m_fpga_blast = false;
}

UsbBridgeTerminal::~UsbBridgeTerminal()
{
}


//Configure hardware and link with terminal
void UsbBridgeTerminal::configure()
{
    m_recvQueue = xQueueCreate(10, sizeof(char));
  
    m_sendQueue = xQueueCreate(200, sizeof(char));
    
    CDC_SetRecvCb(&USB_RecvByte);

    // Добавить функцию печати в терминал
    pTerminal->addPrint(::print_usbbridge);

    osThreadDef(UsbTx, vUsbTx, osPriorityNormal, 0, 2*128);
    usbTxHandle = osThreadCreate(osThread(UsbTx), NULL);;
    
    osThreadDef(UsbRx, vUsbRx, osPriorityNormal, 0, 2*128);
    usbRxHandle = osThreadCreate(osThread(UsbRx), NULL);
}


//
void UsbBridgeTerminal::print(const char *str)
{
    uint16_t len = strlen(str);

    if (len <= 0)
        return;

    CDC_Itf_Transmit((uint8_t*)str, len);
    vTaskDelay(1);    
}


// Recv byte from vcp
void UsbBridgeTerminal::recvByte(char byte)
{
    portBASE_TYPE xHigherPriorityTaskWoken = pdFALSE;
    
    xQueueSendFromISR(m_recvQueue, &byte, &xHigherPriorityTaskWoken);
    portEND_SWITCHING_ISR(xHigherPriorityTaskWoken);
}


//
void UsbBridgeTerminal::sendByte(uint8_t byte)
{
    portBASE_TYPE xHigherPriorityTaskWoken = pdFALSE;
    xQueueSendFromISR(m_sendQueue, &byte, &xHigherPriorityTaskWoken);
}


//
void UsbBridgeTerminal::set_fpga_blast(bool state)
{
    m_fpga_blast = state;
}


//
void USB_RecvByte(uint8_t* byte, uint32_t size)
{
    if (terminalUsbBridge.m_fpga_blast) {
        fpga_blast.receive(byte, size);
    }
    else {
        for (uint32_t i = 0; i < size; i++) {
            terminalUsbBridge.recvByte(*byte++);
        }
    }
}

//
void UsbBridgeTerminal::txProc()
{
    uint8_t byte;
    
    if (xQueueReceive(m_sendQueue, &byte, portMAX_DELAY) == pdTRUE)
    {
        CDC_Itf_Transmit(&byte, 1);
        vTaskDelay(1);    
    }
}

//
void UsbBridgeTerminal::rxProc()
{
    uint8_t byte;
    
    if (xQueueReceive(m_recvQueue, &byte, portMAX_DELAY) == pdTRUE)
    {
        pTerminal->insert(byte); 
    }
}    

//
void vUsbTx(void const *params)
{
    for (;;)
    {
        terminalUsbBridge.txProc();
    }
}

//
void vUsbRx(void const *params)
{
    for (;;)
    {
        terminalUsbBridge.rxProc();
    }
}
