/*
 * ll_consolehandler.cpp
 *
 *  Created on: Nov 7, 2019
 *      Author: abody
 */

#include <f4ll/console_handler.h>
#include <f4ll/str_util.h>

#include <cstring>

namespace f4ll {

console_handler::console_handler(
    USART_TypeDef *usart, DMA_TypeDef *dma, uint32_t stream_rx, uint32_t stream_tx, uint8_t *rx_buffer, size_type rx_buffer_size,
    uint8_t *tx_buffer, size_type tx_buffer_size, iconsole_input *rx_callback)
    : usart_core(usart, dma, stream_rx, stream_tx),
      m_tx_buffer(tx_buffer, tx_buffer_size),
      m_rx_buffer(rx_buffer, rx_buffer_size),
      m_rx_callback(rx_callback)
{
    LL_USART_EnableIT_IDLE(usart);
    LL_USART_EnableIT_ERROR(usart);
    setup_receive();
}

void console_handler::setup_receive(void)
{
    uint8_t *bptr = m_rx_buffer.get_free_chunk(m_bytes_requested);
    m_reqd_bytes_registered = 0;
    usart_core::setup_receive(bptr, m_bytes_requested);
}

void console_handler::receiver_idle(void)
{
    uint16_t rcvd_bytes = m_bytes_requested - m_reqd_bytes_registered - LL_DMA_GetDataLength(m_rx_dma.get_dma(), m_rx_dma.get_stream());
    if (rcvd_bytes) {
    	m_reqd_bytes_registered += rcvd_bytes;
        m_rx_buffer.produced(rcvd_bytes);
        m_rx_buffer.commit();
        if (m_rx_callback) {
            m_rx_callback->input_available(rcvd_bytes);
        }
    }
}

void console_handler::framing_error(void) {}

void console_handler::overrun(void) {}

void console_handler::rx_dma_transfer_complete(void)
{
    LL_DMA_DisableStream(m_rx_dma.get_dma(), m_rx_dma.get_stream());
    uint16_t rcvd_bytes = m_bytes_requested - m_reqd_bytes_registered - LL_DMA_GetDataLength(m_rx_dma.get_dma(), m_rx_dma.get_stream());
    if (rcvd_bytes) {
        m_rx_buffer.produced(rcvd_bytes);
        setup_receive();
        m_rx_buffer.commit();
        if (m_rx_callback) {
            m_rx_callback->input_available(rcvd_bytes);
        }
    }
}

void console_handler::rx_dma_half_transfer(void) {}

void console_handler::rx_dma_error(dma_helper::dma_error_type reason)
{
    (void)reason;
}

void console_handler::tx_dma_transfer_complete(void)
{
    LL_USART_EnableIT_TC(m_usart);
    LL_DMA_DisableStream(m_tx_dma.get_dma(), m_tx_dma.get_stream());
}

void console_handler::transmission_complete(void)
{
    m_tx_buffer.consumed(m_bytes_sent);
    if (m_tx_buffer.commited()) {
        uint8_t const *chunk;
        m_tx_buffer.get_chunk(chunk, m_bytes_sent);
        if (m_bytes_sent) {
            setup_transmit(chunk, m_bytes_sent);
        }
    } else {
        m_bytes_sent = 0;
    }
}

void console_handler::tx_dma_half_transfer(void) {}

void console_handler::tx_dma_error(dma_helper::dma_error_type reason)
{
    (void)reason;
}

console_handler::size_type console_handler::append(char const *s)
{
    size_t len = strlen(s);
    if (!len) {
        return 0;
    }
    return len - m_tx_buffer.put(reinterpret_cast<uint8_t const *>(s), len);
}

void console_handler::flush()
{
    if (!m_tx_buffer.uncommited()) {
        return;
    }
    m_tx_buffer.commit();

    if (m_bytes_sent) {
        return;
    }

    uint8_t const *chunk;
    m_tx_buffer.get_chunk(chunk, m_bytes_sent);
    if (m_bytes_sent) {
        setup_transmit(chunk, m_bytes_sent);
    }
}

void console_handler::print(char const *s)
{
    append(s);
    flush();
}

} /* namespace f4ll */
