#ifndef __ATT_H__
#define __ATT_H__

#include "base/bt_common.h"


#include "utils/slist.h"
#include "utils/simple_ram_pool.h"

#include "host/conn.h"


/* Error codes for Error response PDU */
#define BT_ATT_ERR_SUCCESS                 0x00
#define BT_ATT_ERR_INVALID_HANDLE          0x01
#define BT_ATT_ERR_READ_NOT_PERMITTED      0x02
#define BT_ATT_ERR_WRITE_NOT_PERMITTED     0x03
#define BT_ATT_ERR_INVALID_PDU             0x04
#define BT_ATT_ERR_AUTHENTICATION          0x05
#define BT_ATT_ERR_NOT_SUPPORTED           0x06
#define BT_ATT_ERR_INVALID_OFFSET          0x07
#define BT_ATT_ERR_AUTHORIZATION           0x08
#define BT_ATT_ERR_PREPARE_QUEUE_FULL      0x09
#define BT_ATT_ERR_ATTRIBUTE_NOT_FOUND     0x0a
#define BT_ATT_ERR_ATTRIBUTE_NOT_LONG      0x0b
#define BT_ATT_ERR_ENCRYPTION_KEY_SIZE     0x0c
#define BT_ATT_ERR_INVALID_ATTRIBUTE_LEN   0x0d
#define BT_ATT_ERR_UNLIKELY                0x0e
#define BT_ATT_ERR_INSUFFICIENT_ENCRYPTION 0x0f
#define BT_ATT_ERR_UNSUPPORTED_GROUP_TYPE  0x10
#define BT_ATT_ERR_INSUFFICIENT_RESOURCES  0x11
#define BT_ATT_ERR_DB_OUT_OF_SYNC          0x12
#define BT_ATT_ERR_VALUE_NOT_ALLOWED       0x13

/* Common Profile Error Codes (from CSS) */
#define BT_ATT_ERR_WRITE_REQ_REJECTED    0xfc
#define BT_ATT_ERR_CCC_IMPROPER_CONF     0xfd
#define BT_ATT_ERR_PROCEDURE_IN_PROGRESS 0xfe
#define BT_ATT_ERR_OUT_OF_RANGE          0xff

/* Version 5.2, Vol 3, Part F, 3.2.9 defines maximum attribute length to 512 */
#define BT_ATT_MAX_ATTRIBUTE_LEN 512

/* Handle 0x0000 is reserved for future use */
#define BT_ATT_FIRST_ATTRIBUTE_HANDLE  0x0001
/* 0xffff is defined as the maximum, and thus last, valid attribute handle */
#define BT_ATT_LAST_ATTRIBUTE_HANDLE   0xffff


#define BT_EATT_PSM           0x27
#define BT_ATT_DEFAULT_LE_MTU 23
#define BT_ATT_TIMEOUT        K_SECONDS(30)

/* ATT MTU must be equal for RX and TX, so select the smallest value */
// #define BT_ATT_MTU (MIN(BT_L2CAP_RX_MTU, BT_L2CAP_TX_MTU))




/** @brief ATT channel option bit field values.
 * @note @ref BT_ATT_CHAN_OPT_UNENHANCED_ONLY and @ref BT_ATT_CHAN_OPT_ENHANCED_ONLY are mutually
 * exclusive and both bits may not be set.
 */
enum bt_att_chan_opt
{
    /** Both Enhanced and Unenhanced channels can be used  */
    BT_ATT_CHAN_OPT_NONE = 0x0,
    /** Only Unenhanced channels will be used  */
    BT_ATT_CHAN_OPT_UNENHANCED_ONLY = BIT(0),
    /** Only Enhanced channels will be used  */
    BT_ATT_CHAN_OPT_ENHANCED_ONLY = BIT(1),
};


struct bt_att_hdr
{
    uint8_t code;
} __packed;

#define BT_ATT_OP_ERROR_RSP 0x01
struct bt_att_error_rsp
{
    uint8_t request;
    uint16_t handle;
    uint8_t error;
} __packed;

#define BT_ATT_OP_MTU_REQ 0x02
struct bt_att_exchange_mtu_req
{
    uint16_t mtu;
} __packed;

#define BT_ATT_OP_MTU_RSP 0x03
struct bt_att_exchange_mtu_rsp
{
    uint16_t mtu;
} __packed;

/* Find Information Request */
#define BT_ATT_OP_FIND_INFO_REQ 0x04
struct bt_att_find_info_req
{
    uint16_t start_handle;
    uint16_t end_handle;
} __packed;

/* Format field values for BT_ATT_OP_FIND_INFO_RSP */
#define BT_ATT_INFO_16  0x01
#define BT_ATT_INFO_128 0x02

struct bt_att_info_16
{
    uint16_t handle;
    uint16_t uuid;
} __packed;

struct bt_att_info_128
{
    uint16_t handle;
    uint8_t uuid[16];
} __packed;

/* Find Information Response */
#define BT_ATT_OP_FIND_INFO_RSP 0x05
struct bt_att_find_info_rsp
{
    uint8_t format;
    uint8_t info[0];
} __packed;

/* Find By Type Value Request */
#define BT_ATT_OP_FIND_TYPE_REQ 0x06
struct bt_att_find_type_req
{
    uint16_t start_handle;
    uint16_t end_handle;
    uint16_t type;
    uint8_t value[0];
} __packed;

struct bt_att_handle_group
{
    uint16_t start_handle;
    uint16_t end_handle;
} __packed;

/* Find By Type Value Response */
#define BT_ATT_OP_FIND_TYPE_RSP 0x07
struct bt_att_find_type_rsp
{
    struct bt_att_handle_group list[0];
} __packed;

/* Read By Type Request */
#define BT_ATT_OP_READ_TYPE_REQ 0x08
struct bt_att_read_type_req
{
    uint16_t start_handle;
    uint16_t end_handle;
    uint8_t uuid[0];
} __packed;

struct bt_att_data
{
    uint16_t handle;
    uint8_t value[0];
} __packed;

/* Read By Type Response */
#define BT_ATT_OP_READ_TYPE_RSP 0x09
struct bt_att_read_type_rsp
{
    uint8_t len;
    struct bt_att_data data[0];
} __packed;

/* Read Request */
#define BT_ATT_OP_READ_REQ 0x0a
struct bt_att_read_req
{
    uint16_t handle;
} __packed;

/* Read Response */
#define BT_ATT_OP_READ_RSP 0x0b
struct bt_att_read_rsp
{
    uint8_t value[0];
} __packed;

/* Read Blob Request */
#define BT_ATT_OP_READ_BLOB_REQ 0x0c
struct bt_att_read_blob_req
{
    uint16_t handle;
    uint16_t offset;
} __packed;

/* Read Blob Response */
#define BT_ATT_OP_READ_BLOB_RSP 0x0d
struct bt_att_read_blob_rsp
{
    uint8_t value[0];
} __packed;

/* Read Multiple Request */
#define BT_ATT_READ_MULT_MIN_LEN_REQ 0x04

#define BT_ATT_OP_READ_MULT_REQ 0x0e
struct bt_att_read_mult_req
{
    uint16_t handles[0];
} __packed;

/* Read Multiple Response */
#define BT_ATT_OP_READ_MULT_RSP 0x0f
struct bt_att_read_mult_rsp
{
    uint8_t value[0];
} __packed;

/* Read by Group Type Request */
#define BT_ATT_OP_READ_GROUP_REQ 0x10
struct bt_att_read_group_req
{
    uint16_t start_handle;
    uint16_t end_handle;
    uint8_t uuid[0];
} __packed;

struct bt_att_group_data
{
    uint16_t start_handle;
    uint16_t end_handle;
    uint8_t value[0];
} __packed;

/* Read by Group Type Response */
#define BT_ATT_OP_READ_GROUP_RSP 0x11
struct bt_att_read_group_rsp
{
    uint8_t len;
    struct bt_att_group_data data[0];
} __packed;

/* Write Request */
#define BT_ATT_OP_WRITE_REQ 0x12
struct bt_att_write_req
{
    uint16_t handle;
    uint8_t value[0];
} __packed;

/* Write Response */
#define BT_ATT_OP_WRITE_RSP 0x13

/* Prepare Write Request */
#define BT_ATT_OP_PREPARE_WRITE_REQ 0x16
struct bt_att_prepare_write_req
{
    uint16_t handle;
    uint16_t offset;
    uint8_t value[0];
} __packed;

/* Prepare Write Respond */
#define BT_ATT_OP_PREPARE_WRITE_RSP 0x17
struct bt_att_prepare_write_rsp
{
    uint16_t handle;
    uint16_t offset;
    uint8_t value[0];
} __packed;

/* Execute Write Request */
#define BT_ATT_FLAG_CANCEL 0x00
#define BT_ATT_FLAG_EXEC   0x01

#define BT_ATT_OP_EXEC_WRITE_REQ 0x18
struct bt_att_exec_write_req
{
    uint8_t flags;
} __packed;

/* Execute Write Response */
#define BT_ATT_OP_EXEC_WRITE_RSP 0x19

/* Handle Value Notification */
#define BT_ATT_OP_NOTIFY 0x1b
struct bt_att_notify
{
    uint16_t handle;
    uint8_t value[0];
} __packed;

/* Handle Value Indication */
#define BT_ATT_OP_INDICATE 0x1d
struct bt_att_indicate
{
    uint16_t handle;
    uint8_t value[0];
} __packed;

/* Handle Value Confirm */
#define BT_ATT_OP_CONFIRM 0x1e

struct bt_att_signature
{
    uint8_t value[12];
} __packed;

#define BT_ATT_OP_READ_MULT_VL_REQ 0x20
struct bt_att_read_mult_vl_req
{
    uint16_t handles[0];
} __packed;

/* Read Multiple Response */
#define BT_ATT_OP_READ_MULT_VL_RSP 0x21
struct bt_att_read_mult_vl_rsp
{
    uint16_t len;
    uint8_t value[0];
} __packed;

/* Handle Multiple Value Notification */
#define BT_ATT_OP_NOTIFY_MULT 0x23
struct bt_att_notify_mult
{
    uint16_t handle;
    uint16_t len;
    uint8_t value[0];
} __packed;

/* Write Command */
#define BT_ATT_OP_WRITE_CMD 0x52
struct bt_att_write_cmd
{
    uint16_t handle;
    uint8_t value[0];
} __packed;

/* Signed Write Command */
#define BT_ATT_OP_SIGNED_WRITE_CMD 0xd2
struct bt_att_signed_write_cmd
{
    uint16_t handle;
    uint8_t value[0];
} __packed;

typedef void (*bt_att_func_t)(struct bt_conn *conn, uint8_t err, const void *pdu, uint16_t length,
                              void *user_data);

typedef int (*bt_att_encode_t)(simple_buf_t *buf, size_t len, void *user_data);

/* ATT request context */
struct bt_att_req
{
    sys_snode_t node;
    bt_att_func_t func;
    simple_buf_t *buf;
#if defined(CONFIG_BT_SMP)
    bt_att_encode_t encode;
    uint8_t retrying : 1;
    uint8_t att_op;
    size_t len;
#endif /* CONFIG_BT_SMP */
    void *user_data;
};

extern SIMPLE_POOL_PTR_DEFINE(att_req_pool);

void att_sent(struct bt_conn *conn, void *user_data);

void bt_att_init(void);
uint16_t bt_att_get_mtu(struct bt_conn *conn);
simple_buf_t *bt_att_create_pdu(uint8_t op, size_t len);

/* Allocate a new request */
struct bt_att_req *bt_att_req_alloc(void);

/* Free a request */
void bt_att_req_free(struct bt_att_req *req);

/* Send ATT PDU over a connection */
int bt_att_send(struct bt_conn *conn, simple_buf_t *buf);

/* Send ATT Request over a connection */
int bt_att_req_send(struct bt_conn *conn, struct bt_att_req *req);

/* Cancel ATT request */
void bt_att_req_cancel(struct bt_conn *conn, struct bt_att_req *req);

/* Disconnect EATT channels */
int bt_eatt_disconnect(struct bt_conn *conn);

/** @brief Find a pending ATT request by its user_data pointer.
 *  @param conn The connection the request was issued on.
 *  @param user_data The pointer value to look for.
 *  @return The found request. NULL if not found.
 */
struct bt_att_req *bt_att_find_req_by_user_data(struct bt_conn *conn, const void *user_data);

/* Checks if only the fixed ATT channel is connected */
bool bt_att_fixed_chan_only(struct bt_conn *conn);

/* Clear the out of sync flag on all channels */
void bt_att_clear_out_of_sync_sent(struct bt_conn *conn);

/* Check if BT_ATT_ERR_DB_OUT_OF_SYNC has been sent on the fixed ATT channel */
bool bt_att_out_of_sync_sent_on_fixed(struct bt_conn *conn);

typedef void (*bt_gatt_complete_func_t)(struct bt_conn *conn, void *user_data);
void bt_att_set_tx_meta_data(simple_buf_t *buf, bt_gatt_complete_func_t func, void *user_data,
                             enum bt_att_chan_opt chan_opt);
void bt_att_increment_tx_meta_data_attr_count(simple_buf_t *buf, uint16_t attr_count);

bool bt_att_tx_meta_data_match(const simple_buf_t *buf, bt_gatt_complete_func_t func,
                               const void *user_data, enum bt_att_chan_opt chan_opt);

void bt_att_free_tx_meta_data(const simple_buf_t *buf);

#if defined(CONFIG_BT_EATT)
#define BT_ATT_CHAN_OPT(_params) (_params)->chan_opt
#else
#define BT_ATT_CHAN_OPT(_params) BT_ATT_CHAN_OPT_UNENHANCED_ONLY
#endif /* CONFIG_BT_EATT */

bool bt_att_chan_opt_valid(struct bt_conn *conn, enum bt_att_chan_opt chan_opt);
uint8_t bt_att_check_allow_sleep(void);

void bt_att_sleep_wake_init(void);

#if defined(CONFIG_BT_MONITOR_SLEEP)
void bt_att_monitor_sleep(void);
#endif

uint8_t bt_att_get_tx_free_size(void);

int bt_att_recv(struct bt_conn *conn, simple_buf_t *buf);



void bt_att_init(void);
int bt_att_connected(struct bt_conn *conn);
int bt_att_disconnected(struct bt_conn *conn);

void bt_att_tx_polling();
uint8_t bt_att_can_sleep();

int bt_att_mtu_get(void);
void bt_att_pool_init(simple_ram_pool_t* ram_pool);

#if defined (CONFIG_BT_SMP)
void bt_att_encrypt_change(struct bt_conn *conn, uint8_t hci_status);
#endif
#endif