/**
 * Copyright (c) 2015 - 2018, Nordic Semiconductor ASA
 *
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without modification,
 * are permitted provided that the following conditions are met:
 *
 * 1. Redistributions of source code must retain the above copyright notice, this
 *    list of conditions and the following disclaimer.
 *
 * 2. Redistributions in binary form, except as embedded into a Nordic
 *    Semiconductor ASA integrated circuit in a product or a software update for
 *    such product, must reproduce the above copyright notice, this list of
 *    conditions and the following disclaimer in the documentation and/or other
 *    materials provided with the distribution.
 *
 * 3. Neither the name of Nordic Semiconductor ASA nor the names of its
 *    contributors may be used to endorse or promote products derived from this
 *    software without specific prior written permission.
 *
 * 4. This software, with or without modification, must only be used with a
 *    Nordic Semiconductor ASA integrated circuit.
 *
 * 5. Any software provided in binary form under this license must not be reverse
 *    engineered, decompiled, modified and/or disassembled.
 *
 * THIS SOFTWARE IS PROVIDED BY NORDIC SEMICONDUCTOR ASA "AS IS" AND ANY EXPRESS
 * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
 * OF MERCHANTABILITY, NONINFRINGEMENT, AND FITNESS FOR A PARTICULAR PURPOSE ARE
 * DISCLAIMED. IN NO EVENT SHALL NORDIC SEMICONDUCTOR ASA OR CONTRIBUTORS BE
 * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE
 * GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
 * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 *
 */
#include <stdint.h>
#include "boards.h"
#include "ipv6_medium.h"
#include "ipv6_medium_ble.h"
#include "ble_advdata.h"
#include "ble_hci.h"
#include "ble_srv_common.h"
#include "ble_ipsp.h"
#include "sdk_config.h"
#include "nrf_sdm.h"
#include "nrf_sdh.h"
#include "nrf_sdh_ble.h"
#ifdef COMMISSIONING_ENABLED
#include "commissioning.h"
#endif // COMMISSIONING_ENABLED

#define PUBLIC_BLE_GAP_ADDR_CREATE_FROM_EUI64(ble_gap_addr, eui64, ble_gap_addr_type)              \
             ble_gap_addr[0] = eui64[7];                                                           \
             ble_gap_addr[1] = eui64[6];                                                           \
             ble_gap_addr[2] = eui64[5];                                                           \
             ble_gap_addr[3] = eui64[2];                                                           \
             ble_gap_addr[4] = eui64[1];                                                           \
             ble_gap_addr[5] = 0x00;                                                               \
             ble_gap_addr_type = BLE_GAP_ADDR_TYPE_PUBLIC;

#define IOT_TIMER_DISABLE_API_PARAM_CHECK    0

#if (IOT_TIMER_DISABLE_API_PARAM_CHECK == 0)

#define NULL_PARAM_CHECK(PARAM)                                                                    \
        if ((PARAM) == NULL)                                                                       \
        {                                                                                          \
            return (NRF_ERROR_NULL);                                                               \
        }

#else // IOT_TIMER_DISABLE_API_PARAM_CHECK

#define NULL_PARAM_CHECK(PARAM)

#endif //IOT_TIMER_DISABLE_API_PARAM_CHECK

#define BLE_IPV6_MEDIUM_BLE_OBSERVER_PRIO   1                                                       /**< BLE observer priority. */
#define BLE_IPSP_TAG                        35                                                      /**< Identifies the L2CAP configuration used with SoftDevice. */


static ipv6_medium_instance_id_t         m_module_instance_id = 0x01;                               /**< Module instance identifier. As of today, only a single instance is supported. */
static ipv6_medium_evt_handler_t         m_ipv6_medium_evt_handler;                                 /**< Pointer to the event handler procedure of the parent layer. */
static ipv6_medium_error_handler_t       m_ipv6_medium_error_handler;                               /**< Pointer to the error handler procedure of the parent layer. */
static ble_gap_addr_t                    m_local_ble_addr;                                          /**< Local BT device address. */
static ipv6_medium_ble_gap_params_t    * m_p_node_gap_params;                                       /**< Pointer to advertising parameters to be used. */
static ipv6_medium_ble_adv_params_t    * m_p_node_adv_params;                                       /**< Pointer to GAP parameters to be used. */

#ifndef COMMISSIONING_ENABLED
static ipv6_medium_ble_gap_params_t      m_gap_params;                                              /**< Advertising parameters w/o commissioning. */
static ipv6_medium_ble_adv_params_t      m_adv_params;                                              /**< GAP parameters w/o commissioning. */
static ble_uuid_t                        m_adv_uuids[] =
                                             {
                                                 {BLE_UUID_IPSP_SERVICE, BLE_UUID_TYPE_BLE}
                                             };                                                     /**< List of available service UUIDs in advertisement data. */
#else // COMMISSIONING_ENABLED
static uint16_t                          m_conn_handle = BLE_CONN_HANDLE_INVALID;                   /**< Handle of the active connection. */
static bool                              m_connectable_mode_active = false;                         /**< Indicates if the node is in connectable mode. */
static commissioning_id_mode_cb_t        m_commissioning_id_mode_cb;
static commissioning_poweroff_cb_t       m_commissioning_power_off_cb;
static bool                              m_adv_params_applied = false;                              /**< Indicates if advertising (and GAP) parameters have been applied. */
#endif // COMMISSIONING_ENABLED

static uint8_t                           m_adv_handle = BLE_GAP_ADV_SET_HANDLE_NOT_SET;             /**< Advertising handle used to identify an advertising set. */
static uint8_t                           m_enc_advdata[BLE_GAP_ADV_SET_DATA_SIZE_MAX];              /**< Buffer for storing an encoded advertising set. */

/**@brief Struct that contains pointers to the encoded advertising data. */
static ble_gap_adv_data_t m_adv_data =
{
    .adv_data =
    {
        .p_data = m_enc_advdata,
        .len    = BLE_GAP_ADV_SET_DATA_SIZE_MAX
    },
    .scan_rsp_data =
    {
        .p_data = NULL,
        .len    = 0
        
    }
};

#if IPV6_MEDIUM_CONFIG_LOG_ENABLED

#define NRF_LOG_MODULE_NAME ipv6_medium

#define NRF_LOG_LEVEL       IPV6_MEDIUM_CONFIG_LOG_LEVEL
#define NRF_LOG_INFO_COLOR  IPV6_MEDIUM_CONFIG_INFO_COLOR
#define NRF_LOG_DEBUG_COLOR IPV6_MEDIUM_CONFIG_DEBUG_COLOR

#include "nrf_log.h"
NRF_LOG_MODULE_REGISTER();

#define IPV6M_TRC     NRF_LOG_DEBUG                                                              /**< Used for getting trace of execution in the module. */
#define IPV6M_ERR     NRF_LOG_ERROR                                                              /**< Used for logging errors in the module. */
#define IPV6M_DUMP    NRF_LOG_HEXDUMP_DEBUG                                                      /**< Used for dumping octet information to get details of bond information etc. */

#define IPV6M_ENTRY()                       IPV6M_TRC(">> %s", __func__)
#define IPV6M_EXIT()                        IPV6M_TRC("<< %s", __func__)

#else // IPV6_MEDIUM_CONFIG_LOG_ENABLED

#define IPV6M_TRC(...)                                                                           /**< Disables traces. */
#define IPV6M_DUMP(...)                                                                          /**< Disables dumping of octet streams. */
#define IPV6M_ERR(...)                                                                           /**< Disables error logs. */

#define IPV6M_ENTRY(...)
#define IPV6M_EXIT(...)

#endif // IPV6_MEDIUM_CONFIG_LOG_ENABLED


#ifndef COMMISSIONING_ENABLED

/**@brief Function for setting advertisement parameters.
 *
 * @details These parameters are applied if the Commissioning module is
 *          not used or in Joining mode.
 */
static void adv_params_set(void)
{
    IPV6M_ENTRY();
    memset(&m_adv_params, 0x00, sizeof(m_adv_params));

    m_adv_params.advdata.name_type               = BLE_ADVDATA_FULL_NAME;
    m_adv_params.advdata.flags                   = BLE_GAP_ADV_FLAG_BR_EDR_NOT_SUPPORTED;
    m_adv_params.advdata.uuids_complete.uuid_cnt = sizeof(m_adv_uuids) / sizeof(m_adv_uuids[0]);
    m_adv_params.advdata.uuids_complete.p_uuids  = m_adv_uuids;

    m_adv_params.advparams.properties.type = BLE_GAP_ADV_TYPE_CONNECTABLE_SCANNABLE_UNDIRECTED;
    m_adv_params.advparams.p_peer_addr     = NULL; // Undirected advertisement.
    m_adv_params.advparams.filter_policy   = BLE_GAP_ADV_FP_ANY;
    m_adv_params.advparams.interval        = APP_ADV_ADV_INTERVAL;
    m_adv_params.advparams.duration        = APP_ADV_DURATION;

    IPV6M_EXIT();
}

/**@brief Function for setting GAP parameters.
 *
 * @details These parameters are applied if the Commissioning module is
 *          not used or in Joining mode.
 */
static void gap_params_set(void)
{
    memset(&m_gap_params, 0x00, sizeof(m_gap_params));

    BLE_GAP_CONN_SEC_MODE_SET_OPEN(&m_gap_params.sec_mode);

    m_gap_params.appearance = BLE_APPEARANCE_UNKNOWN;

    m_gap_params.p_dev_name   = (const uint8_t *)DEVICE_NAME;
    m_gap_params.dev_name_len = strlen(DEVICE_NAME);

    m_gap_params.gap_conn_params.min_conn_interval = (uint16_t)MIN_CONN_INTERVAL;
    m_gap_params.gap_conn_params.max_conn_interval = (uint16_t)MAX_CONN_INTERVAL;
    m_gap_params.gap_conn_params.slave_latency     = SLAVE_LATENCY;
    m_gap_params.gap_conn_params.conn_sup_timeout  = CONN_SUP_TIMEOUT;
}

#endif // COMMISSIONING_ENABLED


/**@brief Function for applying the advertisement parameters.
 *
 * @details Encodes the required advertising data and passes it to the stack.
 */
static void adv_params_apply(void)
{
    uint32_t err_code;


    err_code = ble_advdata_encode(&m_p_node_adv_params->advdata, m_adv_data.adv_data.p_data, &m_adv_data.adv_data.len);
    APP_ERROR_CHECK(err_code);
#ifndef COMMISSIONING_ENABLED
    err_code = sd_ble_gap_adv_set_configure(&m_adv_handle, &m_adv_data, &m_adv_params.advparams);
    APP_ERROR_CHECK(err_code);
#else
    err_code = sd_ble_gap_adv_set_configure(&m_adv_handle, &m_adv_data, &m_p_node_adv_params->advparams);
    APP_ERROR_CHECK(err_code);
#endif
}


/**@brief Function for applying the GAP configuration.
 *
 * @details This function sets up all the necessary GAP (Generic Access Profile) parameters of the
 *          device including the device name, appearance, and the preferred connection parameters.
 */
static void gap_params_apply(void)
{
    uint32_t err_code;

    err_code = sd_ble_gap_device_name_set(&m_p_node_gap_params->sec_mode, \
                                          m_p_node_gap_params->p_dev_name, \
                                          m_p_node_gap_params->dev_name_len);
    APP_ERROR_CHECK(err_code);

    err_code = sd_ble_gap_appearance_set(m_p_node_gap_params->appearance);
    APP_ERROR_CHECK(err_code);

    err_code = sd_ble_gap_ppcp_set(&m_p_node_gap_params->gap_conn_params);
    APP_ERROR_CHECK(err_code);
}


/**@brief Function for handling the application's BLE Stack events and
 *        passing them on to the applications as generic transport medium events.
 *
 * @param[in]   p_ble_evt   Bluetooth stack event.
 */
static void on_ble_evt(ble_evt_t const * p_ble_evt)
{
    ipv6_medium_evt_t ipv6_medium_evt;

    memset(&ipv6_medium_evt, 0x00, sizeof(ipv6_medium_evt));
    ipv6_medium_evt.ipv6_medium_instance_id.ipv6_medium_instance_id   = m_module_instance_id;
    ipv6_medium_evt.ipv6_medium_instance_id.ipv6_medium_instance_type = IPV6_MEDIUM_ID_BLE;
    ipv6_medium_evt.medium_specific.ble.p_ble_evt                     = (ble_evt_t*)p_ble_evt;

    ipv6_medium_error_t ipv6_medium_error;
    memset(&ipv6_medium_error, 0x00, sizeof(ipv6_medium_error));
    ipv6_medium_error.ipv6_medium_instance_id.ipv6_medium_instance_id   = m_module_instance_id;
    ipv6_medium_error.ipv6_medium_instance_id.ipv6_medium_instance_type = IPV6_MEDIUM_ID_BLE;

    bool do_notify_event = false;
    bool do_notify_error = false;

    switch (p_ble_evt->header.evt_id)
    {
        case BLE_GAP_EVT_CONNECTED:
        {
#ifdef COMMISSIONING_ENABLED
            m_conn_handle = p_ble_evt->evt.gap_evt.conn_handle;
#endif // COMMISSIONING_ENABLED
            ipv6_medium_evt.ipv6_medium_evt_id = IPV6_MEDIUM_EVT_CONN_UP;
            do_notify_event = true;

            break;
        }
        case BLE_GAP_EVT_DISCONNECTED:
        {
#ifdef COMMISSIONING_ENABLED
            m_conn_handle = BLE_CONN_HANDLE_INVALID;
#endif // COMMISSIONING_ENABLED
            ipv6_medium_evt.ipv6_medium_evt_id = IPV6_MEDIUM_EVT_CONN_DOWN;
            do_notify_event = true;

            break;
        }
        case BLE_GAP_EVT_ADV_SET_TERMINATED:
        {
            if (p_ble_evt->evt.gap_evt.params.adv_set_terminated.reason == BLE_GAP_EVT_ADV_SET_TERMINATED_REASON_TIMEOUT)
            {
                ipv6_medium_evt.ipv6_medium_evt_id = IPV6_MEDIUM_EVT_CONNECTABLE_MODE_EXIT;
                do_notify_event = true;
            }
            else
            {
                // This is not necessarily an error, only added here to show error handler usage.
                ipv6_medium_error.medium_specific.ble.dummy_value = 0x13;
                do_notify_error = true;
            }
            break;
        }
        default:
        {
            ipv6_medium_evt.ipv6_medium_evt_id = IPV6_MEDIUM_EVT_PHY_SPECIFIC;
            do_notify_event = true;

            break;
        }
    }

    ble_ipsp_evt_handler(p_ble_evt);

    if (do_notify_event == true)
    {
        m_ipv6_medium_evt_handler(&ipv6_medium_evt);
    }
    if (do_notify_error == true)
    {
        m_ipv6_medium_error_handler(&ipv6_medium_error);
    }
}

/*
 * @brief Function for handling BLE events.
 *
 * @param[in]   p_ble_evt       Event received from the BLE stack.
 * @param[in]   p_context       Context.
 */
static void ble_evt_handler(const ble_evt_t * p_ble_evt, void * p_context)
{
    UNUSED_PARAMETER(p_context);
#ifdef COMMISSIONING_ENABLED
    commissioning_ble_evt_handler(p_ble_evt);
    ble_ncfgs_ble_evt_handler(p_ble_evt);
#endif // COMMISSIONING_ENABLED

    on_ble_evt(p_ble_evt);
}




/**@brief Function for initializing the BLE stack.
 *
 * @details Initializes the SoftDevice and the BLE event interrupt.
 */
static uint32_t ble_stack_init(void)
{
    ret_code_t    err_code;
    uint32_t      ram_start = 0;
    ble_cfg_t     ble_cfg;

    err_code = nrf_sdh_enable_request();

    if (err_code == NRF_SUCCESS)
    {
        // Fetch the start address of the application RAM.
        err_code = nrf_sdh_ble_app_ram_start_get(&ram_start);
    }

    if (err_code == NRF_SUCCESS)
    {
        // Configure the maximum number of connections.
        memset(&ble_cfg, 0, sizeof(ble_cfg));
        ble_cfg.gap_cfg.role_count_cfg.periph_role_count  = BLE_IPSP_MAX_CHANNELS;
        ble_cfg.gap_cfg.role_count_cfg.central_role_count = 0;
        ble_cfg.gap_cfg.role_count_cfg.central_sec_count  = 0;
        err_code = sd_ble_cfg_set(BLE_GAP_CFG_ROLE_COUNT, &ble_cfg, ram_start);
    }

    if (err_code == NRF_SUCCESS)
    {
        memset(&ble_cfg, 0, sizeof(ble_cfg));

        // Configure total number of connections.
        ble_cfg.conn_cfg.conn_cfg_tag                     = BLE_IPSP_TAG;
        ble_cfg.conn_cfg.params.gap_conn_cfg.conn_count   = BLE_IPSP_MAX_CHANNELS;
        ble_cfg.conn_cfg.params.gap_conn_cfg.event_length = BLE_GAP_EVENT_LENGTH_DEFAULT;
        err_code = sd_ble_cfg_set(BLE_CONN_CFG_GAP, &ble_cfg, ram_start);

    }

    if (err_code ==  NRF_SUCCESS)
    {
        memset(&ble_cfg, 0, sizeof(ble_cfg));

        // Configure the number of custom UUIDS.
#ifdef COMMISSIONING_ENABLED
        ble_cfg.common_cfg.vs_uuid_cfg.vs_uuid_count = 1;
#else
        ble_cfg.common_cfg.vs_uuid_cfg.vs_uuid_count = 0;
#endif // COMMISSIONING_ENABLED

        err_code = sd_ble_cfg_set(BLE_COMMON_CFG_VS_UUID, &ble_cfg, ram_start);
    }

    if (err_code == NRF_SUCCESS)
    {
        memset(&ble_cfg, 0, sizeof(ble_cfg));

        // Set L2CAP channel configuration
        ble_cfg.conn_cfg.conn_cfg_tag                        = BLE_IPSP_TAG;
        ble_cfg.conn_cfg.params.l2cap_conn_cfg.rx_mps        = BLE_IPSP_RX_MPS;
        ble_cfg.conn_cfg.params.l2cap_conn_cfg.rx_queue_size = BLE_IPSP_RX_BUFFER_COUNT;
        ble_cfg.conn_cfg.params.l2cap_conn_cfg.tx_mps        = BLE_IPSP_TX_MPS;
        ble_cfg.conn_cfg.params.l2cap_conn_cfg.tx_queue_size = 1;
        ble_cfg.conn_cfg.params.l2cap_conn_cfg.ch_count      = 1; // One IPSP channel per link.
        err_code = sd_ble_cfg_set(BLE_CONN_CFG_L2CAP, &ble_cfg, ram_start);
    }

    if (err_code == NRF_SUCCESS)
    {
        memset(&ble_cfg, 0, sizeof(ble_cfg));

        // Set the ATT table size.
#ifdef COMMISSIONING_ENABLED
        ble_cfg.gatts_cfg.attr_tab_size.attr_tab_size = 1024;
#else
        ble_cfg.gatts_cfg.attr_tab_size.attr_tab_size = 256;
#endif // COMMISSIONING_ENABLED
        err_code = sd_ble_cfg_set(BLE_GATTS_CFG_ATTR_TAB_SIZE, &ble_cfg, ram_start);
    }

    if (err_code ==  NRF_SUCCESS)
    {
        err_code = nrf_sdh_ble_enable(&ram_start);
    }

    NRF_SDH_BLE_OBSERVER(m_ble_evt_observer, BLE_IPV6_MEDIUM_BLE_OBSERVER_PRIO, ble_evt_handler, NULL);

    return err_code;
}


uint32_t ipv6_medium_connectable_mode_enter(ipv6_medium_instance_id_t ipv6_medium_instance_id)
{
    IPV6M_ENTRY();

    if (ipv6_medium_instance_id != m_module_instance_id)
    {
        return NRF_ERROR_INVALID_PARAM;
    }

#ifdef COMMISSIONING_ENABLED
    if (m_adv_params_applied == false)
    {
        // Apply advertising (and GAP) parameters, if not applied when node mode changed.
        commissioning_gap_params_get(&m_p_node_gap_params);
        commissioning_adv_params_get(&m_p_node_adv_params);
        gap_params_apply();
        adv_params_apply();
    }
    m_adv_params_applied = false;
#endif // COMMISSIONING_ENABLED

    adv_params_apply();

    uint32_t err_code = sd_ble_gap_adv_start(m_adv_handle, BLE_IPSP_TAG);
#ifdef COMMISSIONING_ENABLED
    if (err_code == NRF_SUCCESS)
    {
        m_connectable_mode_active = true;
    }
#endif // COMMISSIONING_ENABLED
    IPV6M_EXIT();
    return err_code;
}


uint32_t ipv6_medium_connectable_mode_exit(ipv6_medium_instance_id_t ipv6_medium_instance_id)
{
    if (ipv6_medium_instance_id != m_module_instance_id)
    {
        return NRF_ERROR_INVALID_PARAM;
    }

    uint32_t err_code = sd_ble_gap_adv_stop(m_adv_handle);
#ifdef COMMISSIONING_ENABLED
    if (err_code == NRF_SUCCESS)
    {
        m_connectable_mode_active = false;
    }
#endif // COMMISSIONING_ENABLED
    return err_code;
}


uint32_t ipv6_medium_eui48_get(ipv6_medium_instance_id_t   ipv6_medium_instance_id, \
                               eui48_t                   * p_ipv6_medium_eui48)
{
    if (ipv6_medium_instance_id != m_module_instance_id)
    {
        return NRF_ERROR_INVALID_PARAM;
    }
    ble_gap_addr_t local_ble_addr;
    uint32_t err_code = sd_ble_gap_addr_get(&local_ble_addr);

    memcpy(p_ipv6_medium_eui48->identifier, local_ble_addr.addr, EUI_48_SIZE);

    return err_code;
}


uint32_t ipv6_medium_eui48_set(ipv6_medium_instance_id_t   ipv6_medium_instance_id, \
                               eui48_t                   * p_ipv6_medium_eui48)
{
    if (ipv6_medium_instance_id != m_module_instance_id)
    {
        return NRF_ERROR_INVALID_PARAM;
    }
    if (p_ipv6_medium_eui48->identifier[5] != 0x00)
    {
        return NRF_ERROR_INVALID_PARAM;
    }

    m_local_ble_addr.addr_type = BLE_GAP_ADDR_TYPE_PUBLIC;
    memcpy(m_local_ble_addr.addr, p_ipv6_medium_eui48->identifier, EUI_48_SIZE);

    return sd_ble_gap_addr_set(&m_local_ble_addr);
}


uint32_t ipv6_medium_eui64_get(ipv6_medium_instance_id_t   ipv6_medium_instance_id, \
                               eui64_t                   * p_ipv6_medium_eui64)
{
    if (ipv6_medium_instance_id != m_module_instance_id)
    {
        return NRF_ERROR_INVALID_PARAM;
    }
    ble_gap_addr_t local_ble_addr;

    uint32_t err_code = sd_ble_gap_addr_get(&local_ble_addr);
    APP_ERROR_CHECK(err_code);

    IPV6_EUI64_CREATE_FROM_EUI48(p_ipv6_medium_eui64->identifier,
                                 local_ble_addr.addr,
                                 local_ble_addr.addr_type);
    return NRF_SUCCESS;
}


uint32_t ipv6_medium_eui64_set(ipv6_medium_instance_id_t   ipv6_medium_instance_id, \
                               eui64_t                   * p_ipv6_medium_eui64)
{
    if (ipv6_medium_instance_id != m_module_instance_id)
    {
        return NRF_ERROR_INVALID_PARAM;
    }
    if ((p_ipv6_medium_eui64->identifier[0] != 0x02)  ||
        (p_ipv6_medium_eui64->identifier[3] != 0xFF)  ||
        (p_ipv6_medium_eui64->identifier[4] != 0xFE))
    {
        return NRF_ERROR_INVALID_PARAM;
    }

    ble_gap_addr_t local_ble_addr;

    PUBLIC_BLE_GAP_ADDR_CREATE_FROM_EUI64(local_ble_addr.addr, \
                                          p_ipv6_medium_eui64->identifier, \
                                          local_ble_addr.addr_type);

    return sd_ble_gap_addr_set(&local_ble_addr);
}


#ifdef COMMISSIONING_ENABLED

void commissioning_evt_handler(commissioning_evt_t * p_commissioning_evt)
{
    IPV6M_ENTRY();

    switch (p_commissioning_evt->commissioning_evt_id)
    {
        case COMMISSIONING_EVT_CONFIG_MODE_ENTER:
            // Fall-through.
        case COMMISSIONING_EVT_JOINING_MODE_ENTER:
        {
            m_commissioning_power_off_cb(p_commissioning_evt->power_off_enable_requested);

            if (m_conn_handle != BLE_CONN_HANDLE_INVALID)
            {
                // Making sure that advertising (and GAP) parameters are
                // applied when entering connectable mode the next time.
                m_adv_params_applied = false;
                UNUSED_VARIABLE(sd_ble_gap_disconnect(m_conn_handle, \
                                                      BLE_HCI_REMOTE_USER_TERMINATED_CONNECTION));
            }
            else
            {
                bool do_return_to_connectable_mode = m_connectable_mode_active;
                UNUSED_VARIABLE(ipv6_medium_connectable_mode_exit(m_module_instance_id));

                commissioning_gap_params_get(&m_p_node_gap_params);
                commissioning_adv_params_get(&m_p_node_adv_params);
                gap_params_apply();
                adv_params_apply();
                // Advertising and GAP parameters applied, making sure that
                // it is not repeated when entering connectable mode the next time.
                m_adv_params_applied = true;

                if (do_return_to_connectable_mode == true)
                {
                    // Restart connectable mode, if the node was in connectable mode applying
                    // the new parameters.
                    UNUSED_VARIABLE(ipv6_medium_connectable_mode_enter(m_module_instance_id));
                }
            }

            break;
        }
        case COMMISSIONING_EVT_IDENTITY_MODE_ENTER:
        {
            m_commissioning_id_mode_cb(CMD_IDENTITY_MODE_ENTER);

            break;
        }
        case COMMISSIONING_EVT_IDENTITY_MODE_EXIT:
        {
            m_commissioning_id_mode_cb(CMD_IDENTITY_MODE_EXIT);

            break;
        }
        default:
        {
            // No implementation needed.
            break;
        }
    }

    IPV6M_EXIT();
}

#endif // COMMISSIONING_ENABLED


uint32_t ipv6_medium_init(ipv6_medium_init_params_t * p_init_param,          \
                          ipv6_medium_type_t          desired_medium_type,   \
                          ipv6_medium_instance_t    * p_new_medium_instance)
{
    IPV6M_ENTRY();
    uint32_t err_code = NRF_SUCCESS;
    NULL_PARAM_CHECK(p_init_param->ipv6_medium_evt_handler);
    if (desired_medium_type != IPV6_MEDIUM_ID_BLE)
    {
        return NRF_ERROR_INVALID_PARAM;
    }

    m_ipv6_medium_evt_handler   = p_init_param->ipv6_medium_evt_handler;
    m_ipv6_medium_error_handler = p_init_param->ipv6_medium_error_handler;

    p_new_medium_instance->ipv6_medium_instance_type = IPV6_MEDIUM_ID_BLE;
    p_new_medium_instance->ipv6_medium_instance_id   = m_module_instance_id;

    err_code = ble_stack_init();
    if (err_code != NRF_SUCCESS)
    {
        return err_code;
    }

#ifndef COMMISSIONING_ENABLED
    gap_params_set();
    adv_params_set();
    m_p_node_gap_params = &m_gap_params;
    m_p_node_adv_params = &m_adv_params;
    gap_params_apply();
#else // COMMISSIONING_ENABLED
    m_commissioning_id_mode_cb   = p_init_param->commissioning_id_mode_cb;
    m_commissioning_power_off_cb = p_init_param->commissioning_power_off_cb;

    commissioning_init_params_t init_param;
    memset(&init_param, 0x00, sizeof(init_param));
    init_param.commissioning_evt_handler = commissioning_evt_handler;
    uint8_t new_mode;
    err_code = commissioning_init(&init_param, \
                                  &new_mode);

    commissioning_node_mode_change(new_mode);
#endif // COMMISSIONING_ENABLED

    IPV6M_EXIT();
    return err_code;
}
