/**
 * Copyright (c) 2016 - 2020, 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 "nrf_ble_escs.h"
#include <string.h>
#include "es_app_config.h"

#ifdef BLE_HANDLER_DEBUG
    #include "SEGGER_RTT.h"
    #define DEBUG_PRINTF SEGGER_RTT_printf
#else
    #define DEBUG_PRINTF(...)
#endif

#define EID_BUFF_SIZE       64

typedef struct
{
    uint16_t val_handle;
    uint16_t uuid;
} val_handle_to_uuid_t;

static ble_add_char_params_t BROADCAST_CAP_CHAR_INIT =
{
    .uuid        = BLE_UUID_ESCS_BROADCAST_CAP_CHAR,
    .read_access = SEC_OPEN,
    .char_props  =
    {
       .read = 1,
    },
    .is_defered_read = true,
    .is_var_len      = true,
    .init_len        = NRF_BLE_ESCS_BROADCAST_CAP_LEN,
    .max_len         = NRF_BLE_ESCS_BROADCAST_CAP_LEN,
};

static ble_add_char_params_t ACTIVE_SLOT_CHAR_INIT =
{
    .uuid         = BLE_UUID_ESCS_ACTIVE_SLOT_CHAR,
    .read_access  = SEC_OPEN,
    .write_access = SEC_OPEN,
    .char_props   =
    {
       .read  = 1,
       .write = 1,
    },
    .is_defered_read  = true,
    .is_defered_write = true,
    .is_value_user    = true,
    .init_len         = sizeof(nrf_ble_escs_active_slot_t),
    .max_len          = sizeof(nrf_ble_escs_active_slot_t),
};

static ble_add_char_params_t ADV_INTERVAL_CHAR_INIT =
{
    .uuid         = BLE_UUID_ESCS_ADV_INTERVAL_CHAR,
    .read_access  = SEC_OPEN,
    .write_access = SEC_OPEN,
    .char_props   =
    {
       .read  = 1,
       .write = 1,
    },
    .is_defered_read  = true,
    .is_defered_write = true,
    .init_len         = sizeof(nrf_ble_escs_adv_interval_t),
    .max_len          = sizeof(nrf_ble_escs_adv_interval_t),
};

static ble_add_char_params_t RADIO_TX_PWR_CHAR_INIT =
{
    .uuid         = BLE_UUID_ESCS_RADIO_TX_PWR_CHAR,
    .read_access  = SEC_OPEN,
    .write_access = SEC_OPEN,
    .char_props   =
    {
       .read  = 1,
       .write = 1,
    },
    .is_defered_read  = true,
    .is_defered_write = true,
    .init_len         = sizeof(nrf_ble_escs_radio_tx_pwr_t),
    .max_len          = sizeof(nrf_ble_escs_radio_tx_pwr_t),
};

static ble_add_char_params_t ADV_TX_PWR_CHAR_INIT =
{
    .uuid         = BLE_UUID_ESCS_ADV_TX_PWR_CHAR,
    .read_access  = SEC_OPEN,
    .write_access = SEC_OPEN,
    .char_props   =
    {
       .read  = 1,
       .write = 1,
    },
    .is_defered_read  = true,
    .is_defered_write = true,
    .init_len         = sizeof(nrf_ble_escs_adv_tx_pwr_t),
    .max_len          = sizeof(nrf_ble_escs_adv_tx_pwr_t),
};

static ble_add_char_params_t LOCK_STATE_CHAR_INIT =
{
    .uuid         = BLE_UUID_ESCS_LOCK_STATE_CHAR,
    .read_access  = SEC_OPEN,
    .write_access = SEC_OPEN,
    .char_props   =
    {
       .read  = 1,
       .write = 1,
    },
    .is_defered_read  = true,
    .is_defered_write = true,
    .is_var_len       = true,
    .is_value_user    = true,
    .init_len         = 1,
    .max_len          = 17,
};

static ble_add_char_params_t UNLOCK_CHAR_INIT =
{
    .uuid         = BLE_UUID_ESCS_UNLOCK_CHAR,
    .read_access  = SEC_OPEN,
    .write_access = SEC_OPEN,
    .char_props   =
    {
       .read  = 1,
       .write = 1,
    },
    .is_defered_read  = true,
    .is_defered_write = true,
    .init_len         = 1,
    .max_len          = ESCS_AES_KEY_SIZE,
};

static ble_add_char_params_t PUBLIC_ECDH_KEY_CHAR_INIT =
{
    .uuid        = BLE_UUID_ESCS_PUBLIC_ECDH_KEY_CHAR,
    .read_access = SEC_OPEN,
    .char_props  =
    {
       .read  = 1,
    },
    .is_defered_read = true,
    .is_var_len      = true,
    .init_len        = 1,
    .max_len         = ESCS_ECDH_KEY_SIZE,
};

static ble_add_char_params_t EID_ID_KEY_CHAR_INIT =
{
    .uuid        = BLE_UUID_ESCS_EID_ID_KEY_CHAR,
    .read_access = SEC_OPEN,
    .char_props  =
    {
       .read  = 1,
    },
    .is_defered_read = true,
    .is_var_len      = true,
    .init_len        = 1,
    .max_len         = ESCS_AES_KEY_SIZE,
};

static ble_add_char_params_t RW_ADV_SLOT_CHAR_INIT =
{
    .uuid         = BLE_UUID_ESCS_RW_ADV_SLOT_CHAR,
    .read_access  = SEC_OPEN,
    .write_access = SEC_OPEN,
    .char_props   =
    {
       .read  = 1,
       .write = 1,
    },
    .is_defered_read  = true,
    .is_defered_write = true,
    .is_var_len       = true,
    .max_len          = ESCS_ADV_SLOT_CHAR_LENGTH_MAX,
};

static ble_add_char_params_t FACTORY_RESET_CHAR_INIT =
{
    .uuid         = BLE_UUID_ESCS_FACTORY_RESET_CHAR,
    .write_access = SEC_OPEN,
    .char_props   =
    {
       .write = 1,
    },
    .is_defered_write = true,
    .init_len         = sizeof(nrf_ble_escs_factory_reset_t),
    .max_len          = sizeof(nrf_ble_escs_factory_reset_t),
};

static ble_add_char_params_t REMAIN_CONNECTABLE_CHAR_INIT =
{
    .uuid         = BLE_UUID_ESCS_REMAIN_CONNECTABLE_CHAR,
    .read_access  = SEC_OPEN,
    .write_access = SEC_OPEN,
    .char_props   =
    {
       .read  = 1,
       .write = 1,
    },
    .is_defered_read  = true,
    .is_defered_write = true,
    .init_len         = 1,
    .max_len          = 1,
};

static val_handle_to_uuid_t m_handle_to_uuid_map[BLE_ESCS_NUMBER_OF_CHARACTERISTICS]; //!< Map from handle to UUID.
static uint8_t              m_handle_to_uuid_map_idx = 0;   //!< Index of map from handle to UUID.
static uint8_t              m_eid_mem[EID_BUFF_SIZE] = {0}; //!< Memory buffer used for EID writes.
static ble_user_mem_block_t m_eid_mem_block =
{
    .p_mem = m_eid_mem,
    .len = EID_BUFF_SIZE
};                                                          //!<  Memory block used for EID writes.



/**@brief Function for adding characteristic to Eddystone service.
 *
 * @param[in] p_escs       Eddystone Configuration Service structure.
 * @param[in] p_escs_init  Information needed to initialize the service.
 * @param[in] p_char_init  Information needed to initialize the characteristic.
 *
 * @return NRF_SUCCESS on success, otherwise an error code.
 */
static uint32_t char_add(ble_add_char_params_t    * p_char_init,
                         nrf_ble_escs_t           * p_escs,
                         void                     * p_value,
                         ble_gatts_char_handles_t * p_handles)
{
    uint32_t err_code;

    VERIFY_PARAM_NOT_NULL(p_char_init);
    VERIFY_PARAM_NOT_NULL(p_escs);
    VERIFY_PARAM_NOT_NULL(p_value);
    VERIFY_PARAM_NOT_NULL(p_handles);

    p_char_init->uuid_type    = p_escs->uuid_type;
    p_char_init->p_init_value = p_value;

    err_code = characteristic_add(p_escs->service_handle,
                                  p_char_init,
                                  p_handles);

    if (err_code == NRF_SUCCESS)
    {
        ASSERT(m_handle_to_uuid_map_idx < BLE_ESCS_NUMBER_OF_CHARACTERISTICS);
        m_handle_to_uuid_map[m_handle_to_uuid_map_idx].val_handle = p_handles->value_handle;
        m_handle_to_uuid_map[m_handle_to_uuid_map_idx].uuid = p_char_init->uuid;
        m_handle_to_uuid_map_idx++;
    }

    return err_code;
}


/**@brief Function for handling the @ref BLE_GAP_EVT_CONNECTED event from the SoftDevice.
 *
 * @param[in] p_escs     Eddystone Configuration Service structure.
 * @param[in] p_ble_evt Pointer to the event received from BLE stack.
 */
static void on_connect(nrf_ble_escs_t * p_escs, ble_evt_t const * p_ble_evt)
{
    VERIFY_PARAM_NOT_NULL_VOID(p_escs);
    p_escs->conn_handle = p_ble_evt->evt.gap_evt.conn_handle;
}


/**@brief Function for handling the @ref BLE_GAP_EVT_DISCONNECTED event from the SoftDevice.
 *
 * @param[in] p_escs    Eddystone Configuration Service structure.
 * @param[in] p_ble_evt Pointer to the event received from BLE stack.
 */
static void on_disconnect(nrf_ble_escs_t * p_escs, ble_evt_t const * p_ble_evt)
{
    VERIFY_PARAM_NOT_NULL_VOID(p_escs);
    UNUSED_PARAMETER(p_ble_evt);
    p_escs->conn_handle = BLE_CONN_HANDLE_INVALID;
}


static uint32_t get_evt_type_for_handle(uint16_t handle, uint16_t * p_uuid)
{
    VERIFY_PARAM_NOT_NULL(p_uuid);

    for (uint8_t i = 0; i < BLE_ESCS_NUMBER_OF_CHARACTERISTICS; ++i)
    {
        if (m_handle_to_uuid_map[i].val_handle == handle)
        {
            *p_uuid = m_handle_to_uuid_map[i].uuid;
            return NRF_SUCCESS;
        }
    }

    return NRF_ERROR_NOT_FOUND;
}

/**@brief Function for handling the @ref BLE_GATTS_EVT_RW_AUTHORIZE_REQUEST: BLE_GATTS_AUTHORIZE_TYPE_WRITE event from the SoftDevice.
 *
 * @param[in] p_escs     Eddystone Configuration Service structure.
 * @param[in] p_ble_evt Pointer to the event received from BLE stack.
 */
static ret_code_t on_write(nrf_ble_escs_t * p_escs, ble_evt_t const * p_ble_evt)
{
    uint32_t err_code;
    uint16_t write_evt_uuid = 0;

    VERIFY_PARAM_NOT_NULL(p_escs);
    VERIFY_PARAM_NOT_NULL(p_ble_evt);

    ble_gatts_evt_write_t const * p_evt_write =
        &p_ble_evt->evt.gatts_evt.params.authorize_request.request.write;

    err_code = get_evt_type_for_handle(p_evt_write->handle, &write_evt_uuid);
    RETURN_IF_ERROR(err_code);

    p_escs->write_evt_handler(p_escs,
                              write_evt_uuid,
                              p_evt_write->handle,
                              p_evt_write->data,
                              p_evt_write->len);

    return NRF_SUCCESS;
}


/**@brief Function for handling the @ref BLE_GATTS_EVT_RW_AUTHORIZE_REQUEST: BLE_GATTS_AUTHORIZE_TYPE_WRITE:  event from the SoftDevice.
 *
 * @param[in] p_escs     Eddystone Configuration Service structure.
 * @param[in] p_ble_evt Pointer to the event received from BLE stack.
 */
static void on_long_write(nrf_ble_escs_t * p_escs, ble_evt_t const * p_ble_evt)
{
    static uint16_t write_evt_uuid;
    static bool write_evt_uuid_set = false;
    uint32_t err_code;

    VERIFY_PARAM_NOT_NULL_VOID(p_escs);
    VERIFY_PARAM_NOT_NULL_VOID(p_ble_evt);

    ble_gatts_evt_write_t const * p_evt_write =
        &p_ble_evt->evt.gatts_evt.params.authorize_request.request.write;

    ble_gatts_rw_authorize_reply_params_t reply = {0};

    if (p_evt_write->op == BLE_GATTS_OP_PREP_WRITE_REQ)
    {
        err_code = get_evt_type_for_handle(p_evt_write->handle, &write_evt_uuid);
        APP_ERROR_CHECK(err_code);

        write_evt_uuid_set = true;

        reply.type                     = BLE_GATTS_AUTHORIZE_TYPE_WRITE;
        reply.params.write.gatt_status = BLE_GATT_STATUS_SUCCESS;
        reply.params.write.update      = 0;
        reply.params.write.offset      = 0;
        reply.params.write.len         = p_evt_write->len;
        reply.params.write.p_data      = NULL;

        err_code = sd_ble_gatts_rw_authorize_reply(p_escs->conn_handle, &reply);
        APP_ERROR_CHECK(err_code);
    }

    else if (p_evt_write->op == BLE_GATTS_OP_EXEC_WRITE_REQ_NOW)
    {
        uint8_t           value_buffer[ESCS_ADV_SLOT_CHAR_LENGTH_MAX] = {0};
        ble_gatts_value_t value =
        {
            .len = sizeof(value_buffer),
            .offset = 0,
            .p_value = &(value_buffer[0])
        };

        ASSERT(write_evt_uuid_set);
        write_evt_uuid_set = false;

        reply.type                     = BLE_GATTS_AUTHORIZE_TYPE_WRITE;
        reply.params.write.gatt_status = BLE_GATT_STATUS_SUCCESS;
        reply.params.write.update      = 0;
        reply.params.write.offset      = 0;
        reply.params.write.len         = p_evt_write->len;
        reply.params.write.p_data      = NULL;

        err_code = sd_ble_gatts_rw_authorize_reply(p_escs->conn_handle, &reply);
        APP_ERROR_CHECK(err_code);

        // Now that the value has been accepted using 'sd_ble_gatts_rw_authorize_reply', it can be found in the database.
        err_code = sd_ble_gatts_value_get(  p_escs->conn_handle,
                                            p_escs->rw_adv_slot_handles.value_handle,
                                            &value);
        APP_ERROR_CHECK(err_code);

        p_escs->write_evt_handler(p_escs,
                                  write_evt_uuid,
                                  p_evt_write->handle,
                                  value.p_value,
                                  value.len);
    }
    else
    {
    }
}


/**@brief Function for handling events from the SoftDevice related to long writes.
 *
 * @param[in] p_escs     Eddystone Configuration Service structure.
 * @param[in] p_ble_evt Pointer to the event received from BLE stack.
 */
static ret_code_t on_read(nrf_ble_escs_t * p_escs, ble_evt_t const * p_ble_evt)
{
    VERIFY_PARAM_NOT_NULL(p_escs);
    VERIFY_PARAM_NOT_NULL(p_ble_evt);
    ret_code_t err_code;
    uint16_t read_evt_uuid = 0;
    uint16_t val_handle = p_ble_evt->evt.gatts_evt.params.authorize_request.request.read.handle;
    err_code = get_evt_type_for_handle(val_handle, &read_evt_uuid);
    RETURN_IF_ERROR(err_code);

    p_escs->read_evt_handler(p_escs, read_evt_uuid, val_handle);

    return NRF_SUCCESS;
}


static ret_code_t on_rw_authorize_req(nrf_ble_escs_t * p_escs, ble_evt_t const * p_ble_evt)
{
    ret_code_t err_code;
    VERIFY_PARAM_NOT_NULL(p_escs);
    VERIFY_PARAM_NOT_NULL(p_ble_evt);

    ble_gatts_evt_rw_authorize_request_t const * ar =
        &p_ble_evt->evt.gatts_evt.params.authorize_request;

    if (ar->type == BLE_GATTS_AUTHORIZE_TYPE_READ)
    {
        err_code = on_read(p_escs, p_ble_evt);
        RETURN_IF_ERROR(err_code);
    }
    else if (ar->type == BLE_GATTS_AUTHORIZE_TYPE_WRITE)
    {
        if (ar->request.write.op == BLE_GATTS_OP_WRITE_REQ
         || ar->request.write.op == BLE_GATTS_OP_WRITE_CMD)
        {
            err_code = on_write(p_escs, p_ble_evt);
            RETURN_IF_ERROR(err_code);
        }

        else if (ar->request.write.op == BLE_GATTS_OP_PREP_WRITE_REQ
             || ar->request.write.op == BLE_GATTS_OP_EXEC_WRITE_REQ_NOW)
        {
            on_long_write(p_escs, p_ble_evt);
        }
        else if (ar->request.write.op == BLE_GATTS_OP_EXEC_WRITE_REQ_CANCEL)
        {
            ble_gatts_rw_authorize_reply_params_t auth_reply;
            memset(&auth_reply, 0, sizeof(auth_reply));

            auth_reply.type                     = BLE_GATTS_AUTHORIZE_TYPE_WRITE;
            auth_reply.params.write.gatt_status = BLE_GATT_STATUS_SUCCESS;

            err_code = sd_ble_gatts_rw_authorize_reply(p_ble_evt->evt.gatts_evt.conn_handle, &auth_reply);
            VERIFY_SUCCESS(err_code);
        }
        else
        {
        }
    }
    else
    {
        return NRF_ERROR_INVALID_STATE;
    }

    return NRF_SUCCESS;
}



ret_code_t nrf_ble_escs_on_ble_evt(nrf_ble_escs_t * p_escs, ble_evt_t const * p_ble_evt)
{
    ret_code_t err_code;

    VERIFY_PARAM_NOT_NULL(p_escs);
    VERIFY_PARAM_NOT_NULL(p_ble_evt);

    switch (p_ble_evt->header.evt_id)
    {
        case BLE_GAP_EVT_CONNECTED:
            on_connect(p_escs, p_ble_evt);

            break;

        case BLE_GAP_EVT_DISCONNECTED:
            on_disconnect(p_escs, p_ble_evt);
            break;

        case BLE_GATTS_EVT_RW_AUTHORIZE_REQUEST:
            err_code = on_rw_authorize_req(p_escs, p_ble_evt);
            VERIFY_SUCCESS(err_code);
            break;

        // BLE_EVT_USER_MEM_REQUEST & BLE_EVT_USER_MEM_RELEASE are for long writes to the RW ADV slot characteristic
        case BLE_EVT_USER_MEM_REQUEST:
            err_code = sd_ble_user_mem_reply(p_escs->conn_handle, &m_eid_mem_block);
            VERIFY_SUCCESS(err_code);
            break;

        case BLE_EVT_USER_MEM_RELEASE:
            break;

        default:
            // No implementation needed.
            break;
    }

    return NRF_SUCCESS;
}


ret_code_t nrf_ble_escs_init(nrf_ble_escs_t * p_escs, const nrf_ble_escs_init_t * p_escs_init)
{
    uint32_t      err_code;
    ble_uuid_t    ble_uuid;
    ble_uuid128_t ecs_base_uuid = ESCS_BASE_UUID;
    uint8_t       zero_val = 0;

    VERIFY_PARAM_NOT_NULL(p_escs);
    VERIFY_PARAM_NOT_NULL(p_escs_init);

    // Initialize the service structure.
    p_escs->conn_handle       = BLE_CONN_HANDLE_INVALID;
    p_escs->write_evt_handler = p_escs_init->write_evt_handler;
    p_escs->read_evt_handler  = p_escs_init->read_evt_handler;

    // Add a custom base UUID.
    err_code = sd_ble_uuid_vs_add(&ecs_base_uuid, &p_escs->uuid_type);
    VERIFY_SUCCESS(err_code);

    ble_uuid.type = p_escs->uuid_type;
    ble_uuid.uuid = BLE_UUID_ESCS_SERVICE;

    // Add the service.
    err_code = sd_ble_gatts_service_add(BLE_GATTS_SRVC_TYPE_PRIMARY,
                                        &ble_uuid,
                                        &p_escs->service_handle);
    VERIFY_SUCCESS(err_code);

    m_handle_to_uuid_map_idx = 0;

    // Set up initial values for characteristics

    // Eddystone spec requires big endian
    nrf_ble_escs_broadcast_cap_t temp = p_escs_init->p_init_vals->broadcast_cap;
    temp.supp_frame_types = BYTES_SWAP_16BIT(temp.supp_frame_types);

    nrf_ble_escs_adv_interval_t temp_interval = p_escs_init->p_init_vals->adv_interval;
    temp_interval = BYTES_SWAP_16BIT(temp_interval);

    // Adding chracteristics

    err_code = char_add(&BROADCAST_CAP_CHAR_INIT, p_escs,
                        &temp, &p_escs->broadcast_cap_handles);
    VERIFY_SUCCESS(err_code);

    err_code = char_add(&ACTIVE_SLOT_CHAR_INIT, p_escs,
                        p_escs->p_active_slot, &p_escs->active_slot_handles);
    VERIFY_SUCCESS(err_code);

    err_code = char_add(&ADV_INTERVAL_CHAR_INIT, p_escs,
                        &temp_interval, &p_escs->adv_interval_handles);
    VERIFY_SUCCESS(err_code);

    err_code = char_add(&RADIO_TX_PWR_CHAR_INIT, p_escs,
                        &(p_escs_init->p_init_vals->radio_tx_pwr), &p_escs->radio_tx_pwr_handles);
    VERIFY_SUCCESS(err_code);

    err_code = char_add(&ADV_TX_PWR_CHAR_INIT, p_escs,
                        &(p_escs_init->p_init_vals->adv_tx_pwr), &p_escs->adv_tx_pwr_handles);
    VERIFY_SUCCESS(err_code);

    err_code = char_add(&LOCK_STATE_CHAR_INIT, p_escs,
                        &p_escs->lock_state, &p_escs->lock_state_handles);
    VERIFY_SUCCESS(err_code);

    err_code = char_add(&UNLOCK_CHAR_INIT, p_escs,
                        &zero_val, &p_escs->unlock_handles);
    VERIFY_SUCCESS(err_code);

    err_code = char_add(&PUBLIC_ECDH_KEY_CHAR_INIT,  p_escs,
                        &zero_val, &p_escs->pub_ecdh_key_handles);
    VERIFY_SUCCESS(err_code);

    err_code = char_add(&EID_ID_KEY_CHAR_INIT, p_escs,
                        &zero_val, &p_escs->eid_id_key_handles);
    VERIFY_SUCCESS(err_code);

    err_code = char_add(&RW_ADV_SLOT_CHAR_INIT, p_escs,
                        &zero_val, &p_escs->rw_adv_slot_handles);
    VERIFY_SUCCESS(err_code);

    err_code = char_add(&FACTORY_RESET_CHAR_INIT, p_escs,
                        &(p_escs_init->p_init_vals->factory_reset), &p_escs->factory_reset_handles);
    VERIFY_SUCCESS(err_code);

    err_code = char_add(&REMAIN_CONNECTABLE_CHAR_INIT, p_escs,
                        &(p_escs_init->p_init_vals->remain_connectable.r_is_non_connectable_supported),
                        &p_escs->remain_connectable_handles);
    VERIFY_SUCCESS(err_code);

    return NRF_SUCCESS;
}