// nRF5 SDK includes
#include <nrf_radio.h>
#include <nrf_delay.h>
#include <nrf_drv_clock.h>
#include <app_scheduler.h>
#include <sdk_macros.h>

// Local includes
#include "rf.h"
#include "ble/bf_ble.h"
#include "status.h"
#include "debug/log.h"

static uint16_t CwFreq;
static uint8_t CwTxPower;
static bool IsRunning = false;

/****************************************************************************/
/* INTERNAL FUNCTIONS DEFINITIONS                                           */
/****************************************************************************/
//----------------------------------------------------------------------------
static bool IsTxPowerValid(int8_t txPower)
{
    switch ((uint8_t)txPower)
    {
        case NRF_RADIO_TXPOWER_POS8DBM:
        case NRF_RADIO_TXPOWER_POS7DBM:
        case NRF_RADIO_TXPOWER_POS6DBM:
        case NRF_RADIO_TXPOWER_POS5DBM:
        case NRF_RADIO_TXPOWER_POS4DBM:
        case NRF_RADIO_TXPOWER_POS3DBM:
        case NRF_RADIO_TXPOWER_POS2DBM:
        case NRF_RADIO_TXPOWER_0DBM:
        case NRF_RADIO_TXPOWER_NEG4DBM:
        case NRF_RADIO_TXPOWER_NEG8DBM:
        case NRF_RADIO_TXPOWER_NEG12DBM:
        case NRF_RADIO_TXPOWER_NEG16DBM:
        case NRF_RADIO_TXPOWER_NEG20DBM:
        case NRF_RADIO_TXPOWER_NEG30DBM:
        case NRF_RADIO_TXPOWER_NEG40DBM:
            return true;
        default:
            return false;
    }
}

//----------------------------------------------------------------------------
void StartCwSched(void *evtData, uint16_t evtSize)
{
    // SoftDevice must be disabled to control RADIO peripheral
    if (Ble_IsEnabled())
    {
        ret_code_t ret = Ble_Disable();
        if (ret != STATUS_SUCCESS)
        {
            LOG_ERROR("Error when disabling BLE stack: %d", ret);
            return;
        }
    }

    // Request HF crystal oscillator for proper RADIO behaviour
    if (!IsRunning)
    {
        nrf_drv_clock_hfclk_request(NULL);
        while (!nrf_drv_clock_hfclk_is_running())
        {
            // Wait until HF clock is started
        }
    }

    // Power-cycle the RADIO to re-initialize it
    nrf_radio_task_trigger(NRF_RADIO_TASK_DISABLE);
    nrf_radio_power_set(false);
    nrf_delay_ms(10);
    nrf_radio_power_set(true);

    // Configure and start the RADIO
    nrf_radio_mode_set(NRF_RADIO_MODE_BLE_1MBIT);
    nrf_radio_frequency_set(CwFreq);
    nrf_radio_txpower_set(CwTxPower);
    nrf_radio_task_trigger(NRF_RADIO_TASK_TXEN);

    IsRunning = true;
}

/****************************************************************************/
/* EXTERNAL FUNCTIONS DEFINITIONS                                           */
/****************************************************************************/
//----------------------------------------------------------------------------
ret_code_t Rf_StartCw(uint16_t freq, int8_t txPower)
{
    // Check frequency is valid
    if ((freq < 2360) || (freq > 2500))
    {
        return NRF_ERROR_INVALID_PARAM;
    }

    // Check TX power is valid
    if (!IsTxPowerValid(txPower))
    {
        return NRF_ERROR_INVALID_PARAM;
    }

    LOG_INFO("Starting RF continuous wave - freq: %dMHz - tx_power: %ddBm", freq, txPower);

    CwFreq = freq;
    CwTxPower = txPower;
    // Go through scheduler to avoid issue when disabling BLE stack from a BLE event observer
    return app_sched_event_put(NULL, 0, &StartCwSched);
}

//----------------------------------------------------------------------------
ret_code_t Rf_Stop(void)
{
    if (IsRunning)
    {
        nrf_drv_clock_hfclk_release();
        nrf_radio_task_trigger(NRF_RADIO_TASK_DISABLE);
        IsRunning = false;

        // Enable BLE stack
        return Ble_Enable();
    }

    return NRF_SUCCESS;
}