/**
 * @file    ble.c
 * @brief   BLE module
 * @author  Kenny Tran
 * @date    Jul 06 2023
 * @version 0.0.1
 *
 * @section LICENSES
 * Copyright (C) Morgan Advanced Programmable Systems, Inc.
 */


/*--------------------------------------------------------------------------------------------------
 * INCLUDE FILES
 *------------------------------------------------------------------------------------------------*/
#include <stdint.h>
#include <stdbool.h>
#include <stdlib.h>
#include <string.h>

#include <zephyr/bluetooth/bluetooth.h>
#include <zephyr/bluetooth/hci.h>
#include <zephyr/bluetooth/conn.h>
#include <zephyr/bluetooth/uuid.h>
#include <zephyr/bluetooth/gatt.h>
#include <zephyr/settings/settings.h>
#include <zephyr/kernel.h>
#include <zephyr/random/random.h>

#include "../lib/errno.h"
#include "../lib/fs.h"
#include "../lib/print.h"
#include "../lib/rgb.h"

#include "../bsp/als.h"
#include "../bsp/rtc.h"
#include "../bsp/wdt.h"

#include "app.h"
#include "bubble.h"
#include "fiber.h"
#include "fsm.h"
#include "motion.h"
#include "pin.h"
#include "scope_ring.h"
#include "shot_capture.h"
#include "shot_storage.h"
#include "uuid.h"

#include "ble.h"


/*--------------------------------------------------------------------------------------------------
 * MACROS & CONSTANTS
 *------------------------------------------------------------------------------------------------*/
#define _BLE_DEVICE_NAME_LEN    (sizeof(BLE_DEVICE_NAME) - 1)


/*--------------------------------------------------------------------------------------------------
 * TYPE DEFINITIONS, STRUCTURES & ENUMERATIONS
 *------------------------------------------------------------------------------------------------*/
typedef struct bt_conn_cb              _ble_conn_s;
typedef struct bt_data                 _ble_data_s;
typedef struct bt_conn_auth_cb         _ble_conn_auth_s;
typedef struct bt_conn_auth_info_cb    _ble_conn_auth_info_s;
typedef struct bt_gatt_exchange_params _ble_mtu_exchange_params_s;
typedef struct bt_gatt_cb              _ble_gatt_cb_s;
typedef struct k_work_delayable        _ble_delayed_work_s;
typedef struct _ble_char
{
    uint32_t             uuid[4];   /* 128-bit */
    bool                 notify;
    struct bt_gatt_attr *p_notify_service;
    BLE_read_cb_t        read_cb;
    BLE_write_cb_t       write_cb;
    BLE_ccc_cb_t         ccc_cb;
} _ble_char_s;


/*--------------------------------------------------------------------------------------------------
 * PRIVATE CONSTANTS & VARIABLES
 *------------------------------------------------------------------------------------------------*/
/* BLE core */
static FS_file_t             _ble_settings_file  = NULL;
static FS_file_t             _ble_config_file    = NULL;
static bool                  _ble_fs_ready       = false;
static atomic_t              _ble_settings_loading = ATOMIC_INIT(0);
static bool                  _ble_enabled      = false;
static bool                  _ble_connected    = false;
static struct bt_conn       *_ble_current_conn = NULL;
static BLE_settings_s        _ble_settings = { 0 };
static BLE_config_s          _ble_config = (BLE_config_s) {
    .adv_name = BLE_DEVICE_NAME_PREFIX,
    .enabled  = false,
};
static _ble_conn_s                _ble_conn_cbs;
static _ble_conn_auth_s           _ble_conn_auth_cbs;
static _ble_conn_auth_info_s      _ble_conn_auth_info_cbs;
static _ble_mtu_exchange_params_s _ble_mtu_exchange_params;
static _ble_gatt_cb_s             _ble_gatt_cbs;

static _ble_data_s           _ble_ad_data[] =
{
    (_ble_data_s) {
        .type     = BT_DATA_FLAGS,
        .data_len = sizeof((uint8_t []) { (BT_LE_AD_GENERAL | BT_LE_AD_NO_BREDR) }),
        .data     = (const uint8_t *) ((uint8_t []) { (BT_LE_AD_GENERAL | BT_LE_AD_NO_BREDR) })
    },
    (_ble_data_s) {
        .type     = BT_DATA_NAME_COMPLETE,
        .data_len = (sizeof (BLE_DEVICE_NAME_PREFIX) - 1),
        .data     = (const uint8_t *) BLE_DEVICE_NAME_PREFIX
    }
};
static const _ble_data_s _ble_scan_data[] =
{
    BT_DATA_BYTES(BT_DATA_UUID128_ALL, UUID_CONTROLS_SERVICE_VAL)
};
static _ble_delayed_work_s _ble_delayed_work;
static _ble_char_s         _ble_char_table[BLE_CHAR_COUNT];

/* Shot data transfer state. */
static uint8_t  _ble_shot_data_status        = 0;
static uint8_t *_ble_shot_data_buf           = NULL;
static uint32_t _ble_shot_data_total_len     = 0;
static uint32_t _ble_shot_data_sent          = 0;
static bool     _ble_shot_data_transferring  = false;
static uint16_t _ble_shot_data_chunk_size    = 0;
static struct k_work _ble_eraseall_work;
static struct k_work _ble_dur_loc_work;
static uint8_t       _ble_dur_loc_duration;
static uint8_t       _ble_dur_loc_location;

/* BLE pairing. */
static struct k_work           _ble_pair_smp_work;
static struct k_work_delayable _ble_pair_delay_work; 
static struct bt_conn         *_ble_pair_conn     = NULL;
static struct k_work_delayable _ble_settings_write_dwork;
static struct k_work_delayable _ble_bond_clear_dwork;

/* Custom settings backend (persists Zephyr BT bond keys into _ble_config). */
static struct settings_store_itf _settings_itf;
static struct settings_store     _settings_store;


/*--------------------------------------------------------------------------------------------------
 * PRIVATE FUNCTION PROTOTYPES
 *------------------------------------------------------------------------------------------------*/
/* Connection callbacks */
static void _ble_connected_cb            (struct bt_conn *conn, uint8_t err);
static void _ble_disconnected_cb         (struct bt_conn *conn, uint8_t err);
static void _ble_security_changed_cb     (struct bt_conn *conn, bt_security_t level,
                                          enum bt_security_err err);
static void _ble_auth_cancel_cb          (struct bt_conn *conn);
static void _ble_pairing_complete_cb     (struct bt_conn *conn, bool bonded);
static enum bt_security_err _ble_pairing_accept_cb (struct bt_conn *conn, const struct bt_conn_pairing_feat *const feat);
static void _ble_pairing_failed_cb       (struct bt_conn *conn, enum bt_security_err reason);
static void _ble_att_mtu_updated_cb      (struct bt_conn *conn, uint16_t tx, uint16_t rx);
static void _ble_pair_delay_handler      (struct k_work *work);
static void _ble_mtu_exchange_cb         (struct bt_conn *conn, uint8_t err, struct bt_gatt_exchange_params *params);

/* Config persistence. */
static void _ble_settings_read  (void);
static void _ble_settings_write (void);

static void _ble_config_read  (void);
static void _ble_config_write (void);

/* Advertising name characteristic */
static ssize_t _ble_adv_name_write (struct bt_conn *conn, const struct bt_gatt_attr *attr,
                                    const void *buf, uint16_t len, uint16_t offset, uint8_t flags);
static ssize_t _ble_adv_name_read  (struct bt_conn *conn, const struct bt_gatt_attr *attr,
                                    void *buf, uint16_t len, uint16_t offset);

/* Bond clear. */
static ssize_t _ble_bond_clear_write (struct bt_conn *conn, const struct bt_gatt_attr *attr,
                                      const void *buf, uint16_t len, uint16_t offset, uint8_t flags);
                                    
static void    _ble_reenable_later (struct k_work *work);

/* Work handlers */
static void _ble_eraseall_work_handler (struct k_work *work);
static void _ble_dur_loc_work_handler  (struct k_work *work);
static void _ble_settings_write_work_handler (struct k_work *work);
static void _ble_bond_clear_dwork_handler (struct k_work *work);

/* APP characteristics */
static ssize_t _ble_app_main_pcb_version_read (struct bt_conn *conn, const struct bt_gatt_attr *attr,
                                               void *buf, uint16_t len, uint16_t offset);
static ssize_t _ble_app_ring_pcb_version_read (struct bt_conn *conn, const struct bt_gatt_attr *attr,
                                               void *buf, uint16_t len, uint16_t offset);
static ssize_t _ble_app_version_read          (struct bt_conn *conn, const struct bt_gatt_attr *attr,
                                               void *buf, uint16_t len, uint16_t offset);
static ssize_t _ble_app_mfg_id_read           (struct bt_conn *conn, const struct bt_gatt_attr *attr,
                                               void *buf, uint16_t len, uint16_t offset);
static ssize_t _ble_app_mfg_date_read         (struct bt_conn *conn, const struct bt_gatt_attr *attr,
                                               void *buf, uint16_t len, uint16_t offset);
static ssize_t _ble_app_pn_read               (struct bt_conn *conn, const struct bt_gatt_attr *attr,
                                               void *buf, uint16_t len, uint16_t offset);
static ssize_t _ble_app_sn_read               (struct bt_conn *conn, const struct bt_gatt_attr *attr,
                                               void *buf, uint16_t len, uint16_t offset);
static ssize_t _ble_app_power_count_read      (struct bt_conn *conn, const struct bt_gatt_attr *attr,
                                               void *buf, uint16_t len, uint16_t offset);
static ssize_t _ble_app_cli_enabled_write     (struct bt_conn *conn, const struct bt_gatt_attr *attr,
                                               const void *buf, uint16_t len, uint16_t offset,
                                               uint8_t flags);
static ssize_t _ble_app_cli_enabled_read      (struct bt_conn *conn, const struct bt_gatt_attr *attr,
                                               void *buf, uint16_t len, uint16_t offset);
static ssize_t _ble_app_reset_write           (struct bt_conn *conn, const struct bt_gatt_attr *attr,
                                               const void *buf, uint16_t len, uint16_t offset,
                                               uint8_t flags);

/* SCOPE RING characteristics */
static ssize_t _ble_level_option_write      (struct bt_conn *conn, const struct bt_gatt_attr *attr,
                                             const void *buf, uint16_t len, uint16_t offset, uint8_t flags);
static ssize_t _ble_level_option_read       (struct bt_conn *conn, const struct bt_gatt_attr *attr,
                                             void *buf, uint16_t len, uint16_t offset);
static void    _ble_level_option_changed    (const struct bt_gatt_attr *attr, uint16_t value);
static ssize_t _ble_level_viscosity_write   (struct bt_conn *conn, const struct bt_gatt_attr *attr,
                                             const void *buf, uint16_t len, uint16_t offset, uint8_t flags);
static ssize_t _ble_level_viscosity_read    (struct bt_conn *conn, const struct bt_gatt_attr *attr,
                                             void *buf, uint16_t len, uint16_t offset);
static void    _ble_level_viscosity_changed (const struct bt_gatt_attr *attr, uint16_t value);
static ssize_t _ble_level_sensitivity_write   (struct bt_conn *conn, const struct bt_gatt_attr *attr,
                                               const void *buf, uint16_t len, uint16_t offset, uint8_t flags);
static ssize_t _ble_level_sensitivity_read    (struct bt_conn *conn, const struct bt_gatt_attr *attr,
                                               void *buf, uint16_t len, uint16_t offset);
static void    _ble_level_sensitivity_changed (const struct bt_gatt_attr *attr, uint16_t value);
static ssize_t _ble_ring_color_write        (struct bt_conn *conn, const struct bt_gatt_attr *attr,
                                             const void *buf, uint16_t len, uint16_t offset, uint8_t flags);
static ssize_t _ble_ring_color_read         (struct bt_conn *conn, const struct bt_gatt_attr *attr,
                                             void *buf, uint16_t len, uint16_t offset);
static void    _ble_ring_color_changed      (const struct bt_gatt_attr *attr, uint16_t value);
static ssize_t _ble_ring_brightness_write   (struct bt_conn *conn, const struct bt_gatt_attr *attr,
                                             const void *buf, uint16_t len, uint16_t offset, uint8_t flags);
static ssize_t _ble_ring_brightness_read    (struct bt_conn *conn, const struct bt_gatt_attr *attr,
                                             void *buf, uint16_t len, uint16_t offset);
static void    _ble_ring_brightness_changed (const struct bt_gatt_attr *attr, uint16_t value);
static ssize_t _ble_indicator_color_write        (struct bt_conn *conn, const struct bt_gatt_attr *attr,
                                                  const void *buf, uint16_t len, uint16_t offset, uint8_t flags);
static ssize_t _ble_indicator_color_read         (struct bt_conn *conn, const struct bt_gatt_attr *attr,
                                                  void *buf, uint16_t len, uint16_t offset);
static void    _ble_indicator_color_changed      (const struct bt_gatt_attr *attr, uint16_t value);
static ssize_t _ble_indicator_brightness_write   (struct bt_conn *conn, const struct bt_gatt_attr *attr,
                                                  const void *buf, uint16_t len, uint16_t offset, uint8_t flags);
static ssize_t _ble_indicator_brightness_read    (struct bt_conn *conn, const struct bt_gatt_attr *attr,
                                                  void *buf, uint16_t len, uint16_t offset);
static void    _ble_indicator_brightness_changed (const struct bt_gatt_attr *attr, uint16_t value);
static ssize_t _ble_level_color_write        (struct bt_conn *conn, const struct bt_gatt_attr *attr,
                                              const void *buf, uint16_t len, uint16_t offset, uint8_t flags);
static ssize_t _ble_level_color_read         (struct bt_conn *conn, const struct bt_gatt_attr *attr,
                                              void *buf, uint16_t len, uint16_t offset);
static void    _ble_level_color_changed      (const struct bt_gatt_attr *attr, uint16_t value);
static ssize_t _ble_level_brightness_write   (struct bt_conn *conn, const struct bt_gatt_attr *attr,
                                              const void *buf, uint16_t len, uint16_t offset, uint8_t flags);
static ssize_t _ble_level_brightness_read    (struct bt_conn *conn, const struct bt_gatt_attr *attr,
                                              void *buf, uint16_t len, uint16_t offset);
static void    _ble_level_brightness_changed (const struct bt_gatt_attr *attr, uint16_t value);

/* BUBBLE / FIBER / PIN */
static ssize_t _ble_bubble_brightness_write   (struct bt_conn *conn, const struct bt_gatt_attr *attr,
                                               const void *buf, uint16_t len, uint16_t offset, uint8_t flags);
static ssize_t _ble_bubble_brightness_read    (struct bt_conn *conn, const struct bt_gatt_attr *attr,
                                               void *buf, uint16_t len, uint16_t offset);
static void    _ble_bubble_brightness_changed (const struct bt_gatt_attr *attr, uint16_t value);
static ssize_t _ble_fiber_brightness_write    (struct bt_conn *conn, const struct bt_gatt_attr *attr,
                                               const void *buf, uint16_t len, uint16_t offset, uint8_t flags);
static ssize_t _ble_fiber_brightness_read     (struct bt_conn *conn, const struct bt_gatt_attr *attr,
                                               void *buf, uint16_t len, uint16_t offset);
static void    _ble_fiber_brightness_changed  (const struct bt_gatt_attr *attr, uint16_t value);
static ssize_t _ble_pin_brightness_write      (struct bt_conn *conn, const struct bt_gatt_attr *attr,
                                               const void *buf, uint16_t len, uint16_t offset, uint8_t flags);
static ssize_t _ble_pin_brightness_read       (struct bt_conn *conn, const struct bt_gatt_attr *attr,
                                               void *buf, uint16_t len, uint16_t offset);
static void    _ble_pin_brightness_changed    (const struct bt_gatt_attr *attr, uint16_t value);

/* MOTION */
static ssize_t _ble_level_calibrate_write   (struct bt_conn *conn, const struct bt_gatt_attr *attr,
                                             const void *buf, uint16_t len, uint16_t offset, uint8_t flags);
static ssize_t _ble_level_calibrate_read    (struct bt_conn *conn, const struct bt_gatt_attr *attr,
                                             void *buf, uint16_t len, uint16_t offset);
static void    _ble_level_calibrate_changed (const struct bt_gatt_attr *attr, uint16_t value);
static ssize_t _ble_level_angle_read        (struct bt_conn *conn, const struct bt_gatt_attr *attr,
                                             void *buf, uint16_t len, uint16_t offset);
static void    _ble_level_angle_changed     (const struct bt_gatt_attr *attr, uint16_t value);

/* ALS */
static ssize_t _ble_ambient_light_read    (struct bt_conn *conn, const struct bt_gatt_attr *attr,
                                           void *buf, uint16_t len, uint16_t offset);
static void    _ble_ambient_light_changed (const struct bt_gatt_attr *attr, uint16_t value);

/* FSM */
static ssize_t _ble_fsm_timeout_write           (struct bt_conn *conn, const struct bt_gatt_attr *attr,
                                                  const void *buf, uint16_t len, uint16_t offset, uint8_t flags);
static ssize_t _ble_fsm_timeout_read            (struct bt_conn *conn, const struct bt_gatt_attr *attr,
                                                  void *buf, uint16_t len, uint16_t offset);
static void    _ble_fsm_timeout_changed         (const struct bt_gatt_attr *attr, uint16_t value);
static ssize_t _ble_fsm_auto_brightness_write   (struct bt_conn *conn, const struct bt_gatt_attr *attr,
                                                  const void *buf, uint16_t len, uint16_t offset, uint8_t flags);
static ssize_t _ble_fsm_auto_brightness_read    (struct bt_conn *conn, const struct bt_gatt_attr *attr,
                                                  void *buf, uint16_t len, uint16_t offset);
static void    _ble_fsm_auto_brightness_changed (const struct bt_gatt_attr *attr, uint16_t value);
static ssize_t _ble_fsm_mode_write              (struct bt_conn *conn, const struct bt_gatt_attr *attr,
                                                  const void *buf, uint16_t len, uint16_t offset, uint8_t flags);
static ssize_t _ble_fsm_mode_read               (struct bt_conn *conn, const struct bt_gatt_attr *attr,
                                                  void *buf, uint16_t len, uint16_t offset);
static void    _ble_fsm_mode_changed            (const struct bt_gatt_attr *attr, uint16_t value);
static ssize_t _ble_fsm_app_config_write        (struct bt_conn *conn, const struct bt_gatt_attr *attr,
                                                  const void *buf, uint16_t len, uint16_t offset, uint8_t flags);
static ssize_t _ble_fsm_app_config_read         (struct bt_conn *conn, const struct bt_gatt_attr *attr,
                                                  void *buf, uint16_t len, uint16_t offset);

/* BATTERY */
static ssize_t _ble_battery_level_read    (struct bt_conn *conn, const struct bt_gatt_attr *attr,
                                           void *buf, uint16_t len, uint16_t offset);
static void    _ble_battery_level_changed (const struct bt_gatt_attr *attr, uint16_t value);

/* SHOT CAPTURE */
static ssize_t _ble_shot_capture_tap_ths_write        (struct bt_conn *conn, const struct bt_gatt_attr *attr,
                                                        const void *buf, uint16_t len, uint16_t offset, uint8_t flags);
static ssize_t _ble_shot_capture_tap_ths_read         (struct bt_conn *conn, const struct bt_gatt_attr *attr,
                                                        void *buf, uint16_t len, uint16_t offset);
static ssize_t _ble_shot_capture_quiet_time_write     (struct bt_conn *conn, const struct bt_gatt_attr *attr,
                                                        const void *buf, uint16_t len, uint16_t offset, uint8_t flags);
static ssize_t _ble_shot_capture_quiet_time_read      (struct bt_conn *conn, const struct bt_gatt_attr *attr,
                                                        void *buf, uint16_t len, uint16_t offset);
static ssize_t _ble_shot_capture_shock_time_write     (struct bt_conn *conn, const struct bt_gatt_attr *attr,
                                                        const void *buf, uint16_t len, uint16_t offset, uint8_t flags);
static ssize_t _ble_shot_capture_shock_time_read      (struct bt_conn *conn, const struct bt_gatt_attr *attr,
                                                        void *buf, uint16_t len, uint16_t offset);
static ssize_t _ble_shot_capture_user_shot_cnt_reset  (struct bt_conn *conn, const struct bt_gatt_attr *attr,
                                                        const void *buf, uint16_t len, uint16_t offset, uint8_t flags);
static ssize_t _ble_shot_capture_user_shot_cnt_read   (struct bt_conn *conn, const struct bt_gatt_attr *attr,
                                                        void *buf, uint16_t len, uint16_t offset);
static void    _shot_capture_user_shot_cnt_ccc_changed (const struct bt_gatt_attr *attr, uint16_t value);
static ssize_t _ble_shot_capture_shot_cnt_reset       (struct bt_conn *conn, const struct bt_gatt_attr *attr,
                                                        const void *buf, uint16_t len, uint16_t offset, uint8_t flags);
static ssize_t _ble_shot_capture_shot_cnt_read        (struct bt_conn *conn, const struct bt_gatt_attr *attr,
                                                        void *buf, uint16_t len, uint16_t offset);
static void    _shot_capture_shot_cnt_ccc_changed     (const struct bt_gatt_attr *attr, uint16_t value);
static ssize_t _ble_shot_capture_capture_en_write     (struct bt_conn *conn, const struct bt_gatt_attr *attr,
                                                        const void *buf, uint16_t len, uint16_t offset, uint8_t flags);
static ssize_t _ble_shot_capture_capture_en_read      (struct bt_conn *conn, const struct bt_gatt_attr *attr,
                                                        void *buf, uint16_t len, uint16_t offset);
static ssize_t _ble_shot_capture_dur_loc_write        (struct bt_conn *conn, const struct bt_gatt_attr *attr,
                                                        const void *buf, uint16_t len, uint16_t offset, uint8_t flags);
static ssize_t _ble_shot_capture_dur_loc_read         (struct bt_conn *conn, const struct bt_gatt_attr *attr,
                                                        void *buf, uint16_t len, uint16_t offset);
static ssize_t _ble_shot_data_request_write           (struct bt_conn *conn, const struct bt_gatt_attr *attr,
                                                        const void *buf, uint16_t len, uint16_t offset, uint8_t flags);
static ssize_t _ble_shot_data_request_read            (struct bt_conn *conn, const struct bt_gatt_attr *attr,
                                                        void *buf, uint16_t len, uint16_t offset);
static void    _ble_shot_data_request_ccc_changed     (const struct bt_gatt_attr *attr, uint16_t value);
static void    _ble_shot_data_ccc_changed             (const struct bt_gatt_attr *attr, uint16_t value);
static void    _ble_shot_data_send_next               (struct bt_conn *conn, void *user_data);

/* BLE PAIRING */
static void    _ble_pair_smp_handler           (struct k_work *work);
static void    _ble_bond_clear                 (void);

/* RTC */
static ssize_t _ble_rtc_secs_read (struct bt_conn *conn, const struct bt_gatt_attr *attr,
                                   void *buf, uint16_t len, uint16_t offset);
{
    uint32_t s_elapsed = (uint32_t) RTC_seconds_elapsed ();

    return bt_gatt_attr_read (conn, attr, buf, len, offset, s_elapsed, sizeof (s_elapsed));
}

/* SETTINGS BACKEND */
static ssize_t _settings_read_cb (void *cb_arg, void *data, size_t len);
static int     _settings_load (struct settings_store *cs, const struct settings_load_arg *arg);
static int     _settings_save (struct settings_store *cs, const char *name,
                               const void *value, size_t val_len);
static int     _settings_entry_find  (const char *key);
static int     _settings_entry_alloc (const char *key);


/*--------------------------------------------------------------------------------------------------
 * BLE SERVICES & CHARACTERISTICS
 *------------------------------------------------------------------------------------------------*/
BT_GATT_SERVICE_DEFINE(_ble_service,
    BT_GATT_PRIMARY_SERVICE(UUID_CONTROLS_SERVICE), 	/* Index 0  */

    /* ADV_NAME - Index 1, 2, 3 */
    BT_GATT_CHARACTERISTIC(UUID_BLE_ADV_NAME, (BT_GATT_CHRC_READ | BT_GATT_CHRC_NOTIFY | BT_GATT_CHRC_WRITE),
        (BT_GATT_PERM_READ | BT_GATT_PERM_WRITE), _ble_adv_name_read, _ble_adv_name_write, NULL),
    BT_GATT_CCC(NULL, (BT_GATT_PERM_READ | BT_GATT_PERM_WRITE)),

    /* BOND_CLEAR - Index 4, 5 (write-only, no CCC) */
    BT_GATT_CHARACTERISTIC(UUID_BLE_BOND_CLEAR, BT_GATT_CHRC_WRITE,
        BT_GATT_PERM_WRITE, NULL, _ble_bond_clear_write, NULL),

    /* RING_COLOR - Index 6, 7, 8 */
    BT_GATT_CHARACTERISTIC(UUID_RING_COLOR, (BT_GATT_CHRC_READ | BT_GATT_CHRC_NOTIFY | BT_GATT_CHRC_WRITE),
        (BT_GATT_PERM_READ | BT_GATT_PERM_WRITE), _ble_ring_color_read, _ble_ring_color_write, NULL),
    BT_GATT_CCC(_ble_ring_color_changed, (BT_GATT_PERM_READ | BT_GATT_PERM_WRITE)),

    /* RING_BRIGHTNESS - Index 9, 10, 11 */
    BT_GATT_CHARACTERISTIC(UUID_RING_BRIGHTNESS, (BT_GATT_CHRC_READ | BT_GATT_CHRC_NOTIFY | BT_GATT_CHRC_WRITE),
        (BT_GATT_PERM_READ | BT_GATT_PERM_WRITE), _ble_ring_brightness_read, _ble_ring_brightness_write, NULL),
    BT_GATT_CCC(_ble_ring_brightness_changed, (BT_GATT_PERM_READ | BT_GATT_PERM_WRITE)),

    /* INDICATOR_COLOR - Index 12, 13, 14 */
    BT_GATT_CHARACTERISTIC(UUID_INDICATOR_COLOR, (BT_GATT_CHRC_READ | BT_GATT_CHRC_NOTIFY | BT_GATT_CHRC_WRITE),
        (BT_GATT_PERM_READ | BT_GATT_PERM_WRITE), _ble_indicator_color_read, _ble_indicator_color_write, NULL),
    BT_GATT_CCC(_ble_indicator_color_changed, (BT_GATT_PERM_READ | BT_GATT_PERM_WRITE)),

    /* INDICATOR_BRIGHTNESS - Index 15, 16, 17 */
    BT_GATT_CHARACTERISTIC(UUID_INDICATOR_BRIGHTNESS, (BT_GATT_CHRC_READ | BT_GATT_CHRC_NOTIFY | BT_GATT_CHRC_WRITE),
        (BT_GATT_PERM_READ | BT_GATT_PERM_WRITE), _ble_indicator_brightness_read, _ble_indicator_brightness_write, NULL),
    BT_GATT_CCC(_ble_indicator_brightness_changed, (BT_GATT_PERM_READ | BT_GATT_PERM_WRITE)),

    /* LEVEL_COLOR - Index 18, 19, 20 */
    BT_GATT_CHARACTERISTIC(UUID_LEVEL_COLOR, (BT_GATT_CHRC_READ | BT_GATT_CHRC_NOTIFY | BT_GATT_CHRC_WRITE),
        (BT_GATT_PERM_READ | BT_GATT_PERM_WRITE), _ble_level_color_read, _ble_level_color_write, NULL),
    BT_GATT_CCC(_ble_level_color_changed, (BT_GATT_PERM_READ | BT_GATT_PERM_WRITE)),

    /* LEVEL_BRIGHTNESS - Index 21, 22, 23 */
    BT_GATT_CHARACTERISTIC(UUID_LEVEL_BRIGHTNESS, (BT_GATT_CHRC_READ | BT_GATT_CHRC_NOTIFY | BT_GATT_CHRC_WRITE),
        (BT_GATT_PERM_READ | BT_GATT_PERM_WRITE), _ble_level_brightness_read, _ble_level_brightness_write, NULL),
    BT_GATT_CCC(_ble_level_brightness_changed, (BT_GATT_PERM_READ | BT_GATT_PERM_WRITE)),

    /* BUBBLE_BRIGHTNESS - Index 24, 25, 26 */
    BT_GATT_CHARACTERISTIC(UUID_BUBBLE_BRIGHTNESS, (BT_GATT_CHRC_READ | BT_GATT_CHRC_NOTIFY | BT_GATT_CHRC_WRITE),
        (BT_GATT_PERM_READ | BT_GATT_PERM_WRITE), _ble_bubble_brightness_read, _ble_bubble_brightness_write, NULL),
    BT_GATT_CCC(_ble_bubble_brightness_changed, (BT_GATT_PERM_READ | BT_GATT_PERM_WRITE)),

    /* FIBER_BRIGHTNESS - Index 27, 28, 29 */
    BT_GATT_CHARACTERISTIC(UUID_FIBER_BRIGHTNESS, (BT_GATT_CHRC_READ | BT_GATT_CHRC_NOTIFY | BT_GATT_CHRC_WRITE),
        (BT_GATT_PERM_READ | BT_GATT_PERM_WRITE), _ble_fiber_brightness_read, _ble_fiber_brightness_write, NULL),
    BT_GATT_CCC(_ble_fiber_brightness_changed, (BT_GATT_PERM_READ | BT_GATT_PERM_WRITE)),

    /* PIN_BRIGHTNESS - Index 30, 31, 32 */
    BT_GATT_CHARACTERISTIC(UUID_PIN_BRIGHTNESS, (BT_GATT_CHRC_READ | BT_GATT_CHRC_NOTIFY | BT_GATT_CHRC_WRITE),
        (BT_GATT_PERM_READ | BT_GATT_PERM_WRITE), _ble_pin_brightness_read, _ble_pin_brightness_write, NULL),
    BT_GATT_CCC(_ble_pin_brightness_changed, (BT_GATT_PERM_READ | BT_GATT_PERM_WRITE)),

    /* LEVEL_CALIBRATE - Index 33, 34, 35 */
    BT_GATT_CHARACTERISTIC(UUID_LEVEL_CALIBRATE, (BT_GATT_CHRC_READ | BT_GATT_CHRC_NOTIFY | BT_GATT_CHRC_WRITE),
        (BT_GATT_PERM_READ | BT_GATT_PERM_WRITE), _ble_level_calibrate_read, _ble_level_calibrate_write, NULL),
    BT_GATT_CCC(_ble_level_calibrate_changed, (BT_GATT_PERM_READ | BT_GATT_PERM_WRITE)),

    /* LEVEL_SENSITIVITY - Index 36, 37, 38 */
    BT_GATT_CHARACTERISTIC(UUID_LEVEL_SENSITIVITY, (BT_GATT_CHRC_READ | BT_GATT_CHRC_NOTIFY | BT_GATT_CHRC_WRITE),
        (BT_GATT_PERM_READ | BT_GATT_PERM_WRITE), _ble_level_sensitivity_read, _ble_level_sensitivity_write, NULL),
    BT_GATT_CCC(_ble_level_sensitivity_changed, (BT_GATT_PERM_READ | BT_GATT_PERM_WRITE)),

    /* LEVEL_OPTION - Index 39, 40, 41 */
    BT_GATT_CHARACTERISTIC(UUID_LEVEL_OPTION, (BT_GATT_CHRC_READ | BT_GATT_CHRC_NOTIFY | BT_GATT_CHRC_WRITE),
        (BT_GATT_PERM_READ | BT_GATT_PERM_WRITE), _ble_level_option_read, _ble_level_option_write, NULL),
    BT_GATT_CCC(_ble_level_option_changed, (BT_GATT_PERM_READ | BT_GATT_PERM_WRITE)),

    /* LEVEL_VISCOSITY - Index 42, 43, 44 */
    BT_GATT_CHARACTERISTIC(UUID_LEVEL_VISCOSITY, (BT_GATT_CHRC_READ | BT_GATT_CHRC_NOTIFY | BT_GATT_CHRC_WRITE),
        (BT_GATT_PERM_READ | BT_GATT_PERM_WRITE), _ble_level_viscosity_read, _ble_level_viscosity_write, NULL),
    BT_GATT_CCC(_ble_level_viscosity_changed, (BT_GATT_PERM_READ | BT_GATT_PERM_WRITE)),

    /* LEVEL_ANGLE - Index 45, 46, 47 */
    BT_GATT_CHARACTERISTIC(UUID_LEVEL_ANGLE, (BT_GATT_CHRC_READ | BT_GATT_CHRC_NOTIFY),
        BT_GATT_PERM_READ, _ble_level_angle_read, NULL, NULL),
    BT_GATT_CCC(_ble_level_angle_changed, (BT_GATT_PERM_READ | BT_GATT_PERM_WRITE)),

    /* AMBIENT_LIGHT - Index 48, 49, 50 */
    BT_GATT_CHARACTERISTIC(UUID_AMBIENT_LIGHT, (BT_GATT_CHRC_READ | BT_GATT_CHRC_NOTIFY),
        BT_GATT_PERM_READ, _ble_ambient_light_read, NULL, NULL),
    BT_GATT_CCC(_ble_ambient_light_changed, (BT_GATT_PERM_READ | BT_GATT_PERM_WRITE)),

    /* FSM_TIMEOUT - Index 51, 52, 53 */
    BT_GATT_CHARACTERISTIC(UUID_FSM_TIMEOUT, (BT_GATT_CHRC_READ | BT_GATT_CHRC_NOTIFY | BT_GATT_CHRC_WRITE),
        (BT_GATT_PERM_READ | BT_GATT_PERM_WRITE), _ble_fsm_timeout_read, _ble_fsm_timeout_write, NULL),
    BT_GATT_CCC(_ble_fsm_timeout_changed, (BT_GATT_PERM_READ | BT_GATT_PERM_WRITE)),

    /* FSM_AUTO_BRIGHTNESS - Index 54, 55, 56 */
    BT_GATT_CHARACTERISTIC(UUID_FSM_AUTO_BRIGHTNESS, (BT_GATT_CHRC_READ | BT_GATT_CHRC_NOTIFY | BT_GATT_CHRC_WRITE),
        (BT_GATT_PERM_READ | BT_GATT_PERM_WRITE), _ble_fsm_auto_brightness_read, _ble_fsm_auto_brightness_write, NULL),
    BT_GATT_CCC(_ble_fsm_auto_brightness_changed, (BT_GATT_PERM_READ | BT_GATT_PERM_WRITE)),

    /* FSM_APP_CONFIG - Index 57, 58, 59 */
    BT_GATT_CHARACTERISTIC(UUID_FSM_APP_CONFIG, (BT_GATT_CHRC_READ | BT_GATT_CHRC_NOTIFY | BT_GATT_CHRC_WRITE),
        (BT_GATT_PERM_READ | BT_GATT_PERM_WRITE), _ble_fsm_app_config_read, _ble_fsm_app_config_write, NULL),
    BT_GATT_CCC(NULL, (BT_GATT_PERM_READ | BT_GATT_PERM_WRITE)),

    /* APP_MPCB_VERSION - Index 60, 61, 62 */
    BT_GATT_CHARACTERISTIC(UUID_APP_MPCB_VERSION, (BT_GATT_CHRC_READ | BT_GATT_CHRC_NOTIFY),
        BT_GATT_PERM_READ, _ble_app_main_pcb_version_read, NULL, NULL),
    BT_GATT_CCC(NULL, (BT_GATT_PERM_READ | BT_GATT_PERM_WRITE)),

    /* APP_RPCB_VERSION - Index 63, 64, 65 */
    BT_GATT_CHARACTERISTIC(UUID_APP_RPCB_VERSION, (BT_GATT_CHRC_READ | BT_GATT_CHRC_NOTIFY),
        BT_GATT_PERM_READ, _ble_app_ring_pcb_version_read, NULL, NULL),
    BT_GATT_CCC(NULL, (BT_GATT_PERM_READ | BT_GATT_PERM_WRITE)),

    /* APP_VERSION - Index 66, 67, 68 */
    BT_GATT_CHARACTERISTIC(UUID_APP_VERSION, (BT_GATT_CHRC_READ | BT_GATT_CHRC_NOTIFY),
        BT_GATT_PERM_READ, _ble_app_version_read, NULL, NULL),
    BT_GATT_CCC(NULL, (BT_GATT_PERM_READ | BT_GATT_PERM_WRITE)),

    /* APP_MFG_ID - Index 69, 70, 71 */
    BT_GATT_CHARACTERISTIC(UUID_APP_MFG_ID, (BT_GATT_CHRC_READ | BT_GATT_CHRC_NOTIFY),
        BT_GATT_PERM_READ, _ble_app_mfg_id_read, NULL, NULL),
    BT_GATT_CCC(NULL, (BT_GATT_PERM_READ | BT_GATT_PERM_WRITE)),

    /* APP_MFG_DATE - Index 72, 73, 74 */
    BT_GATT_CHARACTERISTIC(UUID_APP_MFG_DATE, (BT_GATT_CHRC_READ | BT_GATT_CHRC_NOTIFY),
        BT_GATT_PERM_READ, _ble_app_mfg_date_read, NULL, NULL),
    BT_GATT_CCC(NULL, (BT_GATT_PERM_READ | BT_GATT_PERM_WRITE)),

    /* APP_PN - Index 75, 76, 77 */
    BT_GATT_CHARACTERISTIC(UUID_APP_PN, (BT_GATT_CHRC_READ | BT_GATT_CHRC_NOTIFY),
        BT_GATT_PERM_READ, _ble_app_pn_read, NULL, NULL),
    BT_GATT_CCC(NULL, (BT_GATT_PERM_READ | BT_GATT_PERM_WRITE)),

    /* APP_SN - Index 78, 79, 80 */
    BT_GATT_CHARACTERISTIC(UUID_APP_SN, (BT_GATT_CHRC_READ | BT_GATT_CHRC_NOTIFY),
        BT_GATT_PERM_READ, _ble_app_sn_read, NULL, NULL),
    BT_GATT_CCC(NULL, (BT_GATT_PERM_READ | BT_GATT_PERM_WRITE)),

    /* APP_POWER_COUNT - Index 81, 82, 83 */
    BT_GATT_CHARACTERISTIC(UUID_APP_POWER_COUNT, (BT_GATT_CHRC_READ | BT_GATT_CHRC_NOTIFY),
        BT_GATT_PERM_READ, _ble_app_power_count_read, NULL, NULL),
    BT_GATT_CCC(NULL, (BT_GATT_PERM_READ | BT_GATT_PERM_WRITE)),

    /* APP_CLI_ENABLED - Index 84, 85, 86 */
    BT_GATT_CHARACTERISTIC(UUID_APP_CLI_ENABLED, (BT_GATT_CHRC_READ | BT_GATT_CHRC_NOTIFY | BT_GATT_CHRC_WRITE),
        (BT_GATT_PERM_READ | BT_GATT_PERM_WRITE), _ble_app_cli_enabled_read, _ble_app_cli_enabled_write, NULL),
    BT_GATT_CCC(NULL, (BT_GATT_PERM_READ | BT_GATT_PERM_WRITE)),

    /* APP_RESET - Index 87, 88 (write-only, no CCC) */
    BT_GATT_CHARACTERISTIC(UUID_APP_RESET, BT_GATT_CHRC_WRITE,
        BT_GATT_PERM_WRITE, NULL, _ble_app_reset_write, NULL),

    /* BATTERY_STATUS - Index 89, 90, 91 */
    BT_GATT_CHARACTERISTIC(UUID_BATTERY_STATUS, (BT_GATT_CHRC_READ | BT_GATT_CHRC_NOTIFY),
        BT_GATT_PERM_READ, _ble_battery_level_read, NULL, NULL),
    BT_GATT_CCC(_ble_battery_level_changed, (BT_GATT_PERM_READ | BT_GATT_PERM_WRITE)),

    /* FSM_MODE - Index 92, 93, 94 */
    BT_GATT_CHARACTERISTIC(UUID_FSM_MODE, (BT_GATT_CHRC_READ | BT_GATT_CHRC_NOTIFY | BT_GATT_CHRC_WRITE),
        (BT_GATT_PERM_READ | BT_GATT_PERM_WRITE), _ble_fsm_mode_read, _ble_fsm_mode_write, NULL),
    BT_GATT_CCC(_ble_fsm_mode_changed, (BT_GATT_PERM_READ | BT_GATT_PERM_WRITE)),

    /* RTC - Index 95, 96, 97 */
    BT_GATT_CHARACTERISTIC(UUID_RTC_SECS, (BT_GATT_CHRC_READ | BT_GATT_CHRC_NOTIFY),
        BT_GATT_PERM_READ, _ble_rtc_secs_read, NULL, NULL),
    BT_GATT_CCC(NULL, (BT_GATT_PERM_READ | BT_GATT_PERM_WRITE)),
);

BT_GATT_SERVICE_DEFINE(
  _shot_capture_service, BT_GATT_PRIMARY_SERVICE(UUID_SHOT_CAPTURE_SERVICE),
  BT_GATT_CHARACTERISTIC(UUID_SHOT_CAPTURE_TAP_THS, (BT_GATT_CHRC_READ | BT_GATT_CHRC_WRITE),
    (BT_GATT_PERM_READ | BT_GATT_PERM_WRITE), _ble_shot_capture_tap_ths_read, _ble_shot_capture_tap_ths_write, NULL),
  BT_GATT_CHARACTERISTIC(UUID_SHOT_CAPTURE_QUIET_TIME, (BT_GATT_CHRC_READ | BT_GATT_CHRC_WRITE),
    (BT_GATT_PERM_READ | BT_GATT_PERM_WRITE), _ble_shot_capture_quiet_time_read, _ble_shot_capture_quiet_time_write, NULL),
  BT_GATT_CHARACTERISTIC(UUID_SHOT_CAPTURE_SHOCK_TIME, (BT_GATT_CHRC_READ | BT_GATT_CHRC_WRITE),
    (BT_GATT_PERM_READ | BT_GATT_PERM_WRITE), _ble_shot_capture_shock_time_read, _ble_shot_capture_shock_time_write, NULL),
  BT_GATT_CHARACTERISTIC(UUID_SHOT_CAPTURE_USER_SHOT_CNT, (BT_GATT_CHRC_READ | BT_GATT_CHRC_NOTIFY | BT_GATT_CHRC_WRITE),
    (BT_GATT_PERM_READ | BT_GATT_PERM_WRITE), _ble_shot_capture_user_shot_cnt_read, _ble_shot_capture_user_shot_cnt_reset, NULL),
  BT_GATT_CCC(_shot_capture_user_shot_cnt_ccc_changed, BT_GATT_PERM_READ | BT_GATT_PERM_WRITE),
  BT_GATT_CHARACTERISTIC(UUID_SHOT_CAPTURE_SHOT_CNT, (BT_GATT_CHRC_READ | BT_GATT_CHRC_NOTIFY | BT_GATT_CHRC_WRITE),
    (BT_GATT_PERM_READ | BT_GATT_PERM_WRITE), _ble_shot_capture_shot_cnt_read, _ble_shot_capture_shot_cnt_reset, NULL),
  BT_GATT_CCC(_shot_capture_shot_cnt_ccc_changed, BT_GATT_PERM_READ | BT_GATT_PERM_WRITE),
  BT_GATT_CHARACTERISTIC(UUID_SHOT_CAPTURE_CAPTURE_EN, (BT_GATT_CHRC_READ | BT_GATT_CHRC_WRITE),
    (BT_GATT_PERM_READ | BT_GATT_PERM_WRITE), _ble_shot_capture_capture_en_read, _ble_shot_capture_capture_en_write, NULL),
  BT_GATT_CHARACTERISTIC(UUID_SHOT_CAPTURE_DUR_LOC, (BT_GATT_CHRC_READ | BT_GATT_CHRC_WRITE),
    (BT_GATT_PERM_READ | BT_GATT_PERM_WRITE), _ble_shot_capture_dur_loc_read, _ble_shot_capture_dur_loc_write, NULL),
  BT_GATT_CHARACTERISTIC(UUID_SHOT_CAPTURE_SHOT_DATA_REQUEST, (BT_GATT_CHRC_READ | BT_GATT_CHRC_NOTIFY | BT_GATT_CHRC_WRITE),
    (BT_GATT_PERM_READ | BT_GATT_PERM_WRITE), _ble_shot_data_request_read, _ble_shot_data_request_write, NULL),
  BT_GATT_CCC(_ble_shot_data_request_ccc_changed, BT_GATT_PERM_READ | BT_GATT_PERM_WRITE),
  BT_GATT_CHARACTERISTIC(UUID_SHOT_CAPTURE_SHOT_DATA, BT_GATT_CHRC_NOTIFY,
    BT_GATT_PERM_NONE, NULL, NULL, NULL),
  BT_GATT_CCC(_ble_shot_data_ccc_changed, BT_GATT_PERM_READ | BT_GATT_PERM_WRITE),
);


/*--------------------------------------------------------------------------------------------------
 * PUBLIC FUNCTION DEFINITIONS
 *------------------------------------------------------------------------------------------------*/
void BLE_init (void)
{
    PRINT (INFO, "Initializing BLE...\r\n");

    if (FS_init () < ERRNO_SUCCESS)
    {
        PRINT (ERROR, "Failure to initialize filesystem.\r\n");
    }

    _ble_config_read ();  
    _ble_settings_read ();
    
    _ble_conn_cbs.connected        = _ble_connected_cb;
    _ble_conn_cbs.disconnected     = _ble_disconnected_cb;
    _ble_conn_cbs.security_changed = _ble_security_changed_cb;
    _ble_gatt_cbs.att_mtu_updated  = _ble_att_mtu_updated_cb;
    bt_conn_cb_register (&_ble_conn_cbs);
    bt_gatt_cb_register (&_ble_gatt_cbs);

    _ble_conn_auth_cbs.passkey_display       = NULL;
    _ble_conn_auth_cbs.passkey_entry         = NULL;
    _ble_conn_auth_cbs.passkey_confirm       = NULL;
	_ble_conn_auth_cbs.pairing_accept        = _ble_pairing_accept_cb;
    _ble_conn_auth_cbs.cancel                = _ble_auth_cancel_cb;
    _ble_conn_auth_info_cbs.pairing_complete = _ble_pairing_complete_cb;
    _ble_conn_auth_info_cbs.pairing_failed   = _ble_pairing_failed_cb;

    if (bt_conn_auth_cb_register (&_ble_conn_auth_cbs))
    {
        PRINT (ERROR, "Failed to register authorization callbacks.\n");
        return;
    }
    if (bt_conn_auth_info_cb_register (&_ble_conn_auth_info_cbs))
    {
        PRINT (ERROR, "Failed to register authorization info callbacks.\n");
        return;
    }

    if (!strcmp (_ble_config.adv_name, BLE_DEVICE_NAME_PREFIX))
    {
        strcat (_ble_config.adv_name, APP_info_get().sn);
        _ble_config_write ();
    }
    _ble_ad_data[1].data     = _ble_config.adv_name;
    _ble_ad_data[1].data_len = strlen (_ble_config.adv_name);
    bt_set_name (_ble_config.adv_name);

    /* Work items. */
    k_work_init_delayable (&_ble_delayed_work,         _ble_reenable_later);
	k_work_init_delayable (&_ble_pair_delay_work,      _ble_pair_delay_handler);
    k_work_init_delayable (&_ble_settings_write_dwork, _ble_settings_write_work_handler);
    k_work_init_delayable (&_ble_bond_clear_dwork,     _ble_bond_clear_dwork_handler);
    k_work_init           (&_ble_eraseall_work,        _ble_eraseall_work_handler);
    k_work_init           (&_ble_dur_loc_work,         _ble_dur_loc_work_handler);
    k_work_init           (&_ble_pair_smp_work,        _ble_pair_smp_handler);


    /* Set up notify service attribute pointers. */
    /* _ble_service */
    _ble_char_table[(uint32_t) BLE_CHAR_ADV_NAME].p_notify_service             = &_ble_service.attrs[2];
    /* BLE_CHAR_PAIRING (BOND_CLEAR) is write-only — no notify needed */
    _ble_char_table[(uint32_t) BLE_CHAR_RING_COLOR].p_notify_service           = &_ble_service.attrs[7];
    _ble_char_table[(uint32_t) BLE_CHAR_RING_BRIGHTNESS].p_notify_service      = &_ble_service.attrs[10];
    _ble_char_table[(uint32_t) BLE_CHAR_INDICATOR_COLOR].p_notify_service      = &_ble_service.attrs[13];
    _ble_char_table[(uint32_t) BLE_CHAR_INDICATOR_BRIGHTNESS].p_notify_service = &_ble_service.attrs[16];
    _ble_char_table[(uint32_t) BLE_CHAR_LEVEL_COLOR].p_notify_service          = &_ble_service.attrs[19];
    _ble_char_table[(uint32_t) BLE_CHAR_LEVEL_BRIGHTNESS].p_notify_service     = &_ble_service.attrs[22];
    _ble_char_table[(uint32_t) BLE_CHAR_BUBBLE_BRIGHTNESS].p_notify_service    = &_ble_service.attrs[25];
    _ble_char_table[(uint32_t) BLE_CHAR_FIBER_BRIGHTNESS].p_notify_service     = &_ble_service.attrs[28];
    _ble_char_table[(uint32_t) BLE_CHAR_PIN_BRIGHTNESS].p_notify_service       = &_ble_service.attrs[31];
    _ble_char_table[(uint32_t) BLE_CHAR_LEVEL_CALIBRATE].p_notify_service      = &_ble_service.attrs[34];
    _ble_char_table[(uint32_t) BLE_CHAR_LEVEL_SENSITIVITY].p_notify_service    = &_ble_service.attrs[37];
    _ble_char_table[(uint32_t) BLE_CHAR_LEVEL_OPTION].p_notify_service         = &_ble_service.attrs[40];
    _ble_char_table[(uint32_t) BLE_CHAR_LEVEL_VISCOSITY].p_notify_service      = &_ble_service.attrs[43];
    _ble_char_table[(uint32_t) BLE_CHAR_LEVEL_ANGLE].p_notify_service          = &_ble_service.attrs[46];
    _ble_char_table[(uint32_t) BLE_CHAR_AMBIENT_LIGHT].p_notify_service        = &_ble_service.attrs[49];
    _ble_char_table[(uint32_t) BLE_CHAR_FSM_TIMEOUT].p_notify_service          = &_ble_service.attrs[52];
    _ble_char_table[(uint32_t) BLE_CHAR_FSM_AUTO_BRIGHTNESS].p_notify_service  = &_ble_service.attrs[55];
    _ble_char_table[(uint32_t) BLE_CHAR_FSM_APP_CONFIG].p_notify_service       = &_ble_service.attrs[58];
    _ble_char_table[(uint32_t) BLE_CHAR_APP_MPCB_VERSION].p_notify_service     = &_ble_service.attrs[61];
    _ble_char_table[(uint32_t) BLE_CHAR_APP_RPCB_VERSION].p_notify_service     = &_ble_service.attrs[64];
    _ble_char_table[(uint32_t) BLE_CHAR_APP_VERSION].p_notify_service          = &_ble_service.attrs[67];
    _ble_char_table[(uint32_t) BLE_CHAR_APP_MFG_ID].p_notify_service           = &_ble_service.attrs[70];
    _ble_char_table[(uint32_t) BLE_CHAR_APP_MFG_DATE].p_notify_service         = &_ble_service.attrs[73];
    _ble_char_table[(uint32_t) BLE_CHAR_APP_PN].p_notify_service               = &_ble_service.attrs[76];
    _ble_char_table[(uint32_t) BLE_CHAR_APP_SN].p_notify_service               = &_ble_service.attrs[79];
    _ble_char_table[(uint32_t) BLE_CHAR_APP_POWER_COUNT].p_notify_service      = &_ble_service.attrs[82];
    _ble_char_table[(uint32_t) BLE_CHAR_APP_CLI_ENABLED].p_notify_service      = &_ble_service.attrs[85];
    /* APP_RESET is write-only, no notify */
    _ble_char_table[(uint32_t) BLE_CHAR_BATTERY_LEVEL].p_notify_service        = &_ble_service.attrs[90];
    _ble_char_table[(uint32_t) BLE_CHAR_FSM_MODE].p_notify_service             = &_ble_service.attrs[93];

    /* _shot_capture_service */
    _ble_char_table[(uint32_t) BLE_CHAR_USER_SHOT_CNT].p_notify_service        = &_shot_capture_service.attrs[8];
    _ble_char_table[(uint32_t) BLE_CHAR_TOTAL_SHOT_CNT].p_notify_service       = &_shot_capture_service.attrs[11];
    _ble_char_table[(uint32_t) BLE_CHAR_SHOT_DATA_REQUEST].p_notify_service    = &_shot_capture_service.attrs[18];
    _ble_char_table[(uint32_t) BLE_CHAR_SHOT_DATA].p_notify_service            = &_shot_capture_service.attrs[21];

	/* Check and enable BLE. */
	if (_ble_config.enabled)
	{
		BLE_enable (true, false);
	}
}

void BLE_adv_name_set (char *p_str)
{
    memset (_ble_config.adv_name, 0, sizeof (_ble_config.adv_name));
    strcpy (_ble_config.adv_name, p_str);
    _ble_config_write ();

    _ble_ad_data[1].data     = _ble_config.adv_name;
    _ble_ad_data[1].data_len = strlen (_ble_config.adv_name);

    k_work_schedule (&_ble_delayed_work, K_MSEC(500));
}

void BLE_enable (bool en, bool save)
{
    int32_t err;

    if (en)
    {
        err = bt_enable (NULL);
        if (err)
        {
            PRINT (ERROR, "Enabling bluetooth failed (err %d)\r\n", err);
            return;
        }

        PRINT (INFO, "Bluetooth enabled\r\n");

        if (IS_ENABLED(CONFIG_SETTINGS)) {
            settings_load ();
        }

        err = bt_le_adv_start (BT_LE_ADV_CONN, _ble_ad_data, ARRAY_SIZE(_ble_ad_data),
                               _ble_scan_data, ARRAY_SIZE(_ble_scan_data));
        if (err)
        {
            PRINT (ERROR, "Advertising failed to start (err %d)\r\n", err);
            return;
        }

        PRINT (INFO, "Advertising successfully started\r\n");
        _ble_enabled = true;
    }
    else
    {
        err = bt_le_adv_stop ();
        if (err)
        {
            PRINT (ERROR, "Advertising failed to stop (err %d)\r\n", err);
            return;
        }

        PRINT (INFO, "Advertising stopped\r\n");

        err = bt_disable ();
        if (err)
        {
            PRINT (ERROR, "Disabling bluetooth failed (err %d)\r\n", err);
            return;
        }

        PRINT (INFO, "Bluetooth disabled\r\n");
        _ble_enabled = false;
    }

    if (save)
    {
        _ble_config.enabled = _ble_enabled;
        _ble_config_write ();
    }
}

bool BLE_is_enabled (void)
{
    return _ble_enabled;
}

bool BLE_is_connected (void)
{
    return _ble_connected;
}

void BLE_notify (BLE_char_e idx, const void *p_data, uint16_t len)
{
    if (!_ble_connected)
    {
        return;
    }
    else if (!_ble_char_table[(uint32_t) idx].notify)
    {
        return;
    }

    int err = bt_gatt_notify (NULL, _ble_char_table[(uint32_t) idx].p_notify_service, p_data, len);
    if (err)
    {
        PRINT (ERROR, "BLE notify failed for char %d (err %d)\r\n", idx, err);
    }
}

void BLE_cli_cmd (int32_t argc, char **argv)
{
    if (argc == 0)
    {
        PRINT (INFO, "BLE\r\n");
        PRINT (INFO, "-------------------------------------------\r\n");
        PRINT (INFO, "Status          : %s\r\n", (_ble_enabled) ? "enabled" : "disabled");
        PRINT (INFO, "Advertising Name: %s\r\n", _ble_config.adv_name);
        PRINT (INFO, "Bonded          : %s\r\n", (_ble_settings.bonded) ? "yes" : "no");
		if (_ble_settings.bonded)
		{
			char addr[BT_ADDR_LE_STR_LEN];

			bt_addr_le_to_str (&_ble_settings.peer_addr, addr, sizeof (addr));

			PRINT (INFO, "  Peer Addr : %s\r\n", addr);
		}
    }
    else if (argc == 1)
    {
        if (!strcmp (argv[0], "--name"))
        {
            PRINT (INFO, "%s\r\n", _ble_config.adv_name);
        }
		else if (!strcmp (argv[0], "--bondclr"))
		{
            _ble_bond_clear ();
		}
        else
        {
            goto USAGE;
        }
    }
    else if (argc == 2)
    {
        if (!strcmp (argv[0], "-e") || !strcmp (argv[0], "--enable"))
        {
            if (!strcmp (argv[1], "true"))
            {
                BLE_enable (true, false);
            }
            else if (!strcmp (argv[1], "false"))
            {
                BLE_enable (false, false);
            }
            else
            {
                goto USAGE;
            }
        }
        else if (!strcmp (argv[0], "--name"))
        {
            BLE_adv_name_set (argv[1]);
        }
        else
        {
            goto USAGE;
        }
    }
    else
    {
USAGE:
        PRINT (INFO, "Usage: ble [OPTION...]\r\n");
        PRINT (INFO, "  -e | --enable        Enables BLE\r\n");
        PRINT (INFO, "    <%%b>               true  = enable\r\n");
        PRINT (INFO, "                       false = disable\r\n");
        PRINT (INFO, "  --name               Gets/sets BLE advertising name\r\n");
        PRINT (INFO, "    <%%s>               name\r\n");
        PRINT (INFO, "  --bondclr         	 Clears stored bond and bond keys\r\n");
    }
}


/*--------------------------------------------------------------------------------------------------
 * PRIVATE FUNCTION DEFINITIONS — BLE core
 *------------------------------------------------------------------------------------------------*/
static void _ble_connected_cb (struct bt_conn *conn, uint8_t err)
{
    if (err)
    {
        PRINT (INFO, "Connection failed (err %u)\n", err);
        return;
    }

    _ble_connected    = true;
    _ble_current_conn = bt_conn_ref (conn);

    PRINT (INFO, "Connected\n");

    /* Always request MTU exchange immediately on connect — Android won't
     * re-initiate it on bonded reconnects, causing transfers to run at
     * 20 bytes/chunk instead of 514. */
    _ble_mtu_exchange_params.func = _ble_mtu_exchange_cb;
    int mtu_err = bt_gatt_exchange_mtu (conn, &_ble_mtu_exchange_params);
    if (mtu_err)
    {
        PRINT (ERROR, "BLE: MTU exchange request failed (err %d)\r\n", mtu_err);
    }

    if (_ble_settings.bonded)
    {
        PRINT (INFO, "BLE: bonded, requesting security\n");
        int sec_err = bt_conn_set_security (conn, BT_SECURITY_L2);
        if (sec_err)
        {
            PRINT (ERROR, "BLE: bt_conn_set_security failed (err %d)\n", sec_err);
            bt_conn_disconnect (conn, BT_HCI_ERR_AUTH_FAIL);
        }
    }
    else
    {
        PRINT (INFO, "BLE: unbonded, scheduling pairing in 1000ms\n");
        k_work_schedule (&_ble_pair_delay_work, K_MSEC(1000));
    }
}

// static void _ble_connected_cb (struct bt_conn *conn, uint8_t err)
// {
//     if (err)
//     {
//         PRINT (INFO, "Connection failed (err %u)\n", err);
//         return;
//     }

//     _ble_connected    = true;
//     _ble_current_conn = bt_conn_ref (conn);

//     PRINT (INFO, "Connected\n");

//     if (_ble_settings.bonded)
//     {
//         PRINT (INFO, "BLE: bonded, requesting security\n");
//         int sec_err = bt_conn_set_security (conn, BT_SECURITY_L2);
//         if (sec_err)
//         {
//             PRINT (ERROR, "BLE: bt_conn_set_security failed (err %d)\n", sec_err);
//             bt_conn_disconnect (conn, BT_HCI_ERR_AUTH_FAIL);
//         }
//     }
//     else
//     {
//         /* Delay pairing request to let Android finish MTU exchange and
//          * service discovery first. 1000ms is enough for Android 16. */
//         PRINT (INFO, "BLE: unbonded, scheduling pairing in 1000ms\n");
//         k_work_schedule (&_ble_pair_delay_work, K_MSEC(1000));
//     }
// }

static void _ble_disconnected_cb (struct bt_conn *conn, uint8_t reason)
{
    _ble_connected = false;

    if (_ble_current_conn)
    {
        bt_conn_unref (_ble_current_conn);
        _ble_current_conn = NULL;
    }

    k_work_cancel_delayable (&_ble_pair_delay_work);

    _ble_pair_conn  = NULL;

    /* Reset shot data transfer state. */
    _ble_shot_data_transferring = false;
    k_sem_reset(&read_shotdata_consumed);

    PRINT (INFO, "Disconnected (reason %u)\n", reason);

    /* Restart advertising so the phone can reconnect. */
    int err = bt_le_adv_start (BT_LE_ADV_CONN, _ble_ad_data, ARRAY_SIZE(_ble_ad_data),
                               _ble_scan_data, ARRAY_SIZE(_ble_scan_data));
    if (err)
    {
        PRINT (ERROR, "Advertising failed to restart (err %d)\r\n", err);
    }
    else
    {
        PRINT (INFO, "Advertising restarted\r\n");
    }
}

static void _ble_security_changed_cb (struct bt_conn *conn, bt_security_t level,
                                      enum bt_security_err err)
{
    char addr[BT_ADDR_LE_STR_LEN];
    bt_addr_le_to_str (bt_conn_get_dst (conn), addr, sizeof (addr));

    if (!err)
    {
        PRINT (INFO, "Security changed: %s level %u\n", addr, level);
    }
    else
    {
        PRINT (INFO, "Security failed: %s level %u err %d\n", addr, level, err);

        if (_ble_settings.bonded &&
            (err == BT_SECURITY_ERR_AUTH_FAIL ||
             err == BT_SECURITY_ERR_PIN_OR_KEY_MISSING))
        {
            PRINT (INFO, "BLE: stale bond detected, clearing\n");
            _ble_bond_clear ();
        }
    }
}

static void _ble_auth_cancel_cb (struct bt_conn *conn)
{
    char addr[BT_ADDR_LE_STR_LEN];

    bt_addr_le_to_str (bt_conn_get_dst (conn), addr, sizeof (addr));

    PRINT (INFO, "Pairing cancelled: %s\n", addr);
}

static void _ble_pairing_complete_cb(struct bt_conn *conn, bool bonded)
{
    char addr[BT_ADDR_LE_STR_LEN];
    
    bt_addr_le_to_str (bt_conn_get_dst(conn), addr, sizeof(addr));
    PRINT (INFO, "Pairing completed: %s, bonded: %d\n", addr, bonded);

    if (bonded)
    {
        _ble_settings.bonded    = true;
        _ble_settings.peer_addr = *bt_conn_get_dst(conn);
        k_work_schedule (&_ble_settings_write_dwork, K_MSEC(2000));
        PRINT (INFO, "BLE: bond saved for %s\n", addr);
    }
}

static enum bt_security_err _ble_pairing_accept_cb ( struct bt_conn *conn, const struct bt_conn_pairing_feat *const feat)
{
    PRINT (INFO, "BLE: pairing accept\n");
    return BT_SECURITY_ERR_SUCCESS;
}

static void _ble_pairing_failed_cb (struct bt_conn *conn, enum bt_security_err reason)
{
    char    addr[BT_ADDR_LE_STR_LEN];
	uint8_t state;
    
	bt_addr_le_to_str (bt_conn_get_dst (conn), addr, sizeof (addr));

    PRINT (INFO, "Pairing failed conn: %s, reason %d\n", addr, reason);

    if (reason == BT_SECURITY_ERR_PIN_OR_KEY_MISSING)
    {
        _ble_bond_clear ();
    }
}

static void _ble_att_mtu_updated_cb (struct bt_conn *conn, uint16_t tx, uint16_t rx)
{
    PRINT (INFO, "ATT MTU updated: TX %u, RX %u (usable payload: %u bytes)\n", tx, rx, tx - 1);
}

static void _ble_pair_delay_handler (struct k_work *work)
{
    if (!_ble_current_conn)
	{
		return;
	}

    PRINT (INFO, "BLE: initiating pairing after delay\n");
    int err = bt_conn_set_security (_ble_current_conn, BT_SECURITY_L2);
    if (err)
    {
        PRINT (ERROR, "BLE: bt_conn_set_security failed (err %d)\n", err);
        bt_conn_disconnect (_ble_current_conn, BT_HCI_ERR_AUTH_FAIL);
    }
}

static void _ble_mtu_exchange_cb (struct bt_conn *conn, uint8_t err, struct bt_gatt_exchange_params *params)
{
    if (err)
    {
        PRINT (ERROR, "BLE: MTU exchange failed (err %d)\n", err);
    }
    else
    {
        PRINT (INFO, "BLE: MTU exchange complete, MTU = %u\n", bt_gatt_get_mtu (conn));
    }
}

static void _ble_settings_read (void)
{
    ERRNO_e        err;
    BLE_settings_s settings;

    /* Increasing file depth since BLE needs to write at least once on every power up. */
    err = FS_open_or_create (BLE_SETTINGS_FILENAME, &_ble_settings_file, sizeof (BLE_settings_s), (FS_FILE_DEPTH_DEFAULT * 8));
    if (err == ERRNO_SUCCESS)
    {
		_ble_fs_ready = true;

        err = FS_read (_ble_settings_file, (uint8_t *) &settings, 0, sizeof (BLE_settings_s));
        if (err != ERRNO_SUCCESS)
        {
            _ble_settings_write ();
            return;
        }
        _ble_settings = settings;
    }
    else if (err == ERRNO_SIZE_MISMATCH)
    {
        /* Config struct changed size — wipe and recreate. */
        err = FS_delete (BLE_SETTINGS_FILENAME);
        if (err != ERRNO_SUCCESS)
        {
            PRINT (ERROR, "Failure to delete old ble settings file.\r\n");
            return;
        }
        err = FS_open_or_create (BLE_SETTINGS_FILENAME, &_ble_settings_file, sizeof (BLE_settings_s), (FS_FILE_DEPTH_DEFAULT * 8));
        if (err != ERRNO_SUCCESS)
        {
            PRINT (ERROR, "Failure to create ble settings file.\r\n");
            return;
        }

        PRINT (INFO, "New ble settings file created successfully\r\n");

        _ble_settings_write ();
    }
    else
    {
        _ble_settings_write ();
    }
}

static void _ble_settings_write (void)
{
    if (_ble_settings_file == NULL)
    {
        PRINT (ERROR, "BLE settings file not open, skipping write.\r\n");
        return;
    }
    if (FS_write (_ble_settings_file, (uint8_t *) &_ble_settings) != ERRNO_SUCCESS)
    {
        PRINT (ERROR, "Failed to write ble settings to file.\r\n");
    }
}

static void _ble_config_read (void)
{
    ERRNO_e      err;
    BLE_config_s cfg;

    err = FS_open_or_create (BLE_FILENAME, &_ble_config_file, sizeof (BLE_config_s), FS_FILE_DEPTH_DEFAULT);
    if (err == ERRNO_SUCCESS)
    {
        err = FS_read (_ble_config_file, (uint8_t *) &cfg, 0, sizeof (BLE_config_s));
        if (err != ERRNO_SUCCESS)
        {
            _ble_config_write ();
            return;
        }
        _ble_config = cfg;
    }
    else if (err == ERRNO_SIZE_MISMATCH)
    {
        /* Config struct changed size — wipe and recreate. */
        err = FS_delete (BLE_FILENAME);
        if (err != ERRNO_SUCCESS)
        {
            PRINT (ERROR, "Failure to delete old ble file.\r\n");
            return;
        }
        err = FS_open_or_create (BLE_FILENAME, &_ble_config_file, sizeof (BLE_config_s), FS_FILE_DEPTH_DEFAULT);
        if (err != ERRNO_SUCCESS)
        {
            PRINT (ERROR, "Failure to create ble configuration file.\r\n");
            return;
        }

        PRINT (INFO, "New ble file created successfully\r\n");
        _ble_config_write ();
    }
    else
    {
        _ble_config_write ();
    }
}

static void _ble_config_write (void)
{
    if (_ble_config_file == NULL)
    {
        PRINT (ERROR, "BLE config file not open, skipping write.\r\n");
        return;
    }
    if (FS_write (_ble_config_file, (uint8_t *) &_ble_config) != ERRNO_SUCCESS)
    {
        PRINT (ERROR, "Failed to write ble config to file.\r\n");
    }
}

static void _ble_reenable_later (struct k_work *work)
{
    BLE_enable (false, false);
    BLE_enable (true, false);
}


/*--------------------------------------------------------------------------------------------------
 * PRIVATE FUNCTION DEFINITIONS — Settings backend
 *
 * Zephyr's BT stack calls settings_save_one() for each bond sub-key when a
 * bond is formed.  settings_load() is called at BLE_enable() time to restore
 * keys.  We store all entries in the BLE_config_s.settings[] array and flush
 * the whole struct to external flash via _ble_settings_write().
 *------------------------------------------------------------------------------------------------*/

/* Find a slot whose key matches.  Returns index or -1. */
static int _settings_entry_find (const char *key)
{
    for (int i = 0; i < BLE_PAIRING_MAX_ENTRIES; i++)
    {
        if (_ble_settings.settings[i].used &&
            strncmp(_ble_settings.settings[i].key, key, BLE_PAIRING_MAX_KEY_LEN) == 0)
        {
            return i;
        }
    }
    return -1;
}

/* Allocate a new slot, pre-filling the key.  Returns index or -1 if full. */
static int _settings_entry_alloc (const char *key)
{
    for (int i = 0; i < BLE_PAIRING_MAX_ENTRIES; i++)
    {
        if (!_ble_settings.settings[i].used)
        {
            strncpy(_ble_settings.settings[i].key, key, BLE_PAIRING_MAX_KEY_LEN - 1);
            _ble_settings.settings[i].key[BLE_PAIRING_MAX_KEY_LEN - 1] = '\0';
            _ble_settings.settings[i].used = true;
            return i;
        }
    }
    return -1;
}

static ssize_t _settings_read_cb (void *cb_arg, void *data, size_t len)
{
    const BLE_settings_entry_s *e = (const BLE_settings_entry_s *) cb_arg;
    size_t copy_len = (len < e->val_len) ? len : e->val_len;

    memcpy (data, e->val, copy_len);

    return (ssize_t) copy_len;
}

/**
 * @brief  Called by settings_load().  Replay all stored entries back to Zephyr.
 */
static int _settings_load (struct settings_store *cs, const struct settings_load_arg *arg)
{
    PRINT (INFO, "BLE settings: load called, _ble_settings_file=%p\r\n", (void*)_ble_settings_file);

    if (!_ble_fs_ready)
    {
        PRINT (WARNING, "BLE settings: FS not ready, skipping load\r\n");
        return 0;
    }

    /* Prevent _ble_settings_write() during load */
	atomic_set(&_ble_settings_loading, 1);

    for (int i = 0; i < BLE_PAIRING_MAX_ENTRIES; i++)
    {
        BLE_settings_entry_s *e = &_ble_settings.settings[i];
        if (!e->used) continue;

		PRINT (INFO, "BLE settings: replaying '%s' (%u bytes)\r\n", e->key, e->val_len);  // ADD

        const char *name = e->key;
        if (arg->subtree)
        {
            size_t sub_len = strlen(arg->subtree);
            if (strncmp(name, arg->subtree, sub_len) != 0) continue;
            name += sub_len;
            if (*name == '/') name++;
        }

        int err = settings_call_set_handler (name, e->val_len, _settings_read_cb, e, arg);
        if (err)
        {
            PRINT (WARNING, "BLE settings: set_handler err %d for '%s'\r\n", err, e->key);
        }
    }

    /* Restore */
	atomic_set (&_ble_settings_loading, 0);

    return 0;
}

/**
 * @brief  Called by settings_save_one() / settings_delete().
 *         Upserts or removes one entry then flushes to FS.
 */
static int _settings_save (struct settings_store *cs, const char *name,
                           const void *value, size_t val_len)
{
    PRINT (INFO, "BLE settings: saving '%s' (%u bytes)\r\n", name, (unsigned)val_len);

    /* bt/hash is recomputed every boot — no need to persist it. */
    if (strcmp (name, "bt/hash") == 0)
    {
        return 0;
    }

    /* Skip 0-byte deletes of keys we don't have — nothing to delete. */
    if (value == NULL || val_len == 0)
{
    /* During load, the BT stack issues 0-byte saves as housekeeping —
     * never delete a key we have stored, as the real data is about to
     * be (or just was) replayed. */
    if (atomic_get (&_ble_settings_loading))
	{
		return 0;
	}

    /* Outside of load, only delete if explicitly present. */
    int idx = _settings_entry_find (name);
    if (idx >= 0)
    {
        memset (&_ble_settings.settings[idx], 0, sizeof (BLE_settings_entry_s));
        if (_ble_fs_ready)
        {
            k_work_schedule (&_ble_settings_write_dwork, K_MSEC(2000));
        }
    }
    return 0;
}

    int idx = _settings_entry_find (name);
    if (idx < 0)
    {
        idx = _settings_entry_alloc (name);
    }
    if (idx < 0)
    {
        PRINT (ERROR, "BLE settings: table full, cannot save '%s'\r\n", name);
        return -ENOMEM;
    }

    size_t clamped = (val_len > BLE_PAIRING_MAX_VAL_LEN) ? BLE_PAIRING_MAX_VAL_LEN : val_len;
    memcpy (_ble_settings.settings[idx].val, value, clamped);
    _ble_settings.settings[idx].val_len = (uint8_t) clamped;

    if (_ble_fs_ready && !atomic_get(&_ble_settings_loading))
    {
        k_work_schedule (&_ble_settings_write_dwork, K_MSEC(2000));
    }

    return 0;
}

/* Required entry point when CONFIG_SETTINGS_CUSTOM=y.
   Zephyr has the prototype. */
int settings_backend_init (void)
{
	PRINT (INFO, "BLE: settings_backend_init called\r\n");
    _settings_itf.csi_load = _settings_load;
    _settings_itf.csi_save = _settings_save;
    _settings_store.cs_itf = &_settings_itf;
	
    settings_dst_register (&_settings_store);
    settings_src_register (&_settings_store);

    return 0;
}


/*--------------------------------------------------------------------------------------------------
 * PRIVATE FUNCTION DEFINITIONS — Pairing challenge
 *------------------------------------------------------------------------------------------------*/
static void _ble_pair_smp_handler (struct k_work *work)
{
    if (_ble_pair_conn == NULL) return;

    int err = bt_conn_set_security (_ble_pair_conn, BT_SECURITY_L2);
    if (err)
    {
        PRINT (ERROR, "BLE pair: bt_conn_set_security failed (err %d)\r\n", err);
        bt_conn_disconnect (_ble_pair_conn, BT_HCI_ERR_AUTH_FAIL);
    }
}

static void _ble_bond_clear (void)
{
    if (_ble_settings.bonded)
    {
        bt_unpair (BT_ID_DEFAULT, &_ble_settings.peer_addr);
    }

    _ble_settings.bonded = false;

    memset (&_ble_settings.peer_addr, 0, sizeof(_ble_settings.peer_addr));
    memset (_ble_settings.settings,  0, sizeof(_ble_settings.settings));

    _ble_settings_write ();

	PRINT (INFO, "BLE bond cleared.\r\n");
}


/*--------------------------------------------------------------------------------------------------
 * PRIVATE FUNCTION DEFINITIONS — Advertising name characteristic
 *------------------------------------------------------------------------------------------------*/
static ssize_t _ble_adv_name_write (struct bt_conn *conn, const struct bt_gatt_attr *attr,
                                    const void *buf, uint16_t len, uint16_t offset, uint8_t flags)
{
    BLE_adv_name_set ((char *) buf);
    return len;
}

static ssize_t _ble_adv_name_read (struct bt_conn *conn, const struct bt_gatt_attr *attr,
                                   void *buf, uint16_t len, uint16_t offset)
{
    return bt_gatt_attr_read (conn, attr, buf, len, offset,
                              _ble_config.adv_name, strlen (_ble_config.adv_name));
}

static ssize_t _ble_bond_clear_write (struct bt_conn *conn, const struct bt_gatt_attr *attr,
                                      const void *buf, uint16_t len, uint16_t offset, uint8_t flags)
{
    k_work_schedule (&_ble_bond_clear_dwork, K_MSEC(1000));

    return len;
}


/*--------------------------------------------------------------------------------------------------
 * PRIVATE FUNCTION DEFINITIONS — APP characteristics
 *------------------------------------------------------------------------------------------------*/

static ssize_t _ble_app_main_pcb_version_read (struct bt_conn *conn, const struct bt_gatt_attr *attr,
                                               void *buf, uint16_t len, uint16_t offset)
{
    uint8_t version[3];
    version[0] = APP_info_get ().main_pcb_version.major;
    version[1] = APP_info_get ().main_pcb_version.minor;
    version[2] = APP_info_get ().main_pcb_version.build;
    return bt_gatt_attr_read (conn, attr, buf, len, offset, version, 3);
}

static ssize_t _ble_app_ring_pcb_version_read (struct bt_conn *conn, const struct bt_gatt_attr *attr,
                                               void *buf, uint16_t len, uint16_t offset)
{
    uint8_t version[3];
    version[0] = APP_info_get ().ring_pcb_version.major;
    version[1] = APP_info_get ().ring_pcb_version.minor;
    version[2] = APP_info_get ().ring_pcb_version.build;
    return bt_gatt_attr_read (conn, attr, buf, len, offset, version, 3);
}

static ssize_t _ble_app_version_read (struct bt_conn *conn, const struct bt_gatt_attr *attr,
                                      void *buf, uint16_t len, uint16_t offset)
{
    uint8_t version[3];
    version[0] = APP_info_get ().version.major;
    version[1] = APP_info_get ().version.minor;
    version[2] = APP_info_get ().version.build;
    return bt_gatt_attr_read (conn, attr, buf, len, offset, version, 3);
}

static ssize_t _ble_app_mfg_id_read (struct bt_conn *conn, const struct bt_gatt_attr *attr,
                                     void *buf, uint16_t len, uint16_t offset)
{
    uint32_t mfg_id = APP_info_get ().mfg_id;
    return bt_gatt_attr_read (conn, attr, buf, len, offset, &mfg_id, sizeof (mfg_id));
}

static ssize_t _ble_app_mfg_date_read (struct bt_conn *conn, const struct bt_gatt_attr *attr,
                                       void *buf, uint16_t len, uint16_t offset)
{
    uint32_t mfg_date = APP_info_get ().mfg_date;
    return bt_gatt_attr_read (conn, attr, buf, len, offset, &mfg_date, sizeof (mfg_date));
}

static ssize_t _ble_app_pn_read (struct bt_conn *conn, const struct bt_gatt_attr *attr,
                                 void *buf, uint16_t len, uint16_t offset)
{
    APP_info_s info = APP_info_get ();
    return bt_gatt_attr_read (conn, attr, buf, len, offset, info.pn, strlen(info.pn));
}

static ssize_t _ble_app_sn_read (struct bt_conn *conn, const struct bt_gatt_attr *attr,
                                 void *buf, uint16_t len, uint16_t offset)
{
    APP_info_s info = APP_info_get ();
    return bt_gatt_attr_read (conn, attr, buf, len, offset, info.sn, strlen(info.sn));
}

static ssize_t _ble_app_power_count_read (struct bt_conn *conn, const struct bt_gatt_attr *attr,
                                          void *buf, uint16_t len, uint16_t offset)
{
    uint32_t power_count = APP_info_get ().power_cnt;
    return bt_gatt_attr_read (conn, attr, buf, len, offset, &power_count, sizeof (power_count));
}

static ssize_t _ble_app_cli_enabled_write (struct bt_conn *conn, const struct bt_gatt_attr *attr,
                                           const void *buf, uint16_t len, uint16_t offset,
                                           uint8_t flags)
{
    uint8_t en = *((uint8_t *) buf);
    APP_cli_enable (en);
    return len;
}

static ssize_t _ble_app_cli_enabled_read (struct bt_conn *conn, const struct bt_gatt_attr *attr,
                                          void *buf, uint16_t len, uint16_t offset)
{
    uint8_t cli_enabled = APP_info_get ().cli_enabled;
    return bt_gatt_attr_read (conn, attr, buf, len, offset, &cli_enabled, sizeof (cli_enabled));
}

static ssize_t _ble_app_reset_write (struct bt_conn *conn, const struct bt_gatt_attr *attr,
                                     const void *buf, uint16_t len, uint16_t offset, uint8_t flags)
{
    APP_reset ();
    return len;
}


/*--------------------------------------------------------------------------------------------------
 * PRIVATE FUNCTION DEFINITIONS — SCOPE RING characteristics
 *------------------------------------------------------------------------------------------------*/

static ssize_t _ble_level_option_write (struct bt_conn *conn, const struct bt_gatt_attr *attr,
                                        const void *buf, uint16_t len, uint16_t offset, uint8_t flags)
{
    SCOPE_RING_level_option_set ((SCOPE_RING_level_option_e) *((uint8_t *) buf));
    return len;
}

static ssize_t _ble_level_option_read (struct bt_conn *conn, const struct bt_gatt_attr *attr,
                                       void *buf, uint16_t len, uint16_t offset)
{
    uint8_t option = SCOPE_RING_config_get ().level_option;
    return bt_gatt_attr_read (conn, attr, buf, len, offset, &option, sizeof(option));
}

static void _ble_level_option_changed (const struct bt_gatt_attr *attr, uint16_t value)
{
    _ble_char_table[(uint32_t) BLE_CHAR_LEVEL_OPTION].notify = (value == BT_GATT_CCC_NOTIFY);
}

static ssize_t _ble_level_viscosity_write (struct bt_conn *conn, const struct bt_gatt_attr *attr,
                                           const void *buf, uint16_t len, uint16_t offset, uint8_t flags)
{
    SCOPE_RING_level_viscosity_set (*((float *) buf));
    return len;
}

static ssize_t _ble_level_viscosity_read (struct bt_conn *conn, const struct bt_gatt_attr *attr,
                                          void *buf, uint16_t len, uint16_t offset)
{
    float viscosity = SCOPE_RING_config_get ().level_viscosity;
    return bt_gatt_attr_read (conn, attr, buf, len, offset, &viscosity, sizeof(viscosity));
}

static void _ble_level_viscosity_changed (const struct bt_gatt_attr *attr, uint16_t value)
{
    _ble_char_table[(uint32_t) BLE_CHAR_LEVEL_VISCOSITY].notify = (value == BT_GATT_CCC_NOTIFY);
}

static ssize_t _ble_level_sensitivity_write (struct bt_conn *conn, const struct bt_gatt_attr *attr,
                                             const void *buf, uint16_t len, uint16_t offset, uint8_t flags)
{
    SCOPE_RING_level_sensitivity_set (*((float *) buf));
    return len;
}

static ssize_t _ble_level_sensitivity_read (struct bt_conn *conn, const struct bt_gatt_attr *attr,
                                            void *buf, uint16_t len, uint16_t offset)
{
    float sensitivity = SCOPE_RING_config_get ().level_sensitivity;
    return bt_gatt_attr_read (conn, attr, buf, len, offset, &sensitivity, sizeof(sensitivity));
}

static void _ble_level_sensitivity_changed (const struct bt_gatt_attr *attr, uint16_t value)
{
    _ble_char_table[(uint32_t) BLE_CHAR_LEVEL_SENSITIVITY].notify = (value == BT_GATT_CCC_NOTIFY);
}

static ssize_t _ble_ring_color_write (struct bt_conn *conn, const struct bt_gatt_attr *attr,
                                      const void *buf, uint16_t len, uint16_t offset, uint8_t flags)
{
    SCOPE_RING_color_set (SCOPE_RING_LED_BACKGROUND, (RGB_color_e) *((uint8_t *) buf));
    return len;
}

static ssize_t _ble_ring_color_read (struct bt_conn *conn, const struct bt_gatt_attr *attr,
                                     void *buf, uint16_t len, uint16_t offset)
{
    uint8_t val = SCOPE_RING_config_get ().background_color;
    return bt_gatt_attr_read (conn, attr, buf, len, offset, &val, sizeof(val));
}

static void _ble_ring_color_changed (const struct bt_gatt_attr *attr, uint16_t value)
{
    _ble_char_table[(uint32_t) BLE_CHAR_RING_COLOR].notify = (value == BT_GATT_CCC_NOTIFY);
}

static ssize_t _ble_ring_brightness_write (struct bt_conn *conn, const struct bt_gatt_attr *attr,
                                           const void *buf, uint16_t len, uint16_t offset, uint8_t flags)
{
    SCOPE_RING_brightness_set (SCOPE_RING_LED_BACKGROUND, *((uint8_t *) buf), true);
    return len;
}

static ssize_t _ble_ring_brightness_read (struct bt_conn *conn, const struct bt_gatt_attr *attr,
                                          void *buf, uint16_t len, uint16_t offset)
{
    uint8_t val = SCOPE_RING_config_get ().background_brightness;
    return bt_gatt_attr_read (conn, attr, buf, len, offset, &val, sizeof(val));
}

static void _ble_ring_brightness_changed (const struct bt_gatt_attr *attr, uint16_t value)
{
    _ble_char_table[(uint32_t) BLE_CHAR_RING_BRIGHTNESS].notify = (value == BT_GATT_CCC_NOTIFY);
}

static ssize_t _ble_indicator_color_write (struct bt_conn *conn, const struct bt_gatt_attr *attr,
                                           const void *buf, uint16_t len, uint16_t offset, uint8_t flags)
{
    SCOPE_RING_color_set (SCOPE_RING_LED_INDICATORS, (RGB_color_e) *((uint8_t *) buf));
    return len;
}

static ssize_t _ble_indicator_color_read (struct bt_conn *conn, const struct bt_gatt_attr *attr,
                                          void *buf, uint16_t len, uint16_t offset)
{
    uint8_t val = SCOPE_RING_config_get ().indicators_color;
    return bt_gatt_attr_read (conn, attr, buf, len, offset, &val, sizeof(val));
}

static void _ble_indicator_color_changed (const struct bt_gatt_attr *attr, uint16_t value)
{
    _ble_char_table[(uint32_t) BLE_CHAR_INDICATOR_COLOR].notify = (value == BT_GATT_CCC_NOTIFY);
}

static ssize_t _ble_indicator_brightness_write (struct bt_conn *conn, const struct bt_gatt_attr *attr,
                                                const void *buf, uint16_t len, uint16_t offset, uint8_t flags)
{
    SCOPE_RING_brightness_set (SCOPE_RING_LED_INDICATORS, *((uint8_t *) buf), true);
    return len;
}

static ssize_t _ble_indicator_brightness_read (struct bt_conn *conn, const struct bt_gatt_attr *attr,
                                               void *buf, uint16_t len, uint16_t offset)
{
    uint8_t val = SCOPE_RING_config_get ().indicators_brightness;
    return bt_gatt_attr_read (conn, attr, buf, len, offset, &val, sizeof(val));
}

static void _ble_indicator_brightness_changed (const struct bt_gatt_attr *attr, uint16_t value)
{
    _ble_char_table[(uint32_t) BLE_CHAR_INDICATOR_BRIGHTNESS].notify = (value == BT_GATT_CCC_NOTIFY);
}

static ssize_t _ble_level_color_write (struct bt_conn *conn, const struct bt_gatt_attr *attr,
                                       const void *buf, uint16_t len, uint16_t offset, uint8_t flags)
{
    SCOPE_RING_color_set (SCOPE_RING_LED_LEVEL, (RGB_color_e) *((uint8_t *) buf));
    return len;
}

static ssize_t _ble_level_color_read (struct bt_conn *conn, const struct bt_gatt_attr *attr,
                                      void *buf, uint16_t len, uint16_t offset)
{
    uint8_t val = SCOPE_RING_config_get ().level_color;
    return bt_gatt_attr_read (conn, attr, buf, len, offset, &val, sizeof(val));
}

static void _ble_level_color_changed (const struct bt_gatt_attr *attr, uint16_t value)
{
    _ble_char_table[(uint32_t) BLE_CHAR_LEVEL_COLOR].notify = (value == BT_GATT_CCC_NOTIFY);
}

static ssize_t _ble_level_brightness_write (struct bt_conn *conn, const struct bt_gatt_attr *attr,
                                            const void *buf, uint16_t len, uint16_t offset, uint8_t flags)
{
    SCOPE_RING_brightness_set (SCOPE_RING_LED_LEVEL, *((uint8_t *) buf), true);
    return len;
}

static ssize_t _ble_level_brightness_read (struct bt_conn *conn, const struct bt_gatt_attr *attr,
                                           void *buf, uint16_t len, uint16_t offset)
{
    uint8_t val = SCOPE_RING_config_get ().level_brightness;
    return bt_gatt_attr_read (conn, attr, buf, len, offset, &val, sizeof(val));
}

static void _ble_level_brightness_changed (const struct bt_gatt_attr *attr, uint16_t value)
{
    _ble_char_table[(uint32_t) BLE_CHAR_LEVEL_BRIGHTNESS].notify = (value == BT_GATT_CCC_NOTIFY);
}


/*--------------------------------------------------------------------------------------------------
 * PRIVATE FUNCTION DEFINITIONS — BUBBLE / FIBER / PIN
 *------------------------------------------------------------------------------------------------*/

static ssize_t _ble_bubble_brightness_write (struct bt_conn *conn, const struct bt_gatt_attr *attr,
                                             const void *buf, uint16_t len, uint16_t offset, uint8_t flags)
{
    BUBBLE_brightness_set (*((uint8_t *) buf), true);
    return len;
}

static ssize_t _ble_bubble_brightness_read (struct bt_conn *conn, const struct bt_gatt_attr *attr,
                                            void *buf, uint16_t len, uint16_t offset)
{
    uint8_t val = BUBBLE_brightness_get ();
    return bt_gatt_attr_read (conn, attr, buf, len, offset, &val, sizeof(val));
}

static void _ble_bubble_brightness_changed (const struct bt_gatt_attr *attr, uint16_t value)
{
    _ble_char_table[(uint32_t) BLE_CHAR_BUBBLE_BRIGHTNESS].notify = (value == BT_GATT_CCC_NOTIFY);
}

static ssize_t _ble_fiber_brightness_write (struct bt_conn *conn, const struct bt_gatt_attr *attr,
                                            const void *buf, uint16_t len, uint16_t offset, uint8_t flags)
{
    FIBER_brightness_set (*((uint8_t *) buf), true);
    return len;
}

static ssize_t _ble_fiber_brightness_read (struct bt_conn *conn, const struct bt_gatt_attr *attr,
                                           void *buf, uint16_t len, uint16_t offset)
{
    uint8_t val = FIBER_brightness_get ();
    return bt_gatt_attr_read (conn, attr, buf, len, offset, &val, sizeof(val));
}

static void _ble_fiber_brightness_changed (const struct bt_gatt_attr *attr, uint16_t value)
{
    _ble_char_table[(uint32_t) BLE_CHAR_FIBER_BRIGHTNESS].notify = (value == BT_GATT_CCC_NOTIFY);
}

static ssize_t _ble_pin_brightness_write (struct bt_conn *conn, const struct bt_gatt_attr *attr,
                                          const void *buf, uint16_t len, uint16_t offset, uint8_t flags)
{
    PIN_brightness_set (*((uint8_t *) buf), true);
    return len;
}

static ssize_t _ble_pin_brightness_read (struct bt_conn *conn, const struct bt_gatt_attr *attr,
                                         void *buf, uint16_t len, uint16_t offset)
{
    uint8_t val = PIN_brightness_get ();
    return bt_gatt_attr_read (conn, attr, buf, len, offset, &val, sizeof(val));
}

static void _ble_pin_brightness_changed (const struct bt_gatt_attr *attr, uint16_t value)
{
    _ble_char_table[(uint32_t) BLE_CHAR_PIN_BRIGHTNESS].notify = (value == BT_GATT_CCC_NOTIFY);
}


/*--------------------------------------------------------------------------------------------------
 * PRIVATE FUNCTION DEFINITIONS — MOTION
 *------------------------------------------------------------------------------------------------*/

static ssize_t _ble_level_calibrate_write (struct bt_conn *conn, const struct bt_gatt_attr *attr,
                                           const void *buf, uint16_t len, uint16_t offset, uint8_t flags)
{
    FSM_level_calibrate ();
    return len;
}

static ssize_t _ble_level_calibrate_read (struct bt_conn *conn, const struct bt_gatt_attr *attr,
                                          void *buf, uint16_t len, uint16_t offset)
{
    const char *value = attr->user_data;
    return bt_gatt_attr_read (conn, attr, buf, len, offset, value, sizeof(*value));
}

static void _ble_level_calibrate_changed (const struct bt_gatt_attr *attr, uint16_t value)
{
    _ble_char_table[(uint32_t) BLE_CHAR_LEVEL_CALIBRATE].notify = (value == BT_GATT_CCC_NOTIFY);
}

static ssize_t _ble_level_angle_read (struct bt_conn *conn, const struct bt_gatt_attr *attr,
                                      void *buf, uint16_t len, uint16_t offset)
{
    float angle = MOTION_angle_get (MOTION_AXIS_Y);
    return bt_gatt_attr_read (conn, attr, buf, len, offset, &angle, sizeof(angle));
}

static void _ble_level_angle_changed (const struct bt_gatt_attr *attr, uint16_t value)
{
    _ble_char_table[(uint32_t) BLE_CHAR_LEVEL_ANGLE].notify = (value == BT_GATT_CCC_NOTIFY);
}


/*--------------------------------------------------------------------------------------------------
 * PRIVATE FUNCTION DEFINITIONS — ALS
 *------------------------------------------------------------------------------------------------*/

static ssize_t _ble_ambient_light_read (struct bt_conn *conn, const struct bt_gatt_attr *attr,
                                        void *buf, uint16_t len, uint16_t offset)
{
    ALS_data_s data;
    uint32_t   val = 0;

    if (ALS_read (&data) != ERRNO_SUCCESS)
    {
        PRINT (ERROR, "Failure to read ALS.\n");
    }
    else
    {
        val = data.visible;
    }

    return bt_gatt_attr_read (conn, attr, buf, len, offset, &val, sizeof(val));
}

static void _ble_ambient_light_changed (const struct bt_gatt_attr *attr, uint16_t value)
{
    _ble_char_table[(uint32_t) BLE_CHAR_AMBIENT_LIGHT].notify = (value == BT_GATT_CCC_NOTIFY);
}


/*--------------------------------------------------------------------------------------------------
 * PRIVATE FUNCTION DEFINITIONS — FSM
 *------------------------------------------------------------------------------------------------*/

static ssize_t _ble_fsm_timeout_write (struct bt_conn *conn, const struct bt_gatt_attr *attr,
                                       const void *buf, uint16_t len, uint16_t offset, uint8_t flags)
{
    FSM_timout_set (*((uint32_t *) buf));
    return len;
}

static ssize_t _ble_fsm_timeout_read (struct bt_conn *conn, const struct bt_gatt_attr *attr,
                                      void *buf, uint16_t len, uint16_t offset)
{
    uint32_t val = FSM_timout_get ();
    return bt_gatt_attr_read (conn, attr, buf, len, offset, &val, sizeof(val));
}

static void _ble_fsm_timeout_changed (const struct bt_gatt_attr *attr, uint16_t value)
{
    _ble_char_table[(uint32_t) BLE_CHAR_FSM_TIMEOUT].notify = (value == BT_GATT_CCC_NOTIFY);
}

static ssize_t _ble_fsm_auto_brightness_write (struct bt_conn *conn, const struct bt_gatt_attr *attr,
                                               const void *buf, uint16_t len, uint16_t offset, uint8_t flags)
{
    FSM_auto_brightness_set ((bool) *((uint8_t *) buf));
    return len;
}

static ssize_t _ble_fsm_auto_brightness_read (struct bt_conn *conn, const struct bt_gatt_attr *attr,
                                              void *buf, uint16_t len, uint16_t offset)
{
    uint8_t auto_brightness = (uint8_t) FSM_auto_brightness_get ();
    return bt_gatt_attr_read (conn, attr, buf, len, offset, &auto_brightness, sizeof (auto_brightness));
}

static void _ble_fsm_auto_brightness_changed (const struct bt_gatt_attr *attr, uint16_t value)
{
    _ble_char_table[(uint32_t) BLE_CHAR_FSM_AUTO_BRIGHTNESS].notify = (value == BT_GATT_CCC_NOTIFY);
}

static ssize_t _ble_fsm_mode_write (struct bt_conn *conn, const struct bt_gatt_attr *attr,
                                    const void *buf, uint16_t len, uint16_t offset, uint8_t flags)
{
    FSM_mode_set ((FSM_MODE_e) *((uint8_t *) buf));
    return len;
}

static ssize_t _ble_fsm_mode_read (struct bt_conn *conn, const struct bt_gatt_attr *attr,
                                   void *buf, uint16_t len, uint16_t offset)
{
    uint8_t mode = (uint8_t) FSM_mode_get ();
    return bt_gatt_attr_read (conn, attr, buf, len, offset, &mode, sizeof (mode));
}

static void _ble_fsm_mode_changed (const struct bt_gatt_attr *attr, uint16_t value)
{
    _ble_char_table[(uint32_t) BLE_CHAR_FSM_MODE].notify = (value == BT_GATT_CCC_NOTIFY);
}

static ssize_t _ble_fsm_app_config_write (struct bt_conn *conn, const struct bt_gatt_attr *attr,
                                          const void *buf, uint16_t len, uint16_t offset, uint8_t flags)
{
    FSM_app_config_set ((bool) *((uint8_t *) buf));
    return len;
}

static ssize_t _ble_fsm_app_config_read (struct bt_conn *conn, const struct bt_gatt_attr *attr,
                                         void *buf, uint16_t len, uint16_t offset)
{
    uint8_t app_config = (uint8_t) FSM_app_config_get ();
    return bt_gatt_attr_read (conn, attr, buf, len, offset, &app_config, sizeof (app_config));
}


/*--------------------------------------------------------------------------------------------------
 * PRIVATE FUNCTION DEFINITIONS — BATTERY
 *------------------------------------------------------------------------------------------------*/

static ssize_t _ble_battery_level_read (struct bt_conn *conn, const struct bt_gatt_attr *attr,
                                        void *buf, uint16_t len, uint16_t offset)
{
    float batt_life = FSM_battery_life_get ();
    return bt_gatt_attr_read (conn, attr, buf, len, offset, &batt_life, sizeof(batt_life));
}

static void _ble_battery_level_changed (const struct bt_gatt_attr *attr, uint16_t value)
{
    _ble_char_table[(uint32_t) BLE_CHAR_BATTERY_LEVEL].notify = (value == BT_GATT_CCC_NOTIFY);
}


/*--------------------------------------------------------------------------------------------------
 * PRIVATE FUNCTION DEFINITIONS — SHOT CAPTURE
 *------------------------------------------------------------------------------------------------*/

static ssize_t _ble_shot_capture_tap_ths_write (struct bt_conn *conn, const struct bt_gatt_attr *attr,
                                                const void *buf, uint16_t len, uint16_t offset, uint8_t flags)
{
    if (len != 1U) {
        PRINT(INFO, "_ble_shot_capture_tap_ths_write: Incorrect data length");
        return BT_GATT_ERR(BT_ATT_ERR_INVALID_ATTRIBUTE_LEN);
    }
    SHOT_CAPTURE_tap_ths_set(*((uint8_t *) buf));
    return len;
}

static ssize_t _ble_shot_capture_tap_ths_read (struct bt_conn *conn, const struct bt_gatt_attr *attr,
                                               void *buf, uint16_t len, uint16_t offset)
{
    uint8_t val = SHOT_CAPTURE_config_get()->tap_ths;
    return bt_gatt_attr_read (conn, attr, buf, len, offset, &val, sizeof(val));
}

static ssize_t _ble_shot_capture_quiet_time_write (struct bt_conn *conn, const struct bt_gatt_attr *attr,
                                                   const void *buf, uint16_t len, uint16_t offset, uint8_t flags)
{
    if (len != 1U) {
        PRINT(INFO, "_ble_shot_capture_quiet_time_write: Incorrect data length");
        return BT_GATT_ERR(BT_ATT_ERR_INVALID_ATTRIBUTE_LEN);
    }
    SHOT_CAPTURE_quiet_time_set(*((LSM6DS3TR_C_QUIET_e *) buf));
    return len;
}

static ssize_t _ble_shot_capture_quiet_time_read (struct bt_conn *conn, const struct bt_gatt_attr *attr,
                                                  void *buf, uint16_t len, uint16_t offset)
{
    uint8_t val = (uint8_t)SHOT_CAPTURE_config_get()->quiet_time;
    return bt_gatt_attr_read (conn, attr, buf, len, offset, &val, sizeof(val));
}

static ssize_t _ble_shot_capture_shock_time_write (struct bt_conn *conn, const struct bt_gatt_attr *attr,
                                                   const void *buf, uint16_t len, uint16_t offset, uint8_t flags)
{
    if (len != 1U) {
        PRINT(INFO, "_ble_shot_capture_shock_time_write: Incorrect data length");
        return BT_GATT_ERR(BT_ATT_ERR_INVALID_ATTRIBUTE_LEN);
    }
    SHOT_CAPTURE_shock_time_set(*((LSM6DS3TR_C_SHOCK_e *) buf));
    return len;
}

static ssize_t _ble_shot_capture_shock_time_read (struct bt_conn *conn, const struct bt_gatt_attr *attr,
                                                  void *buf, uint16_t len, uint16_t offset)
{
    uint8_t val = (uint8_t)SHOT_CAPTURE_config_get()->shock_time;
    return bt_gatt_attr_read (conn, attr, buf, len, offset, &val, sizeof(val));
}

static ssize_t _ble_shot_capture_user_shot_cnt_reset (struct bt_conn *conn, const struct bt_gatt_attr *attr,
                                                      const void *buf, uint16_t len, uint16_t offset, uint8_t flags)
{
    if (len != 1U) {
        PRINT(INFO, "_ble_shot_capture_user_shot_cnt_reset: Incorrect data length");
        return BT_GATT_ERR(BT_ATT_ERR_INVALID_ATTRIBUTE_LEN);
    }
    uint8_t zero = 0;
    if (*((uint8_t *) buf) == 1) {
        SHOT_CAPTURE_config_get()->user_shot_cnt = 0;
    }
    BLE_notify (BLE_CHAR_USER_SHOT_CNT, &zero, sizeof (zero));
    return len;
}

static ssize_t _ble_shot_capture_user_shot_cnt_read (struct bt_conn *conn, const struct bt_gatt_attr *attr,
                                                     void *buf, uint16_t len, uint16_t offset)
{
    uint32_t val = SHOT_CAPTURE_config_get()->user_shot_cnt;
    return bt_gatt_attr_read(conn, attr, buf, len, offset, &val, sizeof(val));
}

static void _shot_capture_user_shot_cnt_ccc_changed (const struct bt_gatt_attr *attr, uint16_t value)
{
    _ble_char_table[(uint32_t) BLE_CHAR_USER_SHOT_CNT].notify = (value == BT_GATT_CCC_NOTIFY);
}

static ssize_t _ble_shot_capture_shot_cnt_reset (struct bt_conn *conn, const struct bt_gatt_attr *attr,
                                                 const void *buf, uint16_t len, uint16_t offset, uint8_t flags)
{
    if (len != 1U) {
        PRINT(INFO, "_ble_shot_capture_shot_cnt_reset: Incorrect data length");
        return BT_GATT_ERR(BT_ATT_ERR_INVALID_ATTRIBUTE_LEN);
    }
    uint8_t zero = 0;
    if (*((uint8_t *) buf) == 1) {
        SHOT_CAPTURE_config_get()->shot_cnt = 0;
    }
    BLE_notify (BLE_CHAR_TOTAL_SHOT_CNT, &zero, sizeof (zero));
    return len;
}

static ssize_t _ble_shot_capture_shot_cnt_read (struct bt_conn *conn, const struct bt_gatt_attr *attr,
                                                void *buf, uint16_t len, uint16_t offset)
{
    uint32_t val = SHOT_CAPTURE_config_get()->shot_cnt;
    return bt_gatt_attr_read(conn, attr, buf, len, offset, &val, sizeof(val));
}

static void _shot_capture_shot_cnt_ccc_changed (const struct bt_gatt_attr *attr, uint16_t value)
{
    _ble_char_table[(uint32_t) BLE_CHAR_TOTAL_SHOT_CNT].notify = (value == BT_GATT_CCC_NOTIFY);
}

static ssize_t _ble_shot_capture_capture_en_write (struct bt_conn *conn, const struct bt_gatt_attr *attr,
                                                   const void *buf, uint16_t len, uint16_t offset, uint8_t flags)
{
    if (len != 1U) {
        PRINT(INFO, "_ble_shot_capture_capture_en_write: Incorrect data length\r\n");
        return BT_GATT_ERR(BT_ATT_ERR_INVALID_ATTRIBUTE_LEN);
    }
    SHOT_CAPTURE_config_get()->shot_capture_en = (*((uint8_t *) buf) == 1) ? 1 : 0;
    return len;
}

static ssize_t _ble_shot_capture_capture_en_read (struct bt_conn *conn, const struct bt_gatt_attr *attr,
                                                  void *buf, uint16_t len, uint16_t offset)
{
    uint8_t val = SHOT_CAPTURE_config_get()->shot_capture_en;
    return bt_gatt_attr_read(conn, attr, buf, len, offset, &val, sizeof(val));
}

static ssize_t _ble_shot_capture_dur_loc_write (struct bt_conn *conn, const struct bt_gatt_attr *attr,
                                                const void *buf, uint16_t len, uint16_t offset, uint8_t flags)
{
    if (len != 2U) {
        PRINT(INFO, "_ble_shot_capture_dur_loc_write: Incorrect data length\r\n");
        return BT_GATT_ERR(BT_ATT_ERR_INVALID_ATTRIBUTE_LEN);
    }
    _ble_dur_loc_duration = ((uint8_t *) buf)[0];
    _ble_dur_loc_location = ((uint8_t *) buf)[1];
    k_work_submit(&_ble_dur_loc_work);
    return len;
}

static ssize_t _ble_shot_capture_dur_loc_read (struct bt_conn *conn, const struct bt_gatt_attr *attr,
                                               void *buf, uint16_t len, uint16_t offset)
{
    uint8_t dur_loc[2];
    dur_loc[0] = SHOT_CAPTURE_config_get()->shot_duration;
    dur_loc[1] = SHOT_CAPTURE_config_get()->shot_location;
    return bt_gatt_attr_read(conn, attr, buf, len, offset, &dur_loc, sizeof(dur_loc));
}

/*
 * SHOT DATA REQUEST characteristic
 *
 * Phone writes:
 *   0x01 = Request shot data read from flash
 *   0x02 = Start transfer (phone ready to receive data chunks)
 *   0x03 = Consumed (phone confirmed all data received)
 *   0x04 = Erase all shot data
 *   0x05 = Get file count
 *
 * Device notifies:
 *   0x01 = Data ready  [1 byte status + 4 bytes total_len]
 *   0x02 = No file found
 *   0x03 = Timeout
 *   0x04 = Erase done
 *   0x05 = Erase failed
 *   0x06 = File count  [1 byte status + 4 bytes count]
 */
static ssize_t _ble_shot_data_request_write (struct bt_conn *conn, const struct bt_gatt_attr *attr,
                                             const void *buf, uint16_t len, uint16_t offset, uint8_t flags)
{
    if (len != 1U) {
        PRINT(INFO, "_ble_shot_data_request_write: Incorrect data length\r\n");
        return BT_GATT_ERR(BT_ATT_ERR_INVALID_ATTRIBUTE_LEN);
    }

    uint8_t cmd = *((uint8_t *)buf);

    if (cmd == 0x01) {
        PRINT(INFO, "BLE: Shot data read requested\r\n");
        SHOT_STORAGE_request_read(READ_REQ_BLE);
    } else if (cmd == 0x02) {
        if (_ble_shot_data_buf == NULL || _ble_shot_data_total_len == 0) {
            PRINT(WARNING, "BLE: Start transfer requested but no data available\r\n");
            return BT_GATT_ERR(BT_ATT_ERR_UNLIKELY);
        }
        PRINT(INFO, "BLE: Phone ready, starting shot data transfer (%u bytes)\r\n", _ble_shot_data_total_len);
        _ble_shot_data_sent = 0;
        _ble_shot_data_chunk_size = bt_gatt_get_mtu(conn) - 3;
        PRINT(INFO, "BLE: chunk size %u bytes (MTU %u)\r\n", _ble_shot_data_chunk_size, _ble_shot_data_chunk_size + 3);

        if (_ble_shot_data_chunk_size < 182)
        {
            PRINT (WARNING, "BLE: MTU too low (%u), aborting transfer\n", _ble_shot_data_chunk_size + 3);
            uint8_t status = 0x07;
            BLE_notify (BLE_CHAR_SHOT_DATA_REQUEST, &status, sizeof(status));
            return BT_GATT_ERR(BT_ATT_ERR_UNLIKELY);
        }

        _ble_shot_data_transferring = true;
        _ble_shot_data_send_next(conn, NULL);
    } else if (cmd == 0x03) {
        PRINT(INFO, "BLE: Phone confirmed shot data consumed\r\n");
        _ble_shot_data_buf = NULL;
        _ble_shot_data_total_len = 0;
        _ble_shot_data_sent = 0;
        _ble_shot_data_transferring = false;
        _ble_shot_data_status = 0;
        SHOT_STORAGE_read_done();
    } else if (cmd == 0x04) {
        PRINT(INFO, "BLE: Erase all shot data requested\r\n");
        k_work_submit(&_ble_eraseall_work);
    } else if (cmd == 0x05) {
        uint32_t cnt = SHOT_STORAGE_get_file_cnt();
        uint8_t notify_buf[5];
        notify_buf[0] = 0x06;
        notify_buf[1] = (uint8_t)(cnt & 0xFF);
        notify_buf[2] = (uint8_t)((cnt >> 8) & 0xFF);
        notify_buf[3] = (uint8_t)((cnt >> 16) & 0xFF);
        notify_buf[4] = (uint8_t)((cnt >> 24) & 0xFF);
        BLE_notify(BLE_CHAR_SHOT_DATA_REQUEST, notify_buf, sizeof(notify_buf));
        PRINT(INFO, "BLE: File count requested: %u\r\n", cnt);
    } else {
        PRINT(INFO, "_ble_shot_data_request_write: Unknown command 0x%02X\r\n", cmd);
        return BT_GATT_ERR(BT_ATT_ERR_VALUE_NOT_ALLOWED);
    }

    return len;
}

static ssize_t _ble_shot_data_request_read (struct bt_conn *conn, const struct bt_gatt_attr *attr,
                                            void *buf, uint16_t len, uint16_t offset)
{
    return bt_gatt_attr_read(conn, attr, buf, len, offset,
                             &_ble_shot_data_status, sizeof(_ble_shot_data_status));
}

static void _ble_shot_data_request_ccc_changed (const struct bt_gatt_attr *attr, uint16_t value)
{
    _ble_char_table[(uint32_t) BLE_CHAR_SHOT_DATA_REQUEST].notify = (value == BT_GATT_CCC_NOTIFY);
}

static void _ble_shot_data_ccc_changed (const struct bt_gatt_attr *attr, uint16_t value)
{
    _ble_char_table[(uint32_t) BLE_CHAR_SHOT_DATA].notify = (value == BT_GATT_CCC_NOTIFY);
    PRINT(INFO, "BLE: Shot data notifications %s\r\n", value ? "enabled" : "disabled");
}

static void _ble_shot_data_send_next (struct bt_conn *conn, void *user_data)
{
    if (conn == NULL || !_ble_shot_data_transferring) {
        return;
    }

    if (_ble_shot_data_sent >= _ble_shot_data_total_len) {
        _ble_shot_data_transferring = false;
        PRINT(INFO, "BLE: Shot data transfer complete: %u bytes sent\r\n", _ble_shot_data_sent);
        return;
    }

    uint32_t remaining  = _ble_shot_data_total_len - _ble_shot_data_sent;
    uint16_t chunk_size = (remaining > _ble_shot_data_chunk_size)
                          ? _ble_shot_data_chunk_size : (uint16_t)remaining;

    struct bt_gatt_notify_params params = {
        .uuid  = NULL,
        .attr  = _ble_char_table[(uint32_t) BLE_CHAR_SHOT_DATA].p_notify_service,
        .data  = &_ble_shot_data_buf[_ble_shot_data_sent],
        .len   = chunk_size,
        .func  = _ble_shot_data_send_next,
    };

    int err = bt_gatt_notify_cb(conn, &params);
    if (err) {
        if (err == -ENOMEM) {
            k_msleep(1);
            _ble_shot_data_send_next(conn, NULL);
            return;
        }
        PRINT(ERROR, "BLE: notify failed (err %d), aborting transfer\r\n", err);
        _ble_shot_data_transferring = false;
        return;
    }

    _ble_shot_data_sent += chunk_size;
}

void BLE_shot_data_ready (uint8_t *p_data, uint32_t total_len)
{
    _ble_shot_data_buf       = p_data;
    _ble_shot_data_total_len = total_len;
    _ble_shot_data_sent      = 0;
    _ble_shot_data_status    = 0x01;

    uint8_t notify_buf[5];
    notify_buf[0] = _ble_shot_data_status;
    notify_buf[1] = (uint8_t)(total_len & 0xFF);
    notify_buf[2] = (uint8_t)((total_len >> 8) & 0xFF);
    notify_buf[3] = (uint8_t)((total_len >> 16) & 0xFF);
    notify_buf[4] = (uint8_t)((total_len >> 24) & 0xFF);
    BLE_notify(BLE_CHAR_SHOT_DATA_REQUEST, notify_buf, sizeof(notify_buf));
}

void BLE_shot_data_not_found (void)
{
    _ble_shot_data_buf           = NULL;
    _ble_shot_data_total_len     = 0;
    _ble_shot_data_sent          = 0;
    _ble_shot_data_transferring  = false;
    _ble_shot_data_status        = 0x02;
    BLE_notify(BLE_CHAR_SHOT_DATA_REQUEST, &_ble_shot_data_status, sizeof(_ble_shot_data_status));
}

void BLE_shot_data_timeout (void)
{
    _ble_shot_data_buf           = NULL;
    _ble_shot_data_total_len     = 0;
    _ble_shot_data_sent          = 0;
    _ble_shot_data_transferring  = false;
    _ble_shot_data_status        = 0x03;
    BLE_notify(BLE_CHAR_SHOT_DATA_REQUEST, &_ble_shot_data_status, sizeof(_ble_shot_data_status));
}


/*--------------------------------------------------------------------------------------------------
 * PRIVATE FUNCTION DEFINITIONS — Shot data work handlers
 *------------------------------------------------------------------------------------------------*/
static void _ble_dur_loc_work_handler (struct k_work *work)
{
    SHOT_CAPTURE_task_disable_and_wait();
    SHOT_STORAGE_wait_write_task_idle();
    SHOT_CAPTURE_shotdata_duration_location_set(_ble_dur_loc_duration, _ble_dur_loc_location);
    SHOT_CAPTURE_task_resume();
}

static void _ble_eraseall_work_handler (struct k_work *work)
{
    SHOT_CAPTURE_task_disable_and_wait();
    SHOT_STORAGE_wait_write_task_idle();

    if (SHOT_STORAGE_eraseall() == ERRNO_SUCCESS) {
        _ble_shot_data_status = 0x04;
    } else {
        _ble_shot_data_status = 0x05;
    }
    BLE_notify(BLE_CHAR_SHOT_DATA_REQUEST, &_ble_shot_data_status, sizeof(_ble_shot_data_status));

    SHOT_CAPTURE_task_resume();
}

static void _ble_settings_write_work_handler (struct k_work *work)
{
    _ble_settings_write ();
}

static void _ble_bond_clear_dwork_handler (struct k_work *work)
{
    _ble_bond_clear ();
}
