#include "sdk_common.h"
#if NRF_MODULE_ENABLED(BLE_NUS)
#include "ble.h"
#include "TestService.h"
#include "ble_srv_common.h"

#define NRF_LOG_MODULE_NAME swx_test
#define NRF_LOG_LEVEL       0
//#endif // BLE_NUS_CONFIG_LOG_ENABLED
#include "nrf_log.h"
NRF_LOG_MODULE_REGISTER();


#define BLE_UUID_TEST_NAME_CHARACTERISTIC             0x0002                     

#define TEST_BASE_UUID                  {{0x4A, 0x9E, 0x05, 0x4F, 0x86, 0x98, 0xA4, 0xA5, 0xA1, 0x4C, 0x04, 0x66, 0x00, 0x00, 0x73, 0xB0}} /**< Used vendor specific UUID B073xxxx-6604-4CA1-A5A4-98864F059E4A. */

ble_add_char_user_desc_t nameDescriptor;

/**@brief Function for handling the @ref BLE_GAP_EVT_CONNECTED event from the SoftDevice.
 *
 * @param[in] p_test     Test Service structure.
 * @param[in] p_ble_evt Pointer to the event received from BLE stack.
 */
static void on_connect(ble_test_t * p_test, ble_evt_t const * p_ble_evt)
{
    ret_code_t                 err_code;
    ble_test_evt_t              evt;
    ble_gatts_value_t          gatts_val;
    uint8_t                    cccd_value[2];
    ble_test_client_context_t * p_client = NULL;

    err_code = blcm_link_ctx_get(p_test->p_link_ctx_storage,
                                 p_ble_evt->evt.gap_evt.conn_handle,
                                 (void *) &p_client);
    if (err_code != NRF_SUCCESS)
    {
        NRF_LOG_ERROR("Link context for 0x%02X connection handle could not be fetched.",
                      p_ble_evt->evt.gap_evt.conn_handle);
    }

    /* Check the hosts CCCD value to inform of readiness to send data using the RX characteristic */
    memset(&gatts_val, 0, sizeof(ble_gatts_value_t));
    gatts_val.p_value = cccd_value;
    gatts_val.len     = sizeof(cccd_value);
    gatts_val.offset  = 0;

    err_code = sd_ble_gatts_value_get(p_ble_evt->evt.gap_evt.conn_handle,
                                      p_test->name_handles.cccd_handle,
                                      &gatts_val);

    if ((err_code == NRF_SUCCESS)     &&
        (p_test->data_handler != NULL) &&
        ble_srv_is_notification_enabled(gatts_val.p_value))
    {
        if (p_client != NULL)
        {
            p_client->is_notification_enabled = true;
        }

        memset(&evt, 0, sizeof(ble_test_evt_t));
        evt.type        = BLE_TEST_EVT_COMM_STARTED;
        evt.p_testService       = p_test;
        evt.conn_handle = p_ble_evt->evt.gap_evt.conn_handle;
        evt.p_link_ctx  = p_client;

        p_test->data_handler(&evt);
    }
}

/**@brief Function for handling the @ref BLE_GATTS_EVT_WRITE event from the SoftDevice.
 *
 * @param[in] p_test     Nordic UART Service structure.
 * @param[in] p_ble_evt Pointer to the event received from BLE stack.
 */
static void on_write(ble_test_t * p_test, ble_evt_t const * p_ble_evt)
{
    ret_code_t                    err_code;
    ble_test_evt_t                 evt;
    ble_test_client_context_t    * p_client;
    ble_gatts_evt_write_t const * p_evt_write = &p_ble_evt->evt.gatts_evt.params.write;

    err_code = blcm_link_ctx_get(p_test->p_link_ctx_storage,
                                 p_ble_evt->evt.gatts_evt.conn_handle,
                                 (void *) &p_client);
    if (err_code != NRF_SUCCESS)
    {
        NRF_LOG_ERROR("Link context for 0x%02X connection handle could not be fetched.",
                      p_ble_evt->evt.gatts_evt.conn_handle);
    }

    memset(&evt, 0, sizeof(ble_test_evt_t));
    evt.p_testService       = p_test;
    evt.conn_handle = p_ble_evt->evt.gatts_evt.conn_handle;
    evt.p_link_ctx  = p_client;

    if ((p_evt_write->len > 0) && (p_test->data_handler != NULL))
    {
        evt.p_data = p_evt_write->data;
        evt.length = p_evt_write->len;
        
        switch (p_evt_write->uuid.uuid)
        {
            case BLE_UUID_TEST_NAME_CHARACTERISTIC:
                evt.type = BLE_TEST_EVT_RX_NAME;
                break;

            default:
                return;

        }
        p_test->data_handler(&evt);													// Do Nothing. This event is not relevant for this service.
    }
}

/**@brief Function for handling the @ref BLE_GATTS_EVT_HVN_TX_COMPLETE event from the SoftDevice.
 *
 * @param[in] p_test     Test Service structure.
 * @param[in] p_ble_evt Pointer to the event received from BLE stack.
 */
static void on_hvx_tx_complete(ble_test_t * p_test, ble_evt_t const * p_ble_evt)
{
    ret_code_t                 err_code;
    ble_test_evt_t              evt;
    ble_test_client_context_t * p_client;

    err_code = blcm_link_ctx_get(p_test->p_link_ctx_storage,
                                 p_ble_evt->evt.gatts_evt.conn_handle,
                                 (void *) &p_client);
    if (err_code != NRF_SUCCESS)
    {
        NRF_LOG_ERROR("Link context for 0x%02X connection handle could not be fetched.",
                      p_ble_evt->evt.gatts_evt.conn_handle);
        return;
    }

    if ((p_client->is_notification_enabled) && (p_test->data_handler != NULL))
    {
        memset(&evt, 0, sizeof(ble_test_evt_t));
        evt.p_testService       = p_test;
        evt.conn_handle = p_ble_evt->evt.gatts_evt.conn_handle;
        evt.p_link_ctx  = p_client;
        evt.type        = BLE_TEST_EVT_RX_NAME;

        p_test->data_handler(&evt);
    }
}

void ble_test_on_ble_evt(ble_evt_t const * p_ble_evt, void * p_context)
{
    if ((p_context == NULL) || (p_ble_evt == NULL))
    {
        return;
    }

    ble_test_t * p_test = (ble_test_t *)p_context;

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

        case BLE_GATTS_EVT_WRITE:
            on_write(p_test, p_ble_evt);
            break;

        case BLE_GATTS_EVT_HVN_TX_COMPLETE:
            on_hvx_tx_complete(p_test, p_ble_evt);
            break;

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

uint32_t ble_test_init(ble_test_t * p_test, ble_test_init_t const * p_test_init)
{
    ret_code_t            err_code;
    ble_uuid_t            ble_uuid;
    ble_uuid128_t         test_base_uuid = TEST_BASE_UUID;
    ble_add_char_params_t add_char_params;

    VERIFY_PARAM_NOT_NULL(p_test);
    VERIFY_PARAM_NOT_NULL(p_test_init);

    // Initialize the service structure.
    p_test->data_handler = p_test_init->data_handler;

    /**@snippet [Adding proprietary Service to the SoftDevice] */
    // Add a custom base UUID.
    err_code = sd_ble_uuid_vs_add(&test_base_uuid, &p_test->uuid_type);
    VERIFY_SUCCESS(err_code);

    ble_uuid.type = p_test->uuid_type;
    ble_uuid.uuid = BLE_UUID_TEST_SERVICE;

    // Add the service.
    err_code = sd_ble_gatts_service_add(BLE_GATTS_SRVC_TYPE_PRIMARY,
                                        &ble_uuid,
                                        &p_test->service_handle);
    /**@snippet [Adding proprietary Service to the SoftDevice] */
    VERIFY_SUCCESS(err_code);

    // Add the Name Characteristic.
    memset(&add_char_params, 0, sizeof(add_char_params));
	memset(&nameDescriptor, 0, sizeof(nameDescriptor));
	nameDescriptor.is_var_len = false;
	nameDescriptor.p_char_user_desc = "Test Name";
	nameDescriptor.max_size = 9;
	nameDescriptor.size = 9;
	nameDescriptor.read_access = SEC_OPEN;
	nameDescriptor.char_props.read			 = true;
    nameDescriptor.is_value_user     = true;

    add_char_params.uuid                     = BLE_UUID_TEST_NAME_CHARACTERISTIC;
    add_char_params.uuid_type                = p_test->uuid_type;
    add_char_params.max_len                  = 20;
    add_char_params.init_len                 = 0;
    add_char_params.is_var_len               = true;
    add_char_params.char_props.write         = true;
	add_char_params.char_props.read			 = true;
	add_char_params.p_user_descr			 = &nameDescriptor;
    add_char_params.is_value_user            = true;//false;//

    add_char_params.read_access  = SEC_OPEN;
    add_char_params.write_access = SEC_OPEN;

    err_code = characteristic_add(p_test->service_handle, &add_char_params, &p_test->name_handles);
    if (err_code != NRF_SUCCESS)
    {
        return err_code;
    }
}

uint32_t UpdateTestName(ble_test_t * p_test, uint16_t conn_handle, uint8_t *pData, uint16_t length)
{
	ble_gatts_hvx_params_t     hvx_params;
	ble_gatts_value_t  gatts_value;

	// Initialize value struct.
	memset(&gatts_value, 0, sizeof(gatts_value));

	gatts_value.len     = length;
	gatts_value.offset  = 0;
	gatts_value.p_value = pData;

	// Update database.
	sd_ble_gatts_value_set(BLE_CONN_HANDLE_INVALID,
									  p_test->name_handles.value_handle,
									  &gatts_value);

    hvx_params.handle = p_test->name_handles.value_handle;
	hvx_params.type   = BLE_GATT_HVX_NOTIFICATION;
	hvx_params.offset = gatts_value.offset;
    hvx_params.p_len  = &gatts_value.len;
    hvx_params.p_data = gatts_value.p_value;


    return sd_ble_gatts_hvx(conn_handle, &hvx_params);
}

#endif // NRF_MODULE_ENABLED(BLE_NUS)
