/* Copyright (c) 2014 Nordic Semiconductor. All Rights Reserved.
 *
 * The information contained herein is property of Nordic Semiconductor ASA.
 * Terms and conditions of usage are described in detail in NORDIC
 * SEMICONDUCTOR STANDARD SOFTWARE LICENSE AGREEMENT.
 *
 * Licensees are granted free, non-transferable use of the information. NO
 * WARRANTY of ANY KIND is provided. This heading must NOT be removed from
 * the file.
 *
 */

#include <stdbool.h>
#include <string.h>
#include "app_error.h"
#include "ser_config.h"
#include "ser_phy.h"
#include "ser_hal_transport.h"

/**
 * @brief States of the RX state machine.
 */
typedef enum
{
    HAL_TRANSP_RX_STATE_CLOSED = 0,
    HAL_TRANSP_RX_STATE_IDLE,
    HAL_TRANSP_RX_STATE_RECEIVING,
    HAL_TRANSP_RX_STATE_DROPPING,
    HAL_TRANSP_RX_STATE_RECEIVED,
    HAL_TRANSP_RX_STATE_RECEIVED_PENDING_BUF_REQ,
    HAL_TRANSP_RX_STATE_RECEIVED_DROPPING,
    HAL_TRANSP_RX_STATE_MAX
}ser_hal_transp_rx_states_t;

/**
 * @brief States of the TX state machine.
 */
typedef enum
{
    HAL_TRANSP_TX_STATE_CLOSED = 0,
    HAL_TRANSP_TX_STATE_IDLE,
    HAL_TRANSP_TX_STATE_TX_ALLOCATED,
    HAL_TRANSP_TX_STATE_TRANSMITTING,
    HAL_TRANSP_TX_STATE_TRANSMITTED,
    HAL_TRANSP_TX_STATE_MAX
}ser_hal_transp_tx_states_t;

/**
 * @brief RX state.
 */
static ser_hal_transp_rx_states_t m_rx_state = HAL_TRANSP_RX_STATE_CLOSED;
/**
 * @brief TX state.
 */
static ser_hal_transp_tx_states_t m_tx_state = HAL_TRANSP_TX_STATE_CLOSED;

/**
 * @brief Transmission buffer.
 */
static uint8_t m_tx_buffer[SER_HAL_TRANSPORT_TX_MAX_PKT_SIZE];
/**
 * @brief Reception buffer.
 */
static uint8_t m_rx_buffer[SER_HAL_TRANSPORT_RX_MAX_PKT_SIZE];

/**
 * @brief Callback function handler for Serialization HAL Transport layer events.
 */
static ser_hal_transport_events_handler_t m_events_handler = NULL;


/**
 * @brief A callback function to be used to handle a PHY module events. This function is called in
 *        an interrupt context.
 */
static void phy_events_handler(ser_phy_evt_t phy_event)
{
    uint32_t                err_code = 0;
    ser_hal_transport_evt_t hal_transp_event;

    memset(&hal_transp_event, 0, sizeof (ser_hal_transport_evt_t));
    hal_transp_event.evt_type = SER_HAL_TRANSP_EVT_TYPE_MAX;

    switch (phy_event.evt_type)
    {
        case SER_PHY_EVT_TX_PKT_SENT:
        {
            if (HAL_TRANSP_TX_STATE_TRANSMITTING == m_tx_state)
            {
                m_tx_state = HAL_TRANSP_TX_STATE_TRANSMITTED;
                err_code   = ser_hal_transport_tx_pkt_free(m_tx_buffer);
                APP_ERROR_CHECK(err_code);
                /* An event to an upper layer that a packet has been transmitted. */
                hal_transp_event.evt_type = SER_HAL_TRANSP_EVT_TX_PKT_SENT;
                m_events_handler(hal_transp_event);
            }
            else
            {
                /* Lower layer should not generate this event in current state. */
                APP_ERROR_CHECK_BOOL(false);
            }
            break;
        }

        case SER_PHY_EVT_RX_BUF_REQUEST:
        {
            /* An event to an upper layer that a packet is being scheduled to receive or to drop. */
            hal_transp_event.evt_type = SER_HAL_TRANSP_EVT_RX_PKT_RECEIVING;

            /* Receive or drop a packet. */
            if (phy_event.evt_params.rx_buf_request.num_of_bytes <= sizeof (m_rx_buffer))
            {
                if (HAL_TRANSP_RX_STATE_IDLE == m_rx_state)
                {
                    m_events_handler(hal_transp_event);
                    err_code = ser_phy_rx_buf_set(m_rx_buffer);
                    APP_ERROR_CHECK(err_code);
                    m_rx_state = HAL_TRANSP_RX_STATE_RECEIVING;
                }
                else if (HAL_TRANSP_RX_STATE_RECEIVED == m_rx_state)
                {
                    /* It is OK to get know higher layer at this point that we are going to receive
                     * a new packet even though we will start receiving when rx buffer is freed. */
                    m_events_handler(hal_transp_event);
                    m_rx_state = HAL_TRANSP_RX_STATE_RECEIVED_PENDING_BUF_REQ;
                }
                else
                {
                    /* Lower layer should not generate this event in current state. */
                    APP_ERROR_CHECK_BOOL(false);
                }
            }
            else
            {
                /* There is not enough memory but packet has to be received to dummy location. */
                if (HAL_TRANSP_RX_STATE_IDLE == m_rx_state)
                {
                    m_events_handler(hal_transp_event);
                    err_code = ser_phy_rx_buf_set(NULL);
                    APP_ERROR_CHECK(err_code);
                    m_rx_state = HAL_TRANSP_RX_STATE_DROPPING;
                }
                else if (HAL_TRANSP_RX_STATE_RECEIVED == m_rx_state)
                {
                    m_events_handler(hal_transp_event);
                    err_code = ser_phy_rx_buf_set(NULL);
                    APP_ERROR_CHECK(err_code);
                    m_rx_state = HAL_TRANSP_RX_STATE_RECEIVED_DROPPING;
                }
                else
                {
                    /* Lower layer should not generate this event in current state. */
                    APP_ERROR_CHECK_BOOL(false);
                }
            }
            break;
        }

        case SER_PHY_EVT_RX_PKT_RECEIVED:
        {
            if (HAL_TRANSP_RX_STATE_RECEIVING == m_rx_state)
            {
                m_rx_state = HAL_TRANSP_RX_STATE_RECEIVED;
                /* Generate the event to an upper layer. */
                hal_transp_event.evt_type =
                    SER_HAL_TRANSP_EVT_RX_PKT_RECEIVED;
                hal_transp_event.evt_params.rx_pkt_received.p_buffer =
                    phy_event.evt_params.rx_pkt_received.p_buffer;
                hal_transp_event.evt_params.rx_pkt_received.num_of_bytes =
                    phy_event.evt_params.rx_pkt_received.num_of_bytes;
                m_events_handler(hal_transp_event);
            }
            else
            {
                /* Lower layer should not generate this event in current state. */
                APP_ERROR_CHECK_BOOL(false);
            }
            break;
        }

        case SER_PHY_EVT_RX_PKT_DROPPED:
        {
            if (HAL_TRANSP_RX_STATE_DROPPING == m_rx_state)
            {
                /* Generate the event to an upper layer. */
                hal_transp_event.evt_type = SER_HAL_TRANSP_EVT_RX_PKT_DROPPED;
                m_events_handler(hal_transp_event);
                m_rx_state = HAL_TRANSP_RX_STATE_IDLE;
            }
            else if (HAL_TRANSP_RX_STATE_RECEIVED_DROPPING == m_rx_state)
            {
                /* Generate the event to an upper layer. */
                hal_transp_event.evt_type = SER_HAL_TRANSP_EVT_RX_PKT_DROPPED;
                m_events_handler(hal_transp_event);
                m_rx_state = HAL_TRANSP_RX_STATE_RECEIVED;
            }
            else
            {
                /* Lower layer should not generate this event in current state. */
                APP_ERROR_CHECK_BOOL(false);
            }
            break;
        }

        case SER_PHY_EVT_RX_OVERFLOW_ERROR:
        {
            /* Generate the event to an upper layer. */
            hal_transp_event.evt_type                        = SER_HAL_TRANSP_EVT_PHY_ERROR;
            hal_transp_event.evt_params.phy_error.error_type =
                SER_HAL_TRANSP_PHY_ERROR_RX_OVERFLOW;
            m_events_handler(hal_transp_event);
            break;
        }

        case SER_PHY_EVT_TX_OVERREAD_ERROR:
        {
            /* Generate the event to an upper layer. */
            hal_transp_event.evt_type                        = SER_HAL_TRANSP_EVT_PHY_ERROR;
            hal_transp_event.evt_params.phy_error.error_type =
                SER_HAL_TRANSP_PHY_ERROR_TX_OVERREAD;
            m_events_handler(hal_transp_event);
            break;
        }

        case SER_PHY_EVT_HW_ERROR:
        {
            /* Generate the event to an upper layer. */
            hal_transp_event.evt_type                        = SER_HAL_TRANSP_EVT_PHY_ERROR;
            hal_transp_event.evt_params.phy_error.error_type =
                SER_HAL_TRANSP_PHY_ERROR_HW_ERROR;
            hal_transp_event.evt_params.phy_error.hw_error_code =
                phy_event.evt_params.hw_error.error_code;
            if (HAL_TRANSP_TX_STATE_TRANSMITTING == m_tx_state)
            {
                m_tx_state = HAL_TRANSP_TX_STATE_TRANSMITTED;
                err_code   = ser_hal_transport_tx_pkt_free(phy_event.evt_params.hw_error.p_buffer);
                APP_ERROR_CHECK(err_code);
                /* An event to an upper layer that a packet has been transmitted. */
            }
            else if (HAL_TRANSP_RX_STATE_RECEIVING == m_rx_state)
            {
                m_rx_state = HAL_TRANSP_RX_STATE_RECEIVED;
                err_code   = ser_hal_transport_rx_pkt_free(phy_event.evt_params.hw_error.p_buffer);
                APP_ERROR_CHECK(err_code);
            }
            m_events_handler(hal_transp_event);

            break;
        }

        default:
        {
            APP_ERROR_CHECK_BOOL(false);
            break;
        }
    }
}

uint32_t ser_hal_transport_open(ser_hal_transport_events_handler_t events_handler)
{
    uint32_t err_code = NRF_SUCCESS;

    if ((HAL_TRANSP_RX_STATE_CLOSED != m_rx_state) || (HAL_TRANSP_TX_STATE_CLOSED != m_tx_state))
    {
        err_code = NRF_ERROR_INVALID_STATE;
    }
    else if (NULL == events_handler)
    {
        err_code = NRF_ERROR_NULL;
    }
    else
    {
        /* We have to change states before calling lower layer because ser_phy_open() function is
         * going to enable interrupts. On success an event from PHY layer can be emitted immediately
         * after return from ser_phy_open(). */
        m_rx_state = HAL_TRANSP_RX_STATE_IDLE;
        m_tx_state = HAL_TRANSP_TX_STATE_IDLE;

        m_events_handler = events_handler;

        /* Initialize a PHY module. */
        err_code = ser_phy_open(phy_events_handler);

        if (NRF_SUCCESS != err_code)
        {
            m_rx_state       = HAL_TRANSP_RX_STATE_CLOSED;
            m_tx_state       = HAL_TRANSP_TX_STATE_CLOSED;
            m_events_handler = NULL;

            if (NRF_ERROR_INVALID_PARAM != err_code)
            {
                err_code = NRF_ERROR_INTERNAL;
            }
        }
    }

    return err_code;
}


void ser_hal_transport_close(void)
{
    /* Reset generic handler for all events, reset internal states and close PHY module. */
    ser_phy_interrupts_disable();
    m_rx_state = HAL_TRANSP_RX_STATE_CLOSED;
    m_tx_state = HAL_TRANSP_TX_STATE_CLOSED;

    m_events_handler = NULL;

    ser_phy_close();
}


uint32_t ser_hal_transport_rx_pkt_free(uint8_t * p_buffer)
{
    uint32_t err_code = NRF_SUCCESS;

    ser_phy_interrupts_disable();

    if (NULL == p_buffer)
    {
        err_code = NRF_ERROR_NULL;
    }
    else if (p_buffer != m_rx_buffer)
    {
        err_code = NRF_ERROR_INVALID_ADDR;
    }
    else if (HAL_TRANSP_RX_STATE_RECEIVED == m_rx_state)
    {
        m_rx_state = HAL_TRANSP_RX_STATE_IDLE;
    }
    else if (HAL_TRANSP_RX_STATE_RECEIVED_DROPPING == m_rx_state)
    {
        m_rx_state = HAL_TRANSP_RX_STATE_DROPPING;
    }
    else if (HAL_TRANSP_RX_STATE_RECEIVED_PENDING_BUF_REQ == m_rx_state)
    {
        err_code = ser_phy_rx_buf_set(m_rx_buffer);

        if (NRF_SUCCESS == err_code)
        {
            m_rx_state = HAL_TRANSP_RX_STATE_RECEIVING;
        }
        else
        {
            err_code = NRF_ERROR_INTERNAL;
        }
    }
    else
    {
        /* Upper layer should not call this function in current state. */
        err_code = NRF_ERROR_INVALID_STATE;
    }
    ser_phy_interrupts_enable();

    return err_code;
}


uint32_t ser_hal_transport_tx_pkt_alloc(uint8_t * * pp_memory, uint16_t * p_num_of_bytes)
{
    uint32_t err_code = NRF_SUCCESS;

    if ((NULL == pp_memory) || (NULL == p_num_of_bytes))
    {
        err_code = NRF_ERROR_NULL;
    }
    else if (HAL_TRANSP_TX_STATE_CLOSED == m_tx_state)
    {
        err_code = NRF_ERROR_INVALID_STATE;
    }
    else if (HAL_TRANSP_TX_STATE_IDLE == m_tx_state)
    {
        m_tx_state      = HAL_TRANSP_TX_STATE_TX_ALLOCATED;
        *pp_memory      = &m_tx_buffer[0];
        *p_num_of_bytes = (uint16_t)sizeof (m_tx_buffer);
    }
    else
    {
        err_code = NRF_ERROR_NO_MEM;
    }

    return err_code;
}


uint32_t ser_hal_transport_tx_pkt_send(const uint8_t * p_buffer, uint16_t num_of_bytes)
{
    uint32_t err_code = NRF_SUCCESS;

    /* The buffer provided to this function must be allocated through ser_hal_transport_tx_alloc()
     * function - this assures correct state and that correct memory buffer is used. */
    if (NULL == p_buffer)
    {
        err_code = NRF_ERROR_NULL;
    }
    else if (0 == num_of_bytes)
    {
        err_code = NRF_ERROR_INVALID_PARAM;
    }
    else if (p_buffer != m_tx_buffer)
    {
        err_code = NRF_ERROR_INVALID_ADDR;
    }
    else if (num_of_bytes > sizeof (m_tx_buffer))
    {
        err_code = NRF_ERROR_DATA_SIZE;
    }
    else if (HAL_TRANSP_TX_STATE_TX_ALLOCATED == m_tx_state)
    {
        ser_phy_interrupts_disable();
        err_code = ser_phy_tx_pkt_send(p_buffer, num_of_bytes);

        if (NRF_SUCCESS == err_code)
        {
            m_tx_state = HAL_TRANSP_TX_STATE_TRANSMITTING;
        }
        else
        {
            if (NRF_ERROR_BUSY != err_code)
            {
                err_code = NRF_ERROR_INTERNAL;
            }
        }
        ser_phy_interrupts_enable();
    }
    else
    {
        err_code = NRF_ERROR_INVALID_STATE;
    }

    return err_code;
}


uint32_t ser_hal_transport_tx_pkt_free(uint8_t * p_buffer)
{
    uint32_t err_code = NRF_SUCCESS;

    if (NULL == p_buffer)
    {
        err_code = NRF_ERROR_NULL;
    }
    else if (p_buffer != m_tx_buffer)
    {
        err_code = NRF_ERROR_INVALID_ADDR;
    }
    else if ((HAL_TRANSP_TX_STATE_TX_ALLOCATED == m_tx_state) ||
             (HAL_TRANSP_TX_STATE_TRANSMITTED == m_tx_state))
    {
        /* Release TX buffer for use. */
        m_tx_state = HAL_TRANSP_TX_STATE_IDLE;
    }
    else
    {
        err_code = NRF_ERROR_INVALID_STATE;
    }

    return err_code;
}
