#ifndef __LL_H__
#define __LL_H__

#include "pdu.h"
#include "utils/slist.h"
#include "utils/simple_pool.h"
#include "utils/simple_ram_pool.h"
#include "utils/simple_data_fifo.h"
#include "ll_conn.h"

#define LL_VERSION_NUMBER BT_HCI_VERSION_5_2

#define LL_ADV_CMDS_ANY    0 /* Any advertising cmd/evt allowed */
#define LL_ADV_CMDS_LEGACY 1 /* Only legacy advertising cmd/evt allowed */
#define LL_ADV_CMDS_EXT    2 /* Only extended advertising cmd/evt allowed */




enum node_rx_type
{
    /* Unused */
    NODE_RX_TYPE_NONE = 0x00,
    NODE_RX_TYPE_SCAN_REQ,
    NODE_RX_TYPE_ADV,
    NODE_RX_TYPE_SCAN_RES,

    NODE_RX_TYPE_CONN_RX,

    NODE_RX_TYPE_CONNECTION,

    NODE_RX_TYPE_TX_CONNECTION,
};

/* Header of node_rx_pdu */
struct node_rx_hdr
{
    sys_snode_t *next;
    uint8_t type;
    int8_t  rssi;
    uint8_t           is_enc:1;
    uint8_t           is_drop:1;
    uint8_t          handle;
};

struct node_rx_pdu
{
    struct node_rx_hdr hdr;
    // set the max adv length.
    uint8_t               pdu[0];
};

struct node_rx_pdu_sub_adv
{
    struct node_rx_hdr hdr;
    // set the max adv length.
    uint8_t               pdu[PDU_AC_LL_SIZE_MAX];
};

struct node_rx_pdu_sub_data
{
    struct node_rx_hdr hdr;
    // set the max data length.
    uint8_t               pdu[PDU_DC_DATA_SIZE_MAX];
};






enum ll_msg_type
{
    /* Unused */
    LL_MSG_TYPE_NONE = 0x00,
    LL_MSG_TYPE_CONNECTION_SUCCESS,
    LL_MSG_TYPE_CONNECTION_DISCONNECTED,

    LL_MSG_TYPE_CONNECTION_PRAMS_UPDATE,
    LL_MSG_TYPE_CONNECTION_PHY_UPDATE,
    LL_MSG_TYPE_CONNECTION_DATA_LENGTH_CHANGED,

    LL_MSG_TYPE_CONN_TX_COMPLETE,
    LL_MSG_TYPE_CONN_TX_PDU,
};


struct ll_msg_node
{
    uint8_t msg;
    uint8_t          handle;    /* State/Role instance handle */
    uint16_t          opcode;
    void          *ptr;    /* ptr info */
};






/* Header of pdu_rx_pdu */
struct node_tx_hdr
{
    sys_snode_t *next;
    uint8_t           frag; /* User metadata */
    uint8_t           opcode; /* User metadata */
};

struct node_tx_pdu
{
    struct node_tx_hdr hdr;
    uint8_t pdu[0];
};


struct node_tx_pdu_sub_data
{
    struct node_tx_hdr hdr;
    // set the max data length.
    uint8_t               pdu[PDU_DC_DATA_SIZE_MAX];
};
struct node_tx_pdu_sub_ctrl
{
    struct node_tx_hdr hdr;
    // set the max data length.
    uint8_t               pdu[PDU_DC_LL_HEADER_SIZE + PDU_DC_PAYLOAD_SIZE_MIN];
};




#define LL_RF_GAP_TIME_COUNT (16)
struct ll_trx_rf_gap_time {
    uint8_t tx_phy;
    uint8_t rx_phy;
    uint8_t time;
};


static inline struct node_rx_pdu *ll_get_node_by_payload(uint8_t *ptr)
{
    return (struct node_rx_pdu *)(ptr - sizeof(struct node_rx_hdr));
}


extern SIMPLE_POOL_PTR_DEFINE(adv_rx_free);
extern SIMPLE_POOL_PTR_DEFINE(pdu_data_rx_free);

extern SIMPLE_POOL_PTR_DEFINE(pdu_data_tx_free);
extern SIMPLE_POOL_PTR_DEFINE(pdu_ctrl_tx_free);


extern SIMPLE_DATA_FIFO_PTR_DEFINE(msg_fifo);


int ll_init(void);
void ll_reset(void);
void ll_init_gap_time(const uint8_t* gap);

void rand_get(void *buf, size_t len);


void *ll_adv_rx_alloc_peek(void);
void *ll_adv_rx_alloc(void);
void ll_adv_rx_free(void *mem);


void ll_node_rx_enqueue(void* node);
void *ll_node_rx_get(void);
void *ll_node_rx_dequeue(void);
int ll_node_rx_process(struct node_rx_pdu *node_rx);
void ll_node_rx_free_check(struct node_rx_pdu *node_rx);



void *ll_pdu_data_rx_alloc_peek(void);
void *ll_pdu_data_rx_alloc(void);
void ll_pdu_data_rx_free(void *mem);




uint8_t ll_msg_alloc_peek(uint8_t **mem);
void ll_msg_alloc(uint8_t idx);
uint8_t ll_msg_common_send(enum ll_msg_type msg, uint16_t handle, uint8_t opcode);
uint8_t ll_msg_send_empty_type(enum ll_msg_type msg, uint16_t handle);
void *ll_msg_get(void);
void ll_msg_free(void);

void ll_free_node_rx(struct node_rx_pdu *node_rx);
void ll_polling_work(void);





void *ll_pdu_tx_ctrl_alloc_peek(void);
void *ll_pdu_tx_ctrl_alloc(void);
void ll_pdu_tx_ctrl_free(void *pdu);
void *ll_pdu_tx_data_alloc_peek(void);
void *ll_pdu_tx_data_alloc(void);
void ll_pdu_tx_data_free(void *pdu);
void *ll_pdu_tx_alloc(uint8_t is_data);
void ll_pdu_tx_free(struct node_tx_pdu *node_tx);
void ll_length_default_get(uint16_t *const max_tx_octets,
                           uint16_t *const max_tx_time);
uint8_t ll_length_default_set(uint16_t max_tx_octets, uint16_t max_tx_time);
void ll_length_max_get(uint16_t *const max_tx_octets,
                       uint16_t *const max_tx_time,
                       uint16_t *const max_rx_octets,
                       uint16_t *const max_rx_time);




uint8_t *ll_addr_get(uint8_t addr_type, uint8_t *p_bdaddr);
uint8_t ll_addr_set(uint8_t addr_type, uint8_t const *const p_bdaddr);


uint32_t ll_get_next_timestamp(uint8_t tx_phy, uint8_t rx_phy);

int ll_check_allow_sleep(void);


void ll_buf_pool_init(simple_ram_pool_t* ram_pool);
#endif