#include "att.h"

#include "host/uuid.h"

#include "utils/byteorder.h"

#include "host/gatt.h"
#include "host/l2cap.h"

#include "utils/simple_buf.h"
#include "utils/simple_data_fifo.h"

#define LOG_MODULE_NAME att
#include "bt_log.h"

#if defined(CONFIG_BT_CONN)

/* ATT connection specific data */
struct bt_att
{
    struct bt_conn *conn;
    struct bt_att_req *req;
    /* request queue */
    sys_slist_t reqs;
    /* tx queue */
    sys_slist_t tx_queue;
    #if CONFIG_BT_ATT_PREPARE_COUNT > 0
	sys_slist_t		prep_queue;
    #endif

    uint16_t mtu;
};

int att_send(struct bt_att *att, simple_buf_t *buf);
struct bt_att* att_info_get_by_conn(struct bt_conn *conn);
uint8_t att_handle_rsp(struct bt_att *att, void *pdu, uint16_t len, uint8_t err);

#if defined(CONFIG_BT_SMP)
static uint8_t att_req_retry(struct bt_att *att)
{
	struct bt_att_req *req = att->req;
	simple_buf_t *buf;

	/* Resend buffer */
	if (!req->encode) {
		/* This request does not support resending */
		return BT_ATT_ERR_AUTHENTICATION;
	}

	buf = bt_att_create_pdu(req->att_op, req->len);
	if (!buf) {
		return BT_ATT_ERR_UNLIKELY;
	}

	if (req->encode(buf, req->len, req->user_data)) {
		simple_buf_free(buf);
		return BT_ATT_ERR_UNLIKELY;
	}

	if (att_send(att, buf)) {
		simple_buf_free(buf);
		return BT_ATT_ERR_UNLIKELY;
	}

	return BT_ATT_ERR_SUCCESS;
}

void bt_att_encrypt_change(struct bt_conn *conn, uint8_t hci_status)
{
	struct bt_att *att = att_info_get_by_conn(conn);
	uint8_t err;

	LOG_DBG("conn %p handle %u sec_level 0x%02x status 0x%02x", conn,
		conn->handle, conn->sec_level, hci_status);

	if (!att) {
		LOG_DBG("Ignore encrypt change");
		return;
	}

	/*
	 * If status (HCI status of security procedure) is non-zero, notify
	 * outstanding request about security failure.
	 */
	if (hci_status) {
		if (att->req && att->req->retrying) {
			att_handle_rsp(att, NULL, 0,
				       BT_ATT_ERR_AUTHENTICATION);
		}

		return;
	}

	bt_gatt_encrypt_change(conn);

	if (conn->sec_level == BT_SECURITY_L1) {
		return;
	}

	if (!(att->req && att->req->retrying)) {
		return;
	}

	LOG_DBG("Retrying");

	err = att_req_retry(att);
	if (err) {
		LOG_DBG("Retry failed (%d)", err);
		att_handle_rsp(att, NULL, 0, err);
	}
}
#endif


struct bt_att * att_info = (struct bt_att *)0x20000894;

uint8_t bt_att_can_sleep()
{
    for(int i = 0; i < CONFIG_BT_MAX_CONN; i ++)
    {
        if(att_info[i].conn != NULL)
        {
            if (!sys_slist_is_empty(&att_info[i].tx_queue)) return 0;
        }
    }

    return 1;
}

static void att_send_process(struct bt_att *att)
{
    simple_buf_t *buf;

    buf = (simple_buf_t *)sys_slist_get(&att->tx_queue);
    if (buf)
    {
        if (att_send(att, buf))
        {
            /* Push it back if it could not be send */
            sys_slist_prepend(&att->tx_queue, (void *)buf);
        }
    }
}

void bt_att_tx_polling()
{
    for(int i = 0; i < CONFIG_BT_MAX_CONN; i ++)
    {
        if(att_info[i].conn != NULL)
        {
            att_send_process(&att_info[i]);
        }
    }
}
#endif /* defined(CONFIG_BT_CONN) */


