/*  -*-  mode: c; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4; coding: utf-8  -*-  */
/************************************************************************************
 **                                                                                **
 **                                        UHSDR                                   **
 **               a powerful firmware for STM32 based SDR transceivers             **
 **                                                                                **
 **--------------------------------------------------------------------------------**
 **                                                                                **
 **  Description:   Please provide one                                             **
 **  Licence:       GNU GPLv3                                                      **
 ************************************************************************************/
#include <assert.h>
#include "ui_driver.h" // for pushing into UI buffer
#include "uhsdr_digi_buffer.h"

#define DIGIMODES_TX_BUFFER_SIZE 128

static __IO uint8_t digimodes_tx_buffer[DIGIMODES_TX_BUFFER_SIZE];
static __IO int32_t digimodes_tx_buffer_head = 0;
static __IO int32_t digimodes_tx_tail = 0;
static __IO uint32_t active_consumer = 0;

// keeps prev consumer to restore it by calling DigiModes_Restore_BufferConsumer();
static __IO uint32_t prev_consumer = CW;

void DigiModes_Restore_BufferConsumer()
{
    active_consumer = prev_consumer;
}

/*
 * The UI consumer has higher priority to protect it by changing modes.
 * UI should release buffer by calling DigiModes_Restore_BufferConsumer()
 */
digi_buff_consumer_t DigiModes_Set_BufferConsumer( digi_buff_consumer_t consumer )
{
    assert((consumer & RTTY) || (consumer & BPSK) || (consumer & CW) || (consumer & UI));
    if ( active_consumer == UI ) // <- UI has higher priority
    {
        prev_consumer = consumer; // in case we change mode
        return prev_consumer;
    }
    prev_consumer = active_consumer;
    active_consumer = consumer;
    return prev_consumer;
}

uint8_t DigiModes_TxBufferHasData()
{
    int32_t len = digimodes_tx_buffer_head - digimodes_tx_tail;
    return len < 0 ? ( len + DIGIMODES_TX_BUFFER_SIZE ) : len;
}

bool DigiModes_TxBufferRemove( uint8_t* c_ptr, digi_buff_consumer_t consumer )
{
    assert( c_ptr );
    assert((consumer & RTTY) || (consumer & BPSK) || (consumer & CW) || (consumer & UI));

    bool retval = false;

    if ( consumer == active_consumer && digimodes_tx_buffer_head != digimodes_tx_tail)
    {
        *c_ptr = digimodes_tx_buffer[digimodes_tx_tail];
        digimodes_tx_tail = (digimodes_tx_tail + 1) % DIGIMODES_TX_BUFFER_SIZE;

        // push removed char to UI buffer to display it on the screen.
        // characters for UI and CW prints when they add to digi_buffer
        if ( active_consumer == RTTY || active_consumer == BPSK )
        {
            UiDriver_TextMsgPutChar( *c_ptr );
        }
        retval = true;
    }
    return retval;
}

/* no room left in the buffer returns 0 */
int32_t DigiModes_TxBufferPutChar( uint8_t c, digi_buff_consumer_t source )
{
    assert((source & CW) || (source & KeyBoard) || (source & UI));

    int32_t ret = 0;
    /*
     * In case when source and consumer are equal we just
     * print on the screen without putting into digi_buffer
     * to prevent from infinite loop
     * ( CW working as a source and receiver at the same time )
     */
    if ( active_consumer == source )
    {
        if(!(ts.dmod_mode == DEMOD_CW && !ts.cw_decoder_enable))
        {
        UiDriver_TextMsgPutChar( c );
        }
    }
    else
    {
        int32_t next_head = (digimodes_tx_buffer_head + 1) % DIGIMODES_TX_BUFFER_SIZE;
        if (next_head != digimodes_tx_tail)
        {
            /* there is room */
            digimodes_tx_buffer[digimodes_tx_buffer_head] = c;
            digimodes_tx_buffer_head = next_head;
            /*
             * If the consumer is UI we print on the screen
             * right the way on the inputing text into buffer
             */
            if ( active_consumer == UI )
            {
                if(!(ts.dmod_mode == DEMOD_CW && !ts.cw_decoder_enable))
                {
                    UiDriver_TextMsgPutChar( c );
                }
            }
            ret ++;
        }
    }
    return ret;
}

void DigiModes_TxBufferPutSign( const char* s, digi_buff_consumer_t source )
{
    assert( s );
    assert((source & CW) || (source & KeyBoard));

    DigiModes_TxBufferPutChar( '<', source );
    DigiModes_TxBufferPutChar( s[0], source );
    DigiModes_TxBufferPutChar( s[1], source );
    DigiModes_TxBufferPutChar( '>', source );
}

void DigiModes_TxBufferReset()
{
    digimodes_tx_tail = digimodes_tx_buffer_head;
}

#if defined(_UNIT_TEST_)
uint32_t DigiModes_TxBufferGetCurrentConsumer( void )
{
    return active_consumer;
}
uint32_t DigiModes_TxBufferGetPrevConsumer( void )
{
    return prev_consumer;
}
#endif
