#include "sdk_config.h"
#include "advertising.h"
#include "ble_types.h"
#include "ble_nus.h"
#include "bsp.h"
#include "bsp_btn_ble.h"
#include "error.h"
#include "nrf_log.h"
#include "nrf_sdh_ble.h"

#define APP_ADV_INTERVAL               800             /**< The advertising interval (in units of 0.625 ms. This value corresponds to 40 ms). */
#define APP_ADV_DURATION               1800            /**< The advertising duration (180 seconds) in units of 10 milliseconds. */         
#define APP_ADV_FAST_INTERVAL          40              /**< The advertising interval (in units of 0.625 ms. This value corresponds to 25 ms). */
#define APP_ADV_SLOW_INTERVAL          3200            /**< Slow advertising interval (in units of 0.625 ms. This value corresponds to 2 seconds). */
#define APP_ADV_FAST_TIMEOUT           30              /**< The duration of the fast advertising period (in seconds). */
#define APP_ADV_SLOW_TIMEOUT           180             /**< The advertising timeout in units of seconds. */

#define NUS_SERVICE_UUID_TYPE          BLE_UUID_TYPE_VENDOR_BEGIN                  /**< UUID type for the Nordic UART Service (vendor specific). */

typedef enum {
    ADV_STATE_IDLE,               /**< Idle; no connectable advertising is ongoing. */
    ADV_STATE_DIRECTED_HIGH_DUTY, /**< Directed advertising (high duty cycle) attempts to connect to the most recently disconnected peer. */
    ADV_STATE_DIRECTED,           /**< Directed advertising (low duty cycle) attempts to connect to the most recently disconnected peer. */
    ADV_STATE_FAST,               /**< Fast advertising will connect to any peer device, or filter with a whitelist if one exists. */
    ADV_STATE_SLOW,               /**< Slow advertising is similar to fast advertising. By default, it uses a longer advertising interval and time-out than fast advertising. However, these options are defined by the user. */
} adv_state_t;

BLE_ADVERTISING_DEF(_gAdvertising);  /**< Advertising module instance. */ 

static ble_uuid_t _gAdvUuids[]          = {                                         /**< Universally unique service identifier. */ 
    {BLE_UUID_NUS_SERVICE, NUS_SERVICE_UUID_TYPE}
};
static adv_state_t _gAdvState = ADV_STATE_IDLE;

/**@brief Function for putting the chip into sleep mode.
 *
 * @note This function will not return.
 */
#if 0 
static void _sleepModeEnter(void) {
    ret_code_t errCode;
    errCode = bsp_indication_set(BSP_INDICATE_IDLE);
    if (errCode != NRF_SUCCESS) {
        NRF_LOG_WARNING("bsp_indication_set() failed, errCode=0x%x\n", errCode);
        return;
    }

    // Prepare wakeup buttons.
    errCode = bsp_btn_ble_sleep_mode_prepare();
    if (errCode != NRF_SUCCESS) {
        NRF_LOG_WARNING("bsp_btn_ble_sleep_mode_prepare() failed, errCode=0x%x\n", errCode);
        return;
    }

    // Go to system-off mode (this function will not return; wakeup will cause a reset).
    errCode = sd_power_system_off();
#ifndef DEBUG_NRF    
    if (errCode != NRF_SUCCESS) {
        NRF_LOG_WARNING("sd_power_system_off() failed, errCode=0x%x\n", errCode);
        return;
    }
#endif
}
#endif

/**@brief Function for handling advertising events.
 *
 * @details This function is called for advertising events that are passed to the application.
 *
 * @param[in] ble_adv_evt  Advertising event.
 */
static void _advEvtHandler(ble_adv_evt_t evt) {
    ret_code_t errCode;

    switch (evt) {
        case BLE_ADV_EVT_FAST:
        {    
            NRF_LOG_INFO("BLE_ADV_EVT_FAST %s\n", nrf_cal_get_time_string(false));
            _gAdvState = ADV_STATE_FAST;
            errCode = bsp_indication_set(BSP_INDICATE_ADVERTISING);
            if (errCode != NRF_SUCCESS) {
                NRF_LOG_WARNING("bsp_indication_set(BSP_INDICATE_ADVERTISING) failed, errCode=0x%x\n", errCode);
                return;
            }
        }
        break;

        case BLE_ADV_EVT_SLOW:
        {    
            NRF_LOG_INFO("BLE_ADV_EVT_SLOW %s\n", nrf_cal_get_time_string(false));
            _gAdvState = ADV_STATE_SLOW;
            errCode = bsp_indication_set(BSP_INDICATE_ADVERTISING_SLOW);
            if (errCode != NRF_SUCCESS) {
                NRF_LOG_WARNING("bsp_indication_set(BSP_INDICATE_ADVERTISING_SLOW) failed, errCode=0x%x\n", errCode);
                return;
            }
        }
        break;
                    
        case BLE_ADV_EVT_IDLE:
        {   
            NRF_LOG_INFO("BLE_ADV_EVT_IDLE %s\n", nrf_cal_get_time_string(false));
            _gAdvState = ADV_STATE_IDLE;
        }
        break;

        default:
        break;
    }
}

#if 0
int advertisingInit(void) {
    ret_code_t errCode;
    ble_advertising_init_t init;

    memset(&init, 0, sizeof(init));

    init.advdata.name_type               = BLE_ADVDATA_FULL_NAME;
    init.advdata.include_appearance      = false;
    init.advdata.flags                   = BLE_GAP_ADV_FLAGS_LE_ONLY_GENERAL_DISC_MODE;
    init.advdata.uuids_complete.uuid_cnt = sizeof(_gAdvUuids) / sizeof(_gAdvUuids[0]);
    init.advdata.uuids_complete.p_uuids  = _gAdvUuids;

    init.srdata.uuids_complete.uuid_cnt = sizeof(_gAdvUuids) / sizeof(_gAdvUuids[0]);
    init.srdata.uuids_complete.p_uuids  = _gAdvUuids;

    init.config.ble_adv_whitelist_enabled = false;
    init.config.ble_adv_fast_enabled  = true;
    init.config.ble_adv_fast_interval = APP_ADV_FAST_INTERVAL;
    init.config.ble_adv_fast_timeout  = APP_ADV_FAST_DURATION;
    init.config.ble_adv_slow_enabled  = true;
    init.config.ble_adv_slow_interval = APP_ADV_SLOW_INTERVAL;
    init.config.ble_adv_slow_timeout  = APP_ADV_SLOW_DURATION;
    init.evt_handler = _advertisingEvtHandler;

    errCode = ble_advertising_init(&_gAdvertising, &init);
    if (errCode != NRF_SUCCESS) {
        NRF_LOG_WARNING("ble_advertising_init() failed errCode=0x%x", errCode);
        return ERROR_ADVERTISING_INITIALIZE_FAIL;
    }

    ble_advertising_conn_cfg_tag_set(&_gAdvertising, APP_BLE_CONN_CFG_TAG);

    return 0;
}
#else
int advInit(void) {
    uint32_t               errCode;
    ble_advertising_init_t init;

    memset(&init, 0, sizeof(init));

    init.advdata.name_type              = BLE_ADVDATA_FULL_NAME;
    init.advdata.include_appearance     = false;
    init.advdata.flags                  = BLE_GAP_ADV_FLAGS_LE_ONLY_LIMITED_DISC_MODE;
    init.srdata.uuids_complete.uuid_cnt = sizeof(_gAdvUuids) / sizeof(_gAdvUuids[0]);
    init.srdata.uuids_complete.p_uuids  = _gAdvUuids;
    init.config.ble_adv_fast_enabled    = true;
    //init.config.ble_adv_fast_interval = APP_ADV_INTERVAL;
    //init.config.ble_adv_fast_timeout  = APP_ADV_DURATION;
    init.config.ble_adv_fast_enabled    = true;
    init.config.ble_adv_fast_interval   = APP_ADV_FAST_INTERVAL;
    init.config.ble_adv_fast_timeout    = APP_ADV_FAST_TIMEOUT;
    init.config.ble_adv_slow_enabled    = true;
    init.config.ble_adv_slow_interval   = APP_ADV_SLOW_INTERVAL;
    init.config.ble_adv_slow_timeout    = APP_ADV_SLOW_TIMEOUT;
    init.evt_handler                    = _advEvtHandler;

    errCode = ble_advertising_init(&_gAdvertising, &init);
    if (errCode != NRF_SUCCESS) {
        NRF_LOG_WARNING("ble_advertising_init() failed errCode=0x%x", errCode);
        return ERROR_ADVERTISING_INITIALIZE_FAIL;
    }

    ble_advertising_conn_cfg_tag_set(&_gAdvertising, APP_BLE_CONN_CFG_TAG);

    _gAdvState = ADV_STATE_IDLE;

    return 0;
}

#endif


ble_adv_modes_config_t advConfigGet(void) {
    ble_adv_modes_config_t config;
    memset(&config, 0, sizeof(ble_adv_modes_config_t));

    config.ble_adv_fast_enabled           = true;
    config.ble_adv_fast_interval          = APP_ADV_INTERVAL;
    config.ble_adv_fast_timeout           = APP_ADV_DURATION;
    config.ble_adv_on_disconnect_disabled = true;

    return config;
}

void advModesConfigSet(void) {
    const ble_adv_modes_config_t config = advConfigGet();
    ble_advertising_modes_config_set(&_gAdvertising, &config);
}    

/* Function for starting advertising. */
int advStart(void) {
    if (_gAdvState == ADV_STATE_IDLE) {
        const ret_code_t errCode = ble_advertising_start(&_gAdvertising, BLE_ADV_MODE_FAST);
        if (errCode != NRF_SUCCESS) {
            NRF_LOG_WARNING("ble_advertising_start() failed errCode=0x%x", errCode);
            return ERROR_ADVERTISING_START_FAIL;
        }     
    }
    
    return 0;
}

