
#include <ble.h>
#include "ble_needlite.h"
#include <string.h>
#include "nordic_common.h"
#include "ble_srv_common.h"
#include "pstorage.h"
#include "app_util.h"
#include "simple_uart.h"
#include "bluetooth_control.h"
#include "debug_util.h"
#include "event_log.h"

/**
 * Characteristic user descriptions.
 * Makes the characteristics show up with meaningfull names instead
 * of just their UUID.
 */
/*
static char on_off_user_desc[7] = "On/Off";
static char brightness_user_desc[11] = "Brightness";
static char displayname_user_desc[12] = "Displayname";
*/

//static char display_name[256] = {0};

/**@brief Function for handling the Connect event.
 *
 * @param[in]   p_needlite       Battery Service structure.
 * @param[in]   p_ble_evt   Event received from the BLE stack.
 */
static void on_connect(ble_needlite_t * p_needlite, ble_evt_t * p_ble_evt)
{
    p_needlite->conn_handle = p_ble_evt->evt.gap_evt.conn_handle;
}


/**@brief Function for handling the Disconnect event.
 *
 * @param[in]   p_needlite       Battery Service structure.
 * @param[in]   p_ble_evt   Event received from the BLE stack.
 */
static void on_disconnect(ble_needlite_t * p_needlite, ble_evt_t * p_ble_evt)
{
    UNUSED_PARAMETER(p_ble_evt);
    p_needlite->conn_handle = BLE_CONN_HANDLE_INVALID;
}


/**@brief Function for handling the Write event.
 *
 * @param[in]   p_needlite       Battery Service structure.
 * @param[in]   p_ble_evt   Event received from the BLE stack.
 */
static void on_write(ble_needlite_t * p_needlite, ble_evt_t * p_ble_evt)
{
    ble_gatts_evt_write_t * p_evt_write = &p_ble_evt->evt.gatts_evt.params.write;

    if (
        (p_evt_write->handle == p_needlite->led_brightness_handles.value_handle)
        &&
        (p_evt_write->len == 1)
    ) {
        //p_needlite->led_write_handler(p_needlite, p_evt_write->data[0]);
        led_brightness(p_needlite, p_evt_write->data[0]);
    }

    else if (
        (p_evt_write->handle == p_needlite->led_state_handles.value_handle)
        &&
        (p_evt_write->len == 1)
    ) {
        //  p_needlite->led_state_handler(p_needlite, p_evt_write->data[0]);
        led_state(p_needlite, p_evt_write->data[0]);
    }

    else if (p_evt_write->handle == p_needlite->displayname_handles.value_handle) {
        set_displayname(p_needlite, p_evt_write->data, p_evt_write->len);
    }
    else if (p_evt_write->handle == p_needlite->event_cmd_handles.value_handle) {
        event_log_request_events_since( *((uint16_t *) p_evt_write->data) );
    }
}

void ble_needlite_on_ble_evt(ble_needlite_t * p_needlite, ble_evt_t * p_ble_evt)
{
    switch (p_ble_evt->header.evt_id) {
    case BLE_GAP_EVT_CONNECTED:
        on_connect(p_needlite, p_ble_evt);
        break;

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

    case BLE_GATTS_EVT_WRITE:
        on_write(p_needlite, p_ble_evt);
        break;
    
    default:
        // No implementation needed.
        break;
    }
}


/**@brief Function for adding the Brightness characteristic.
 *
 * @param[in]   p_needlite        Battery Service structure.
 * @param[in]   p_needlite_init   Information needed to initialize the service.
 *
 * @return      NRF_SUCCESS on success, otherwise an error code.
 */
static uint32_t brightness_char_add(ble_needlite_t * p_needlite, const ble_needlite_init_t * p_needlite_init)
{
//    uint32_t            err_code;
    ble_gatts_char_md_t char_md;
    ble_gatts_attr_md_t cccd_md;
    ble_gatts_attr_t    attr_char_value;
    ble_uuid_t          ble_uuid;
    ble_gatts_attr_md_t attr_md;

    uint8_t             initial_led_level;
//    uint8_t             init_len;

    memset(&cccd_md, 0, sizeof(cccd_md));

    BLE_GAP_CONN_SEC_MODE_SET_OPEN(&cccd_md.read_perm);
    BLE_GAP_CONN_SEC_MODE_SET_OPEN(&cccd_md.write_perm);
    cccd_md.vloc = BLE_GATTS_VLOC_STACK;

    memset(&char_md, 0, sizeof(char_md));

    char_md.char_props.read   = 1;
    char_md.char_props.write   = 1;
    char_md.char_props.notify = 1;

    char_md.p_char_pf         = NULL;
    char_md.p_user_desc_md    = NULL;
    char_md.p_cccd_md         = &cccd_md;
    char_md.p_sccd_md         = NULL;
    char_md.char_ext_props.reliable_wr = true;
    char_md.char_props.write_wo_resp = true;

    /*
    char_md.p_char_user_desc  = (uint8_t *) brightness_user_desc;
    char_md.char_user_desc_size = strlen(brightness_user_desc);
    char_md.char_user_desc_max_size = strlen(brightness_user_desc);
    */

    ble_uuid.type = p_needlite->sv_uuid_type;
    ble_uuid.uuid = NEEDLITE_UUID_BUTTON_CHAR;

    memset(&attr_md, 0, sizeof(attr_md));

    BLE_GAP_CONN_SEC_MODE_SET_OPEN(&attr_md.read_perm);
    BLE_GAP_CONN_SEC_MODE_SET_OPEN(&attr_md.write_perm);
    attr_md.vloc       = BLE_GATTS_VLOC_STACK;
    attr_md.rd_auth    = 0;
    attr_md.wr_auth    = 0;
    attr_md.vlen       = 0;

    initial_led_level = p_needlite_init->initial_led_level;

    memset(&attr_char_value, 0, sizeof(attr_char_value));

    attr_char_value.p_uuid       = &ble_uuid;
    attr_char_value.p_attr_md    = &attr_md;
    attr_char_value.init_len     = sizeof(uint8_t);
    attr_char_value.init_offs    = 0;
    attr_char_value.max_len      = sizeof(uint8_t);
    attr_char_value.p_value      = &initial_led_level;

    sd_ble_gatts_characteristic_add(p_needlite->service_handle, &char_md,
                                    &attr_char_value,
                                    &p_needlite->led_brightness_handles);

    return NRF_SUCCESS;
}

static uint32_t proximity_char_add(ble_needlite_t * p_needlite, const ble_needlite_init_t * p_needlite_init)
{
//    uint32_t            err_code;
    ble_gatts_char_md_t char_md;
    ble_gatts_attr_md_t cccd_md;
    ble_gatts_attr_t    attr_char_value;
    ble_uuid_t          ble_uuid;
    ble_gatts_attr_md_t attr_md;
    uint8_t             initial_led_level;
//    uint8_t             init_len;



    memset(&cccd_md, 0, sizeof(cccd_md));

    BLE_GAP_CONN_SEC_MODE_SET_OPEN(&cccd_md.read_perm);
    BLE_GAP_CONN_SEC_MODE_SET_OPEN(&cccd_md.write_perm);
    cccd_md.vloc = BLE_GATTS_VLOC_STACK;

    memset(&char_md, 0, sizeof(char_md));

    char_md.char_props.read   = 1;
    char_md.char_props.write   = 1;
    char_md.char_props.notify = 1;
    char_md.p_char_user_desc  = NULL;

    char_md.p_char_pf         = NULL;
    char_md.p_user_desc_md    = NULL;
    char_md.p_cccd_md         = &cccd_md;
    char_md.p_sccd_md         = NULL;
    char_md.char_ext_props.reliable_wr = true;
    char_md.char_props.write_wo_resp = true;

    /*
    char_md.p_char_user_desc  = (uint8_t *) on_off_user_desc;
    char_md.char_user_desc_size = strlen(on_off_user_desc);
    char_md.char_user_desc_max_size = strlen(on_off_user_desc);
    */

    ble_uuid.type = p_needlite->sv_uuid_type;
    ble_uuid.uuid = NEEDLITE_UUID_PROXIMITY_CHAR;

    memset(&attr_md, 0, sizeof(attr_md));

    BLE_GAP_CONN_SEC_MODE_SET_OPEN(&attr_md.read_perm);
    BLE_GAP_CONN_SEC_MODE_SET_OPEN(&attr_md.write_perm);
    attr_md.vloc       = BLE_GATTS_VLOC_STACK;
    attr_md.rd_auth    = 0;
    attr_md.wr_auth    = 0;
    attr_md.vlen       = 0;

    initial_led_level = 0;//p_needlite_init->initial_led_level;

    memset(&attr_char_value, 0, sizeof(attr_char_value));

    attr_char_value.p_uuid       = &ble_uuid;
    attr_char_value.p_attr_md    = &attr_md;
    attr_char_value.init_len     = sizeof(uint8_t);
    attr_char_value.init_offs    = 0;
    attr_char_value.max_len      = sizeof(uint8_t);
    attr_char_value.p_value      = &initial_led_level;

    sd_ble_gatts_characteristic_add(p_needlite->service_handle, &char_md,
                                    &attr_char_value,
                                    &p_needlite->proximity_handles);

    return NRF_SUCCESS;
}


static uint32_t onoff_char_add(ble_needlite_t * p_needlite, const ble_needlite_init_t * p_needlite_init)
{
    ble_gatts_char_md_t char_md;
    ble_gatts_attr_md_t cccd_md;
    ble_gatts_attr_t    attr_char_value;
    ble_uuid_t          ble_uuid;
    ble_gatts_attr_md_t attr_md;
	uint8_t initial_state = p_needlite_init->initial_on_off_state;
	
    memset(&cccd_md, 0, sizeof(cccd_md));

    BLE_GAP_CONN_SEC_MODE_SET_OPEN(&cccd_md.read_perm);
    BLE_GAP_CONN_SEC_MODE_SET_OPEN(&cccd_md.write_perm);
    cccd_md.vloc = BLE_GATTS_VLOC_STACK;

    memset(&char_md, 0, sizeof(char_md));

    char_md.char_props.read   = 1;
    char_md.char_props.write   = 1;
    char_md.char_props.notify = 1;
    char_md.p_char_user_desc  = NULL;

    char_md.p_char_pf         = NULL;
    char_md.p_user_desc_md    = NULL;
    char_md.p_cccd_md         = &cccd_md;
    char_md.p_sccd_md         = NULL;
    char_md.char_ext_props.reliable_wr = true;
    char_md.char_props.write_wo_resp = true;

    ble_uuid.type = p_needlite->sv_uuid_type;
    ble_uuid.uuid = NEEDLITE_UUID_LED_CHAR;

    memset(&attr_md, 0, sizeof(attr_md));

    BLE_GAP_CONN_SEC_MODE_SET_OPEN(&attr_md.read_perm);
    BLE_GAP_CONN_SEC_MODE_SET_OPEN(&attr_md.write_perm);
    attr_md.vloc       = BLE_GATTS_VLOC_STACK;
    attr_md.rd_auth    = 0;
    attr_md.wr_auth    = 0;
    attr_md.vlen       = 0;

    memset(&attr_char_value, 0, sizeof(attr_char_value));

    attr_char_value.p_uuid       = &ble_uuid;
    attr_char_value.p_attr_md    = &attr_md;
    attr_char_value.init_len     = sizeof(uint8_t);
    attr_char_value.init_offs    = 0;
    attr_char_value.max_len      = sizeof(uint8_t);
    attr_char_value.p_value      = &initial_state;

    sd_ble_gatts_characteristic_add(p_needlite->service_handle, &char_md,
                                    &attr_char_value,
                                    &p_needlite->led_state_handles);

    return NRF_SUCCESS;
}

static uint32_t displayname_char_add(ble_needlite_t * p_needlite,
                                     const ble_needlite_init_t * p_needlite_init)
{
    ble_gatts_char_md_t char_md;
    ble_gatts_attr_md_t cccd_md;
    ble_gatts_attr_t    attr_char_value;
    ble_uuid_t          ble_uuid;
    ble_gatts_attr_md_t attr_md;

    memset(&cccd_md, 0, sizeof(cccd_md));

    BLE_GAP_CONN_SEC_MODE_SET_OPEN(&cccd_md.read_perm);
    BLE_GAP_CONN_SEC_MODE_SET_OPEN(&cccd_md.write_perm);
    cccd_md.vloc = BLE_GATTS_VLOC_STACK;

    memset(&char_md, 0, sizeof(char_md));

    char_md.char_props.read   = 1;
    char_md.char_props.write  = 1;
    char_md.p_char_user_desc  = NULL;
    char_md.p_char_pf         = NULL;
    char_md.p_user_desc_md    = NULL;
    char_md.p_cccd_md         = &cccd_md;
    char_md.p_sccd_md         = NULL;

    ble_uuid.type = p_needlite->sv_uuid_type;
    ble_uuid.uuid = NEEDLITE_UUID_DISPLAYNAME_CHAR;

    memset(&attr_md, 0, sizeof(attr_md));

    BLE_GAP_CONN_SEC_MODE_SET_OPEN(&attr_md.read_perm);
    BLE_GAP_CONN_SEC_MODE_SET_OPEN(&attr_md.write_perm);
    attr_md.vloc       = BLE_GATTS_VLOC_STACK;
    attr_md.rd_auth    = 0;
    attr_md.wr_auth    = 0;
    attr_md.vlen       = 0;

    memset(&attr_char_value, 0, sizeof(attr_char_value));

    attr_char_value.p_uuid       = &ble_uuid;
    attr_char_value.p_attr_md    = &attr_md;
    attr_char_value.init_len     = 0;
    attr_char_value.init_offs    = 0;
    attr_char_value.max_len      = 255;
    attr_char_value.p_value      = NULL;

    sd_ble_gatts_characteristic_add(p_needlite->service_handle, &char_md,
                                    &attr_char_value,
                                    &p_needlite->displayname_handles);
    return NRF_SUCCESS;
}


static uint32_t event_cmd_char_add(ble_needlite_t * p_needlite,
    const ble_needlite_init_t * p_needlite_init)
{
     ble_gatts_char_md_t char_md;
    ble_gatts_attr_md_t cccd_md;
    ble_gatts_attr_t    attr_char_value;
    ble_uuid_t          ble_uuid;
    ble_gatts_attr_md_t attr_md;

    memset(&cccd_md, 0, sizeof(cccd_md));

    BLE_GAP_CONN_SEC_MODE_SET_OPEN(&cccd_md.read_perm);
    BLE_GAP_CONN_SEC_MODE_SET_OPEN(&cccd_md.write_perm);
    cccd_md.vloc = BLE_GATTS_VLOC_STACK;

    memset(&char_md, 0, sizeof(char_md));

    char_md.char_props.read   = 0;
    char_md.char_props.write   = 1;
    char_md.char_props.notify = 0;
    char_md.p_char_user_desc  = NULL;

    char_md.p_char_pf         = NULL;
    char_md.p_user_desc_md    = NULL;
    char_md.p_cccd_md         = &cccd_md;
    char_md.p_sccd_md         = NULL;

    ble_uuid.type = p_needlite->sv_uuid_type;
    ble_uuid.uuid = NEEDLITE_UUID_EVENT_CMD_CHAR;

    memset(&attr_md, 0, sizeof(attr_md));

    BLE_GAP_CONN_SEC_MODE_SET_OPEN(&attr_md.read_perm);
    BLE_GAP_CONN_SEC_MODE_SET_OPEN(&attr_md.write_perm);
    attr_md.vloc       = BLE_GATTS_VLOC_STACK;
    attr_md.rd_auth    = 0;
    attr_md.wr_auth    = 0;
    attr_md.vlen       = 0;
    
    memset(&attr_char_value, 0, sizeof(attr_char_value));

    attr_char_value.p_uuid       = &ble_uuid;
    attr_char_value.p_attr_md    = &attr_md;
    attr_char_value.init_len     = 0;
    attr_char_value.init_offs    = 0;
    attr_char_value.max_len      = sizeof(uint16_t);
    attr_char_value.p_value      = 0;

    sd_ble_gatts_characteristic_add(p_needlite->service_handle, &char_md,
                                    &attr_char_value,
                                    &p_needlite->event_cmd_handles);
    return NRF_SUCCESS;
}

static uint32_t event_data_char_add(ble_needlite_t * p_needlite,
    const ble_needlite_init_t * p_needlite_init)
{
     ble_gatts_char_md_t char_md;
    ble_gatts_attr_md_t cccd_md;
    ble_gatts_attr_t    attr_char_value;
    ble_uuid_t          ble_uuid;
    ble_gatts_attr_md_t attr_md;

    memset(&cccd_md, 0, sizeof(cccd_md));

    BLE_GAP_CONN_SEC_MODE_SET_OPEN(&cccd_md.read_perm);
    BLE_GAP_CONN_SEC_MODE_SET_OPEN(&cccd_md.write_perm);
    cccd_md.vloc = BLE_GATTS_VLOC_STACK;

    memset(&char_md, 0, sizeof(char_md));

    char_md.char_props.read   = 1;
    char_md.char_props.write   = 0;
    char_md.char_props.notify = 1;
    char_md.p_char_user_desc  = NULL;

    char_md.p_char_pf         = NULL;
    char_md.p_user_desc_md    = NULL;
    char_md.p_cccd_md         = &cccd_md;
    char_md.p_sccd_md         = NULL;

    ble_uuid.type = p_needlite->sv_uuid_type;
    ble_uuid.uuid = NEEDLITE_UUID_EVENT_DATA_CHAR;

    memset(&attr_md, 0, sizeof(attr_md));

    BLE_GAP_CONN_SEC_MODE_SET_OPEN(&attr_md.read_perm);
    BLE_GAP_CONN_SEC_MODE_SET_OPEN(&attr_md.write_perm);
    attr_md.vloc       = BLE_GATTS_VLOC_STACK;
    attr_md.rd_auth    = 0;
    attr_md.wr_auth    = 0;
    attr_md.vlen       = 0;
    
    memset(&attr_char_value, 0, sizeof(attr_char_value));

    attr_char_value.p_uuid       = &ble_uuid;
    attr_char_value.p_attr_md    = &attr_md;
    attr_char_value.init_len     = 0;
    attr_char_value.init_offs    = 0;
    attr_char_value.max_len      = sizeof(event_log_packet_t);
    attr_char_value.p_value      = 0;

    sd_ble_gatts_characteristic_add(p_needlite->service_handle, &char_md,
                                    &attr_char_value,
                                    &p_needlite->event_data_handles);
    return NRF_SUCCESS;
}

uint32_t ble_needlite_init(ble_needlite_t * p_needlite, const ble_needlite_init_t * p_needlite_init)
{
    uint32_t   err_code;
    ble_uuid_t ble_uuid;

    // Initialize service structure
    p_needlite->led_write_handler         = p_needlite_init->led_write_handler;
    p_needlite->conn_handle               = BLE_CONN_HANDLE_INVALID;
    p_needlite->is_notification_supported = p_needlite_init->support_notification;

    // Add base UUID to service
    ble_uuid128_t sv_base_uuid = NEEDLITE_UUID_BASE;
    err_code = sd_ble_uuid_vs_add(&sv_base_uuid, &p_needlite->sv_uuid_type);
    if (err_code != NRF_SUCCESS) {
        return err_code;
    }

    ble_uuid.type = p_needlite->sv_uuid_type;
    ble_uuid.uuid = NEEDLITE_UUID_SERVICE;

    err_code = sd_ble_gatts_service_add(BLE_GATTS_SRVC_TYPE_PRIMARY, &ble_uuid, &p_needlite->service_handle);
    if (err_code != NRF_SUCCESS) {
        return err_code;
    }

    // Add characteristics
    err_code = brightness_char_add(p_needlite, p_needlite_init);
    err_code = onoff_char_add(p_needlite, p_needlite_init);
    err_code = displayname_char_add(p_needlite, p_needlite_init);
    err_code = proximity_char_add(p_needlite, p_needlite_init);
    err_code = event_cmd_char_add(p_needlite, p_needlite_init);
    err_code = event_data_char_add(p_needlite, p_needlite_init);
    
    return err_code;
}

uint32_t ble_lbs_brightness_change(ble_needlite_t * p_lbs, uint8_t led_level)
{
    uint32_t err_code = NRF_SUCCESS;
    uint16_t len = sizeof(led_level);

    // Update database
    err_code = sd_ble_gatts_value_set(p_lbs->led_brightness_handles.value_handle,
                                      0, &len, &led_level);

    // Send value if connected
    if (p_lbs->conn_handle != BLE_CONN_HANDLE_INVALID) {
        ble_gatts_hvx_params_t params;

        memset(&params, 0, sizeof(params));
        params.type = BLE_GATT_HVX_NOTIFICATION;
        params.handle = p_lbs->led_brightness_handles.value_handle;
        params.p_data = &led_level;
        params.p_len = &len;

        err_code = sd_ble_gatts_hvx(p_lbs->conn_handle, &params);
    }
    return err_code;
}

uint32_t ble_lbs_onoff_change(ble_needlite_t * p_lbs, uint8_t led_state)
{
    uint32_t err_code = NRF_SUCCESS;
    uint16_t len = sizeof(led_state);

    // Update database
    err_code = sd_ble_gatts_value_set(p_lbs->led_state_handles.value_handle,
                                      0, &len, &led_state);

    // Send value if connected
    if (p_lbs->conn_handle != BLE_CONN_HANDLE_INVALID) {
        ble_gatts_hvx_params_t params;

        memset(&params, 0, sizeof(params));
        params.type = BLE_GATT_HVX_NOTIFICATION;
        params.handle = p_lbs->led_state_handles.value_handle;
        params.p_data = &led_state;
        params.p_len = &len;

        err_code = sd_ble_gatts_hvx(p_lbs->conn_handle, &params);
    }
    return err_code;
}

uint32_t ble_lbs_proximity_change(ble_needlite_t * p_lbs, uint8_t proximity)
{
    uint16_t len = sizeof(proximity);

    // Send value if connected
    if (p_lbs->conn_handle != BLE_CONN_HANDLE_INVALID) {
        ble_gatts_hvx_params_t params;

        memset(&params, 0, sizeof(params));
        params.type = BLE_GATT_HVX_NOTIFICATION;
        params.handle = p_lbs->proximity_handles.value_handle;
        params.p_data = &proximity;
        params.p_len = &len;

        return sd_ble_gatts_hvx(p_lbs->conn_handle, &params);
    }
    return NRF_SUCCESS;
}

uint32_t ble_lbs_event_data_send(ble_needlite_t * p_lbs, event_log_packet_t * packet)
{
    uint16_t len = sizeof(*packet);
    
    // Send value if connected
    if (p_lbs->conn_handle != BLE_CONN_HANDLE_INVALID) {
        ble_gatts_hvx_params_t params;

        memset(&params, 0, sizeof(params));
        params.type = BLE_GATT_HVX_NOTIFICATION;
        params.handle = p_lbs->event_data_handles.value_handle;
        params.p_data = (uint8_t *) packet;
        params.p_len = &len;
        
        return sd_ble_gatts_hvx(p_lbs->conn_handle, &params);
    }
    return NRF_SUCCESS;
}

