/* main.c - Application main entry point */

/*
 * Copyright (c) 2015-2016 Intel Corporation
 *
 * SPDX-License-Identifier: Apache-2.0
 */

#include <zephyr/types.h>
#include <stddef.h>
#include <string.h>
#include <errno.h>
#include <zephyr/sys/printk.h>
#include <zephyr/sys/byteorder.h>
#include <zephyr/kernel.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/bluetooth/services/bas.h>
#include <zephyr/bluetooth/services/hrs.h>


/* The default MTU size before negotiating a longer MTU */
#define BLE_GATT_ATT_MTU_DEFAULT 23

static struct bt_conn *current_conn;
static struct bt_gatt_exchange_params exchange_params;
static uint16_t max_send_len = BLE_GATT_ATT_MTU_DEFAULT - 3;

static const struct bt_data ad[] = {
	BT_DATA_BYTES(BT_DATA_FLAGS, (BT_LE_AD_GENERAL | BT_LE_AD_NO_BREDR)),
	BT_DATA_BYTES(BT_DATA_UUID16_ALL,
		      BT_UUID_16_ENCODE(BT_UUID_HRS_VAL),
		      BT_UUID_16_ENCODE(BT_UUID_BAS_VAL),
		      BT_UUID_16_ENCODE(BT_UUID_DIS_VAL))
			  
};

static struct bt_conn_le_phy_param phy = {
    .options = BT_CONN_LE_PHY_OPT_CODED_S2,
    .pref_rx_phy = BT_GAP_LE_PHY_CODED,
    .pref_tx_phy = BT_GAP_LE_PHY_CODED
};

static void exchange_func(struct bt_conn *conn, uint8_t err, struct bt_gatt_exchange_params *params)
{
	if (!err) {
		max_send_len = bt_gatt_get_mtu(conn);
		printk("ATT MTU size updated to %d bytes\n", max_send_len);
#if CONFIG_BT_USER_DATA_LEN_UPDATE
		err = bt_conn_le_data_len_update(current_conn, BT_LE_DATA_LEN_PARAM_MAX);
		if (err) {
			printk("Failed to update data length (error %u)\n", err);
		}
#endif
	}
}

static void connected(struct bt_conn *conn, uint8_t err)
{
	char addr[BT_ADDR_LE_STR_LEN];
	
	if (err) {
		printk("Connection failed (err 0x%02x)\n", err);
	} 
	
    bt_addr_le_to_str(bt_conn_get_dst(conn), addr, sizeof(addr));
	printk("Connected\n");

	current_conn = bt_conn_ref(conn);

	//int err;
    err = bt_conn_le_phy_update(conn, &phy);
	if (err) {
		printk("bt_conn_le_phy_update() returned %d\n", err);
	}

	exchange_params.func = exchange_func;

	err = bt_gatt_exchange_mtu(current_conn, &exchange_params);
	if (err) {
		printk("bt_gatt_exchange_mtu: %d\n", err);
	}
	
	
}

static void disconnected(struct bt_conn *conn, uint8_t reason)
{
	printk("Disconnected (reason 0x%02x)\n", reason);
}

static const char *phy2str(uint8_t phy)
{
	switch (phy) {
	case 0: return "No packets";
	case BT_GAP_LE_PHY_1M: return "LE 1M";
	case BT_GAP_LE_PHY_2M: return "LE 2M";
	case BT_GAP_LE_PHY_CODED: return "LE Coded";
	default: return "Unknown";
	}
}

static void phy_updated(struct bt_conn *conn, struct bt_conn_le_phy_info *param)
{

	printk("PHY updated. RX PHY: %s, TX PHY: %s\n", phy2str(param->tx_phy), phy2str(param->rx_phy));
}
#if CONFIG_BT_USER_DATA_LEN_UPDATE
static void data_len_updated(struct bt_conn *conn, struct bt_conn_le_data_len_info *info)
{
	printk("Data length updated. RX length: %d, TX length: %d\n", info->rx_max_len,
		info->tx_max_len);
}
#endif
BT_CONN_CB_DEFINE(conn_callbacks) = {
	.connected = connected,
	.disconnected = disconnected,
#if CONFIG_BT_USER_DATA_LEN_UPDATE
	.le_data_len_updated = data_len_updated,
#endif
	.le_phy_updated = phy_updated,
};

static void bt_ready(void)
{
	int err;

	printk("Bluetooth initialized\n");

	err = bt_le_adv_start(BT_LE_ADV_CONN_NAME, ad, ARRAY_SIZE(ad), NULL, 0);
	if (err) {
		printk("Advertising failed to start (err %d)\n", err);
		return;
	}

	printk("Advertising successfully started\n");
}

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

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

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

static struct bt_conn_auth_cb auth_cb_display = {
	.cancel = auth_cancel,
};

static void bas_notify(void)
{
	uint8_t battery_level = bt_bas_get_battery_level();

	battery_level--;

	if (!battery_level) {
		battery_level = 100U;
	}

	bt_bas_set_battery_level(battery_level);
}

static void hrs_notify(void)
{
	static uint8_t heartrate = 90U;

	/* Heartrate measurements simulation */
	heartrate++;
	if (heartrate == 160U) {
		heartrate = 90U;
	}

	bt_hrs_notify(heartrate);
}

void main(void)
{
	int err;

	err = bt_enable(NULL);
	if (err) {
		printk("Bluetooth init failed (err %d)\n", err);
		return;
	}

	bt_ready();

	bt_conn_auth_cb_register(&auth_cb_display);

	/* Implement notification. At the moment there is no suitable way
	 * of starting delayed work so we do it here
	 */
	while (1) {
		k_sleep(K_SECONDS(1));

		/* Heartrate measurements simulation */
		hrs_notify();

		/* Battery level simulation */
		bas_notify();
	}
}
