// Author: Ershov V.
// Modified: Sychov. A / 12/2020
#ifndef QUEUE_H
#define QUEUE_H

#ifdef __cplusplus
extern "C" {
#endif

#include <stdbool.h>
#include <string.h>
#include <stdint.h>
#include "app/ringbuffer/ring_buffer.h"
#include "my_assert.h"

typedef enum QUEUE_RC_t
{
    QUEUE_RC_NOERROR         =  0,

    QUEUE_RC_BAD_PARAMS      = -1,
    QUEUE_RC_NO_MEMORY       = -2,
    QUEUE_RC_OS_ERROR        = -3,
    QUEUE_RC_INIT_ERROR      = -4,
    QUEUE_RC_INTERNAL_ERROR  = -5,
    QUEUE_RC_EMPTY           = -6,
    QUEUE_RC_BAD_STATE       = -7,
    QUEUE_RC_QUERY_ABORTED   = -8,
    QUEUE_RC_TIMEOUT         = -9,
    QUEUE_RC_CREATE_CMD_FAIL = -10,

}QUEUE_RC_t;

/// Forward declaration of t struct.
#pragma pack(push,1)
typedef struct 
{
   int16_t size;      // standard record size (whole record size: header+data+payload)
   int16_t payload;   // extended record size (payload)
}
queue_record_t;
#pragma pack(pop)

STATIC_ASSERT( sizeof(int16_t) == sizeof(RING_BUFFER_RC_TYPE), "Invalid RING_BUFFER_RC_TYPE type" );

typedef ringbuf_t queue_t;

///
/// \brief Function checks if the record queue has no elements.
/// \param self Pointer to the record queue.
/// \return true if the queue is empty, false oserwise.
///
static inline bool queue_empty( const queue_t * self )
{
    assert(self);
    RING_BUFFER_RC_TYPE rc = ringbuf_size(self);
    assert(0 <= rc);
    return (rc == 0);
}

///
/// \brief Placing record to the end of the queue.
/// \param self Pointer to the queue.
/// \param record Pointer to the record struct to place.
/// \return QUEUE_RC_NOERROR if no error or error code.
///
int queue_pushback(queue_t * self, queue_record_t * record);

///
/// \brief Placing record to the begin of the queue.
/// \param self Pointer to the queue.
/// \param record Pointer to the record struct to place.
/// \return QUEUE_RC_NOERROR if no error or error code.
///
int queue_pushfront(queue_t * self, queue_record_t * record);

///
/// \brief Getting record from the queue without extracting.
/// \param self Pointer to the record queue.
/// \param record Pointer to the record to store.
/// \param size Size of the struct to store a record.
/// \return Size of the peeked record on success, error code otherwise.
///
int queue_peek(const queue_t * self, queue_record_t * record, int16_t size);

///
/// \brief Extract record from the queue.
/// \param self Pointer to the record queue.
/// \param record Pointer to the record to store.
/// \param size Size of the struct to store a record.
/// \return Size of the extracted record on success, error code otherwise.
///
static inline int queue_get(queue_t * self, queue_record_t * record, int16_t size)
{
    int rc = queue_peek(self, record, size);
    if(0 < rc)
    {
        RING_BUFFER_RC_TYPE rc = ringbuf_erase(self, record->size);
        assert(record->size == rc);
        (void)rc;
    }

    return rc;
}

///
/// \brief Removes all the records from the queue.
/// \param self Pointer to the record queue.
///
static inline void queue_clear(queue_t * self)
{
    ringbuf_clear(self);
}

///
/// \brief Extract and discard one record from the queue.
/// \param self Pointer to the record queue.
/// \return Size of discarded record if it exist, 0 otherwise.
///
int queue_removefront(queue_t * self);

#ifdef __cplusplus
}
#endif

#endif // QUEUE_H
