/*
 *
 *    Copyright (c) 2020-2021 Project CHIP Authors
 *
 *    Licensed under the Apache License, Version 2.0 (the "License");
 *    you may not use this file except in compliance with the License.
 *    You may obtain a copy of the License at
 *
 *        http://www.apache.org/licenses/LICENSE-2.0
 *
 *    Unless required by applicable law or agreed to in writing, software
 *    distributed under the License is distributed on an "AS IS" BASIS,
 *    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 *    See the License for the specific language governing permissions and
 *    limitations under the License.
 */

/**
 *    @file
 *          Provides an implementation of the BLEManager singleton object
 *          for the Telink platform.
 */
/* this file behaves like a config.h, comes first */
#include <platform/internal/CHIPDeviceLayerInternal.h>

#if CHIP_DEVICE_CONFIG_ENABLE_CHIPOBLE

#include <ble/BleUUID.h>
#include <ble/CHIPBleServiceData.h>
#include <platform/internal/BLEManager.h>

#include <lib/support/CodeUtils.h>
#include <lib/support/logging/CHIPLogging.h>

/*Includes for ieee802154 switchings */
#define DT_DRV_COMPAT telink_b91_zb
#include <drivers/ieee802154/b91.h>
#include <net/ieee802154_radio.h>

/* Telink headers */
#include "drivers.h"
#include "ext_driver/ext_misc.h"
#include "stack/ble/ble.h"
#include "tl_common.h"
#include "types.h"

using namespace ::chip;
using namespace ::chip::Ble;
using namespace ::chip::System;

namespace chip {
namespace DeviceLayer {
namespace Internal {

namespace {

typedef enum
{
    ATT_H_START = 0,

    /* GAP service */
    GenericAccess_PS_H,            // UUID: 2800, VALUE: uuid 1800
    GenericAccess_DeviceName_CD_H, // UUID: 2803, VALUE: Prop: Read | Notify
    GenericAccess_DeviceName_DP_H, // UUID: 2A00, VALUE: device name
    GenericAccess_Appearance_CD_H, // UUID: 2803, VALUE: Prop: Read
    GenericAccess_Appearance_DP_H, // UUID: 2A01, VALUE: appearance
    CONN_PARAM_CD_H,               // UUID: 2803, VALUE: Prop: Read
    CONN_PARAM_DP_H,               // UUID: 2A04, VALUE: connParameter

    /* GATT service */
    GenericAttribute_PS_H,                 // UUID: 2800, VALUE: uuid 1801
    GenericAttribute_ServiceChanged_CD_H,  // UUID: 2803, VALUE: Prop: Indicate
    GenericAttribute_ServiceChanged_DP_H,  // UUID: 2A05, VALUE: service change
    GenericAttribute_ServiceChanged_CCB_H, // UUID: 2902, VALUE: serviceChangeCCC

    /* Matter service */
    Matter_PS_H,
    Matter_RX_CD_H,
    Matter_RX_DP_H,
    Matter_TX_CD_H,
    Matter_TX_DP_H,
    Matter_TX_CCC_H,

    ATT_END_H,

} ATT_HANDLE;

typedef struct
{
    /** Minimum value for the connection event (interval. 0x0006 - 0x0C80 * 1.25 ms) */
    u16 intervalMin;
    /** Maximum value for the connection event (interval. 0x0006 - 0x0C80 * 1.25 ms) */
    u16 intervalMax;
    /** Number of LL latency connection events (0x0000 - 0x03e8) */
    u16 latency;
    /** Connection Timeout (0x000A - 0x0C80 * 10 ms) */
    u16 timeout;
} gap_periConnectParams_t;

#define CHIP_MAC_LEN 6
#define CHIP_MTU_SIZE 244
#define CHIP_MAX_ADV_DATA_LEN 31
#define CHIP_MAX_RESPONSE_DATA_LEN 31
#define CHIP_SHORT_UUID_LEN 2

#define CHIP_ADE_DATA_LEN_FLAGS 0x02
#define CHIP_ADV_DATA_TYPE_FLAGS 0x01
#define CHIP_ADV_DATA_FLAGS 0x06
#define CHIP_ADV_DATA_TYPE_UUID 0x03
#define CHIP_ADV_DATA_TYPE_NAME 0x09
#define CHIP_ADV_DATA_TYPE_SERVICE_DATA 0x16
#define CHIP_ADV_SERVICE_DATA_LEN (sizeof(ChipBLEDeviceIdentificationInfo) + CHIP_SHORT_UUID_LEN + 1)

#define CHIP_BLE_TX_FIFO_SIZE 48
#define CHIP_BLE_TX_FIFO_NUM 33
#define CHIP_BLE_RX_FIFO_SIZE 48
#define CHIP_BLE_RX_FIFO_NUM 8

#define CHIP_BLE_THREAD_STACK_SIZE 2048
#define CHIP_BLE_THREAD_PRIORITY 2

#define CHIP_BLE_DISCONNECT_REASON 8

#define CHIP_RF_PACKET_HEADER_SIZE 3

#define STIMER_IRQ_NUM 1
#define RF_IRQ_NUM 15

#define CHIP_RX_CHAR_UUID 0x11, 0x9D, 0x9F, 0x42, 0x9C, 0x4F, 0x9F, 0x95, 0x59, 0x45, 0x3D, 0x26, 0xF5, 0x2E, 0xEE, 0x18
#define CHIP_TX_CHAR_UUID 0x12, 0x9D, 0x9F, 0x42, 0x9C, 0x4F, 0x9F, 0x95, 0x59, 0x45, 0x3D, 0x26, 0xF5, 0x2E, 0xEE, 0x18

const ChipBleUUID chipUUID_CHIPoBLEChar_RX = { { 0x18, 0xEE, 0x2E, 0xF5, 0x26, 0x3D, 0x45, 0x59, 0x95, 0x9F, 0x4F, 0x9C, 0x42, 0x9F,
                                                 0x9D, 0x11 } };

const ChipBleUUID chipUUID_CHIPoBLEChar_TX = { { 0x18, 0xEE, 0x2E, 0xF5, 0x26, 0x3D, 0x45, 0x59, 0x95, 0x9F, 0x4F, 0x9C, 0x42, 0x9F,
                                                 0x9D, 0x12 } };

static const uint8_t matterServiceUUID[CHIP_SHORT_UUID_LEN] = { 0xF6, 0xFF }; // service UUID

} // unnamed namespace

BLEManagerImpl BLEManagerImpl::sInstance;

void rf_irq_handler(const void * paramiter)
{
    irq_blt_sdk_handler();
}

void stimer_irq_handler(const void * paramiter)
{
    irq_blt_sdk_handler();
}

void BLEManagerImpl::BleEntry(void *, void *, void *)
{
    while (true)
    {
        blt_sdk_main_loop();

        k_msleep(10);
    }
}

/* Thread for running BLE main loop */
K_THREAD_DEFINE(chipBleThread, CHIP_BLE_THREAD_STACK_SIZE, BLEManagerImpl::BleEntry, NULL, NULL, NULL, CHIP_BLE_THREAD_PRIORITY, 0,
                0);

CHIP_ERROR BLEManagerImpl::_Init()
{
    CHIP_ERROR err = CHIP_NO_ERROR;

    /* Initialize the CHIP BleLayer. */
    err = BleLayer::Init(this, this, &DeviceLayer::SystemLayer());
    SuccessOrExit(err);

    /* Set number of connections to zero */
    mNumConnections = 0;

    /* Unsubscribe all connections */
    memset(mSubscribedConns, 0, sizeof(mSubscribedConns));

    /* Enable CHIP over BLE service */
    mServiceMode = ConnectivityManager::kCHIPoBLEServiceMode_Enabled;

    /* Suspend BLE Task */
    k_thread_suspend(chipBleThread);

exit:
    return err;
}

int BLEManagerImpl::RxWriteCallback(uint16_t connHandle, void * p)
{
    rf_packet_att_t * packet = (rf_packet_att_t *) p;
    size_t dataLen           = packet->l2capLen - CHIP_RF_PACKET_HEADER_SIZE;
    ChipDeviceEvent event;

    PacketBufferHandle packetBuf = PacketBufferHandle::NewWithData(packet->dat, dataLen);

    // If successful...
    if (packetBuf.IsNull())
    {
        ChipLogError(DeviceLayer, "Failed to allocate buffer");

        return 0;
    }

    // Arrange to post a CHIPoBLERXWriteEvent event to the CHIP queue.
    event.Type                                = DeviceEventType::kPlatformTelinkBleRXWrite;
    event.Platform.BleRXWriteEvent.connHandle = connHandle;
    event.Platform.BleRXWriteEvent.Data       = std::move(packetBuf).UnsafeRelease();

    PlatformMgr().PostEventOrDie(&event);

    return 0;
}

void BLEManagerImpl::ConnectCallback(uint8_t bleEvent, uint8_t * data, int len)
{
    ChipDeviceEvent event;
    ble_sts_t status = BLE_SUCCESS;

    PlatformMgr().LockChipStack();

    event.Type                             = DeviceEventType::kPlatformTelinkBleConnected;
    event.Platform.BleConnEvent.connHandle = BLS_CONN_HANDLE;
    event.Platform.BleConnEvent.HciResult  = BLE_SUCCESS;

    PlatformMgr().PostEventOrDie(&event);
    PlatformMgr().UnlockChipStack();
}

void BLEManagerImpl::DisconnectCallback(uint8_t bleEvent, uint8_t * data, int len)
{
    ChipDeviceEvent event;

    PlatformMgr().LockChipStack();

    event.Type                             = DeviceEventType::kPlatformTelinkBleDisconnected;
    event.Platform.BleConnEvent.connHandle = BLS_CONN_HANDLE;
    event.Platform.BleConnEvent.HciResult  = *data; // Reason of disconnection stored in first data byte

    PlatformMgr().PostEventOrDie(&event);
    PlatformMgr().UnlockChipStack();
}

int BLEManagerImpl::TxCccWriteCallback(uint16_t connHandle, void * p)
{
    ChipDeviceEvent event;
    rf_packet_att_t * packet = (rf_packet_att_t *) p;
    int dataLen              = packet->rf_len - CHIP_RF_PACKET_HEADER_SIZE;
    uint16_t value           = *((uint16_t *) packet->dat);

    event.Type                                 = DeviceEventType::kPlatformTelinkBleCCCWrite;
    event.Platform.BleCCCWriteEvent.connHandle = connHandle;
    event.Platform.BleCCCWriteEvent.Value      = value;

    PlatformMgr().PostEventOrDie(&event);

    return 0;
}

int BLEManagerImpl::GapEventHandler(uint32_t gapEvent, uint8_t * data, int size)
{
    ChipDeviceEvent event;

    if ((gapEvent & 0xFF) == GAP_EVT_GATT_HANDLE_VLAUE_CONFIRM)
    {
        /* Send TX complete event if everything is fine */
        event.Type                                   = DeviceEventType::kPlatformTelinkBleTXComplete;
        event.Platform.BleTXCompleteEvent.connHandle = BLS_CONN_HANDLE;

        PlatformMgr().PostEventOrDie(&event);
    }

    return 0;
}

CHIP_ERROR BLEManagerImpl::_InitStack(void)
{
    CHIP_ERROR err = CHIP_NO_ERROR;

    uint8_t macPublic[CHIP_MAC_LEN]       = { 0 };
    uint8_t macRandomStatic[CHIP_MAC_LEN] = { 0 };
    int ret                               = 0;

    if (ConnectivityMgr().IsThreadProvisioned())
    {
        ChipLogProgress(DeviceLayer, "Thread Provisioned. Ignore");

        return CHIP_ERROR_INCORRECT_STATE;
    }

    /* Reset Radio */
    rf_radio_reset();

    /* Reset DMA */
    rf_reset_dma();

    /* Init Radio driver */
    ble_radio_init();

    /* Generate MAC address if it does not exist or read it from flash if it is exist already */
    blc_initMacAddress(CFG_ADR_MAC_1M_FLASH, macPublic, macRandomStatic);

    /* Init interrupts and DMA for BLE module ??? */
    blc_ll_initBasicMCU();

    /* Setup MAC Address */
    blc_ll_initStandby_module(macPublic);

    /* Init advertisement */
    blc_ll_initAdvertising_module();

    /* Init slave role */
    blc_ll_initSlaveRole_module();

    /* Init connection mode */
    blc_ll_initConnection_module();

    /* Init GAP */
    err = _InitGap();
    SuccessOrExit(err);

    /* Resetup stimer interrupt to handle BLE stack */
    ret = irq_connect_dynamic(STIMER_IRQ_NUM, 2, stimer_irq_handler, NULL, 0);
    ChipLogDetail(DeviceLayer, "Stimer IRQ assigned vector %d", ret);

    /* Resetup rf interrupt to handle BLE stack */
    ret = irq_connect_dynamic(RF_IRQ_NUM, 2, rf_irq_handler, NULL, 0);
    ChipLogDetail(DeviceLayer, "RF IRQ assigned vector %d", ret);

exit:

    return err;
}

CHIP_ERROR BLEManagerImpl::_InitGap(void)
{
    ble_sts_t status = BLE_SUCCESS;
    /* Fifo buffers */
    static u8 txFifoBuff[CHIP_BLE_TX_FIFO_SIZE * CHIP_BLE_TX_FIFO_NUM] = { 0 };
    static u8 rxFifoBuff[CHIP_BLE_RX_FIFO_SIZE * CHIP_BLE_RX_FIFO_NUM] = { 0 };

    status = blc_ll_initAclConnTxFifo(txFifoBuff, CHIP_BLE_TX_FIFO_SIZE, CHIP_BLE_TX_FIFO_NUM);
    if (status != BLE_SUCCESS)
    {
        ChipLogError(DeviceLayer, "Fail to init BLE TX FIFO. Error %d", status);

        return CHIP_ERROR_INCORRECT_STATE;
    }

    status = blc_ll_initAclConnRxFifo(rxFifoBuff, CHIP_BLE_RX_FIFO_SIZE, CHIP_BLE_RX_FIFO_NUM);
    if (status != BLE_SUCCESS)
    {
        ChipLogError(DeviceLayer, "Fail to init BLE RX FIFO. Error %d", status);

        return CHIP_ERROR_INCORRECT_STATE;
    }

    status = blc_controller_check_appBufferInitialization();
    if (status != BLE_SUCCESS)
    {
        ChipLogError(DeviceLayer, "Buffer initialization check failed. Error %d", status);

        return CHIP_ERROR_INCORRECT_STATE;
    }

    /* Init GAP */
    blc_gap_peripheral_init();

    /* Set up GATT Services */
    _InitGatt();

    /* L2CAP Initialization */
    blc_l2cap_register_handler((void *) blc_l2cap_packet_receive);

    /* Setup connect/terminate callbacks */
    bls_app_registerEventCallback(BLT_EV_FLAG_CONNECT, BLEManagerImpl::ConnectCallback);
    bls_app_registerEventCallback(BLT_EV_FLAG_TERMINATE, BLEManagerImpl::DisconnectCallback);

    /* Add GAP event handler to handle indication send */
    blc_gap_registerHostEventHandler(BLEManagerImpl::GapEventHandler);
    blc_gap_setEventMask(GAP_EVT_MASK_GATT_HANDLE_VLAUE_CONFIRM);

    /* Set MTU */
    status = blc_att_setRxMtuSize(CHIP_MTU_SIZE);
    if (status != BLE_SUCCESS)
    {
        ChipLogError(DeviceLayer, "Fail to set MTU size. Error %d", status);

        return CHIP_ERROR_INCORRECT_STATE;
    }

    return CHIP_NO_ERROR;
}

void BLEManagerImpl::_InitGatt(void)
{
    /* UUIDs */
    static const u16 primaryServiceUUID     = GATT_UUID_PRIMARY_SERVICE;
    static const u16 gapServiceUUID         = SERVICE_UUID_GENERIC_ACCESS;
    static const u16 characterUUID          = GATT_UUID_CHARACTER;
    static const u16 devNameUUID            = GATT_UUID_DEVICE_NAME;
    static const u16 gattServiceUUID        = SERVICE_UUID_GENERIC_ATTRIBUTE;
    static const u16 serviceChangeUUID      = GATT_UUID_SERVICE_CHANGE;
    static const u16 clientCharacterCfgUUID = GATT_UUID_CLIENT_CHAR_CFG;
    static const u16 devServiceUUID         = SERVICE_UUID_DEVICE_INFORMATION;
    static const u16 appearanceUUID         = GATT_UUID_APPEARANCE;
    static const u16 periConnParamUUID      = GATT_UUID_PERI_CONN_PARAM;
    static const u8 MatterRxCharUUID[]      = WRAPPING_BRACES(CHIP_RX_CHAR_UUID);
    static const u8 MatterTxCharUUID[]      = WRAPPING_BRACES(CHIP_TX_CHAR_UUID);

    /* Characteristics */
    static const u8 devNameCharVal[] = { CHAR_PROP_READ | CHAR_PROP_NOTIFY, U16_LO(GenericAccess_DeviceName_DP_H),
                                         U16_HI(GenericAccess_DeviceName_DP_H), U16_LO(GATT_UUID_DEVICE_NAME),
                                         U16_HI(GATT_UUID_DEVICE_NAME) };

    static const u8 appearanceCharVal[] = { CHAR_PROP_READ, U16_LO(GenericAccess_Appearance_DP_H),
                                            U16_HI(GenericAccess_Appearance_DP_H), U16_LO(GATT_UUID_APPEARANCE),
                                            U16_HI(GATT_UUID_APPEARANCE) };

    static const u8 periConnParamCharVal[] = { CHAR_PROP_READ, U16_LO(CONN_PARAM_DP_H), U16_HI(CONN_PARAM_DP_H),
                                               U16_LO(GATT_UUID_PERI_CONN_PARAM), U16_HI(GATT_UUID_PERI_CONN_PARAM) };

    static const u8 serviceChangeCharVal[] = { CHAR_PROP_INDICATE, U16_LO(GenericAttribute_ServiceChanged_DP_H),
                                               U16_HI(GenericAttribute_ServiceChanged_DP_H), U16_LO(GATT_UUID_SERVICE_CHANGE),
                                               U16_HI(GATT_UUID_SERVICE_CHANGE) };

    static const u8 MatterRxCharVal[] = { CHAR_PROP_WRITE | CHAR_PROP_WRITE_WITHOUT_RSP, U16_LO(Matter_RX_DP_H),
                                          U16_HI(Matter_RX_DP_H), CHIP_RX_CHAR_UUID };

    static const u8 MatterTxCharVal[] = { CHAR_PROP_INDICATE, U16_LO(Matter_TX_DP_H), U16_HI(Matter_TX_DP_H), CHIP_TX_CHAR_UUID };

    /* Values */
    static const u16 appearance                             = GAP_APPEARE_UNKNOWN;
    static const gap_periConnectParams_t periConnParameters = { 8, 11, 0, 1000 };
    static u16 serviceChangeVal[2]                          = { 0 };
    static u8 serviceChangeCCC[2]                           = { 0 };
    static u8 matterTxCCC[2]                                = { 0 };

    static const attribute_t gattTable[] = {
        /* Total number of attributes */
        { ATT_END_H - 1, 0, 0, 0, 0, 0 },

        /* 0001 - 0007  GAP service */
        { 7, ATT_PERMISSIONS_READ, 2, 2, (u8 *) (&primaryServiceUUID), (u8 *) (&gapServiceUUID), 0 },
        { 0, ATT_PERMISSIONS_READ, 2, sizeof(devNameCharVal), (u8 *) (&characterUUID), (u8 *) (devNameCharVal), 0 },
        { 0, ATT_PERMISSIONS_READ, 2, (u32) kMaxDeviceNameLength, (u8 *) (&devNameUUID), (u8 *) (mDeviceName), 0 },
        { 0, ATT_PERMISSIONS_READ, 2, sizeof(appearanceCharVal), (u8 *) (&characterUUID), (u8 *) (appearanceCharVal), 0 },
        { 0, ATT_PERMISSIONS_READ, 2, sizeof(appearance), (u8 *) (&appearanceUUID), (u8 *) (&appearance), 0 },
        { 0, ATT_PERMISSIONS_READ, 2, sizeof(periConnParamCharVal), (u8 *) (&characterUUID), (u8 *) (periConnParamCharVal), 0 },
        { 0, ATT_PERMISSIONS_READ, 2, sizeof(periConnParameters), (u8 *) (&periConnParamUUID), (u8 *) (&periConnParameters), 0 },

        /* 0008 - 000b GATT */
        { 4, ATT_PERMISSIONS_READ, 2, 2, (u8 *) (&primaryServiceUUID), (u8 *) (&gattServiceUUID), 0 },
        { 0, ATT_PERMISSIONS_READ, 2, sizeof(serviceChangeCharVal), (u8 *) (&characterUUID), (u8 *) (serviceChangeCharVal), 0 },
        { 0, ATT_PERMISSIONS_READ, 2, sizeof(serviceChangeVal), (u8 *) (&serviceChangeUUID), (u8 *) (&serviceChangeVal), 0 },
        { 0, ATT_PERMISSIONS_RDWR, 2, sizeof(serviceChangeCCC), (u8 *) (&clientCharacterCfgUUID), (u8 *) (serviceChangeCCC), 0 },

        /* 000c - 0011 Matter service */
        { 6, ATT_PERMISSIONS_READ, 2, 2, (u8 *) (&primaryServiceUUID), (u8 *) (&matterServiceUUID), 0 },
        { 0, ATT_PERMISSIONS_READ, 2, sizeof(MatterRxCharVal), (u8 *) (&characterUUID), (u8 *) (MatterRxCharVal), 0 },
        { 0, ATT_PERMISSIONS_RDWR, 16, sizeof(mRxDataBuff), (u8 *) (&MatterRxCharUUID), mRxDataBuff, RxWriteCallback, NULL },
        { 0, ATT_PERMISSIONS_READ, 2, sizeof(MatterTxCharVal), (u8 *) (&characterUUID), (u8 *) (MatterTxCharVal), 0 },
        { 0, ATT_PERMISSIONS_RDWR, 16, sizeof(mTxDataBuff), (u8 *) (&MatterTxCharUUID), mTxDataBuff, 0 },
        { 0, ATT_PERMISSIONS_RDWR, 2, sizeof(matterTxCCC), (u8 *) (&clientCharacterCfgUUID), (u8 *) (matterTxCCC),
          TxCccWriteCallback, NULL }
    };

    bls_att_setAttributeTable((u8 *) gattTable);
}

CHIP_ERROR _Shutdown(void)
{
    ChipLogProgress(DeviceLayer, "BLEManagerImpl::_Shutdown");

    return CHIP_NO_ERROR;
}

CHIP_ERROR BLEManagerImpl::_SetCHIPoBLEServiceMode(CHIPoBLEServiceMode val)
{
    return CHIP_NO_ERROR;
}

CHIP_ERROR BLEManagerImpl::ConfigureAdvertisingData(void)
{
    ble_sts_t status   = BLE_SUCCESS;
    CHIP_ERROR err     = CHIP_NO_ERROR;
    uint8_t index      = 0;
    uint8_t devNameLen = 0;
    ChipBLEDeviceIdentificationInfo deviceIdInfo;
    u8 adv[CHIP_MAX_ADV_DATA_LEN]       = { 0 };
    u8 srsp[CHIP_MAX_RESPONSE_DATA_LEN] = { 0 };

    ChipLogProgress(DeviceLayer, "BLEManagerImpl::ConfigureAdvertisingData");

    /* Get BLE device identification info */
    err = ConfigurationMgr().GetBLEDeviceIdentificationInfo(deviceIdInfo);
    SuccessOrExit(err);

    /* Check device name */
    if (!mFlags.Has(Flags::kDeviceNameSet))
    {
        err = _SetDeviceName("TelinkMatter");
        SuccessOrExit(err);
    }

    /* Fulfill BLE advertisement data */
    /* Set flags */
    adv[index++] = CHIP_ADE_DATA_LEN_FLAGS;
    adv[index++] = CHIP_ADV_DATA_TYPE_FLAGS;
    adv[index++] = CHIP_ADV_DATA_FLAGS;

    /* Set Service Data */
    adv[index++] = CHIP_ADV_SERVICE_DATA_LEN;
    adv[index++] = CHIP_ADV_DATA_TYPE_SERVICE_DATA;
    adv[index++] = matterServiceUUID[0];
    adv[index++] = matterServiceUUID[1];
    memcpy(&adv[index], (void *) &deviceIdInfo, sizeof(deviceIdInfo));
    index += sizeof(deviceIdInfo);

    /* Set device name */
    devNameLen   = strlen(mDeviceName);
    adv[index++] = devNameLen + 1;
    adv[index++] = CHIP_ADV_DATA_TYPE_NAME;
    memcpy(&adv[index], mDeviceName, devNameLen);
    index += devNameLen;

    /* Set advetisment data */
    status = bls_ll_setAdvData(adv, index);
    if (status != BLE_SUCCESS)
    {
        ChipLogError(DeviceLayer, "Fail to set BLE advertisement data. Error %d", status);

        return CHIP_ERROR_INCORRECT_STATE;
    }

    index         = 0;
    srsp[index++] = CHIP_SHORT_UUID_LEN + 1;
    srsp[index++] = CHIP_ADV_DATA_TYPE_UUID;
    srsp[index++] = matterServiceUUID[0];
    srsp[index++] = matterServiceUUID[1];

    /* Set scan response data */
    status = bls_ll_setScanRspData(srsp, sizeof(srsp));
    if (status != BLE_SUCCESS)
    {
        ChipLogError(DeviceLayer, "Fail to set BLE scan response data. Error %d", status);

        return CHIP_ERROR_INCORRECT_STATE;
    }

exit:
    return err;
}

CHIP_ERROR BLEManagerImpl::_SetAdvertisingEnabled(bool val)
{
    CHIP_ERROR err = CHIP_NO_ERROR;

    if (val)
    {
        err = StartAdvertising();
    }
    else
    {
        err = StopAdvertising();
    }

    return err;
}

CHIP_ERROR BLEManagerImpl::StartAdvertising(void)
{
    CHIP_ERROR err   = CHIP_NO_ERROR;
    ble_sts_t status = BLE_SUCCESS;

    /* At first run always select fast advertising, on the next attempt slow down interval. */
    u16 intervalMin = mFlags.Has(Flags::kFastAdvertisingEnabled) ? CHIP_DEVICE_CONFIG_BLE_FAST_ADVERTISING_INTERVAL_MIN
                                                                 : CHIP_DEVICE_CONFIG_BLE_SLOW_ADVERTISING_INTERVAL_MIN;
    u16 intervalMax = mFlags.Has(Flags::kFastAdvertisingEnabled) ? CHIP_DEVICE_CONFIG_BLE_FAST_ADVERTISING_INTERVAL_MAX
                                                                 : CHIP_DEVICE_CONFIG_BLE_SLOW_ADVERTISING_INTERVAL_MAX;

    if (ConnectivityMgr().IsThreadProvisioned())
    {
        ChipLogProgress(DeviceLayer, "Thread provisioned. Start advertisement not possible");

        return CHIP_ERROR_INCORRECT_STATE;
    }

    /* Block IEEE802154 */
    /* @todo: move to RadioSwitch module*/
    const struct device * radio_dev = device_get_binding(CONFIG_NET_CONFIG_IEEE802154_DEV_NAME);
    __ASSERT(radio_dev != NULL, "Fail to get radio device");
    b91_ieee802154_deinit(radio_dev);

    /* It is time to init BLE stack */
    err = _InitStack();
    if (err != CHIP_NO_ERROR)
    {
        ChipLogError(DeviceLayer, "Fail to init BLE stack");

        return err;
    }

    /* Configure CHIP BLE advertisement data */
    err = ConfigureAdvertisingData();
    if (err != CHIP_NO_ERROR)
    {
        ChipLogError(DeviceLayer, "Fail to config BLE advertisement data");

        return err;
    }

    /* Setup advertisement parameters */
    status = bls_ll_setAdvParam(intervalMin, intervalMax, ADV_TYPE_CONNECTABLE_UNDIRECTED, OWN_ADDRESS_PUBLIC, 0, NULL,
                                BLT_ENABLE_ADV_ALL, ADV_FP_NONE);
    if (status != BLE_SUCCESS)
    {
        ChipLogError(DeviceLayer, "Fail to set BLE advertisement parameters. Error %d", status);

        return CHIP_ERROR_INCORRECT_STATE;
    }

    /* Enable advertisement */
    status = bls_ll_setAdvEnable(BLC_ADV_ENABLE);
    if (status != BLE_SUCCESS)
    {
        ChipLogError(DeviceLayer, "Fail to start BLE advertisement. Error %d", status);

        return CHIP_ERROR_INCORRECT_STATE;
    }

    mFlags.Set(Flags::kAdvertising);

    /* Start BLE Task */
    k_thread_resume(chipBleThread);

    return err;
}

CHIP_ERROR BLEManagerImpl::StopAdvertising(void)
{
    ble_sts_t status = BLE_SUCCESS;

    if (ConnectivityMgr().IsThreadProvisioned())
    {
        ChipLogProgress(DeviceLayer, "Thread provisioned. Advertisement already stopped at this stage");

        return CHIP_NO_ERROR;
    }

    /* Disable advertisement */
    status = bls_ll_setAdvEnable(BLC_ADV_DISABLE);
    if (status != BLE_SUCCESS)
    {
        ChipLogError(DeviceLayer, "Fail to stop BLE advertisement. Error %d", status);

        return CHIP_ERROR_INCORRECT_STATE;
    }

    mFlags.Clear(Flags::kAdvertising);

    return CHIP_NO_ERROR;
}

/// @todo: implementation
CHIP_ERROR BLEManagerImpl::_SetAdvertisingMode(BLEAdvertisingMode mode)
{
    return CHIP_NO_ERROR;
}

CHIP_ERROR BLEManagerImpl::_GetDeviceName(char * buf, size_t bufSize)
{
    if (strlen(mDeviceName) >= bufSize)
    {
        return CHIP_ERROR_BUFFER_TOO_SMALL;
    }
    strcpy(buf, mDeviceName);

    return CHIP_NO_ERROR;
}

CHIP_ERROR BLEManagerImpl::_SetDeviceName(const char * devName)
{

    if (mServiceMode == ConnectivityManager::kCHIPoBLEServiceMode_NotSupported)
    {
        ChipLogError(DeviceLayer, "Unsupported");

        return CHIP_ERROR_UNSUPPORTED_CHIP_FEATURE;
    }

    if (devName == NULL && devName[0] == 0)
    {
        ChipLogError(DeviceLayer, "Invalid name");

        return CHIP_ERROR_INVALID_ARGUMENT;
    }

    if (strlen(devName) >= kMaxDeviceNameLength)
    {
        ChipLogError(DeviceLayer, "BLE device name is to long");

        return CHIP_ERROR_INVALID_ARGUMENT;
    }

    strcpy(mDeviceName, devName);
    mFlags.Set(Flags::kDeviceNameSet);

    ChipLogProgress(DeviceLayer, "Setting device name to : \"%s\"", devName);

    return CHIP_NO_ERROR;
}

void BLEManagerImpl::_OnPlatformEvent(const ChipDeviceEvent * event)
{
    CHIP_ERROR err = CHIP_NO_ERROR;

    switch (event->Type)
    {
    case DeviceEventType::kPlatformTelinkBleConnected:
        err = HandleGAPConnect(event);
        break;

    case DeviceEventType::kPlatformTelinkBleDisconnected:
        err = HandleGAPDisconnect(event);
        break;

    case DeviceEventType::kPlatformTelinkBleDisconnectRequest:
        err = HandleDisconnectRequest(event);
        break;

    case DeviceEventType::kPlatformTelinkBleRXWrite:
        err = HandleRXCharWrite(event);
        break;

    case DeviceEventType::kPlatformTelinkBleCCCWrite:
        err = HandleTXCharCCCDWrite(event);
        break;

    case DeviceEventType::kPlatformTelinkBleTXComplete:
        err = HandleTXCharComplete(event);
        break;

    case DeviceEventType::kThreadStateChange:
        err = HandleThreadStateChange(event);
        break;

    case DeviceEventType::kCHIPoBLEConnectionClosed:
        err = HandleBleConnectionClosed(event);
        break;

    case DeviceEventType::kOperationalNetworkEnabled:
        err = HandleOperationalNetworkEnabled(event);
        break;

    default:
        ChipLogDetail(DeviceLayer, "Event: Unknown (0x%04x)", event->Type);
    }

    if (err != CHIP_NO_ERROR)
    {
        ChipLogError(DeviceLayer, "Fail to handle 0x%04x event. Error: %s", event->Type, ErrorStr(err));
    }
}

bool BLEManagerImpl::SubscribeCharacteristic(BLE_CONNECTION_OBJECT conId, const ChipBleUUID * svcId, const ChipBleUUID * charId)
{
    return false;
}

bool BLEManagerImpl::UnsubscribeCharacteristic(BLE_CONNECTION_OBJECT conId, const ChipBleUUID * svcId, const ChipBleUUID * charId)
{
    return false;
}

bool BLEManagerImpl::CloseConnection(BLE_CONNECTION_OBJECT conId)
{
    return false;
}

uint16_t BLEManagerImpl::GetMTU(BLE_CONNECTION_OBJECT conId) const
{
    return blc_att_getEffectiveMtuSize(conId);
}

bool BLEManagerImpl::SendIndication(BLE_CONNECTION_OBJECT conId, const ChipBleUUID * svcId, const ChipBleUUID * charId,
                                    PacketBufferHandle pBuf)
{
    ble_sts_t status = BLE_SUCCESS;

    status = blc_gatt_pushHandleValueIndicate(conId, Matter_TX_DP_H, pBuf->Start(), pBuf->DataLength());
    if (status != BLE_SUCCESS)
    {
        ChipLogError(DeviceLayer, "Fail to send indication. Error %d", status);

        return false;
    }

    return true;
}

bool BLEManagerImpl::SendWriteRequest(BLE_CONNECTION_OBJECT conId, const ChipBleUUID * svcId, const ChipBleUUID * charId,
                                      PacketBufferHandle pBuf)
{
    ChipLogProgress(DeviceLayer, "BLEManagerImpl::SendWriteRequest() not supported");
    return false;
}

bool BLEManagerImpl::SendReadRequest(BLE_CONNECTION_OBJECT conId, const ChipBleUUID * svcId, const ChipBleUUID * charId,
                                     PacketBufferHandle pBuf)
{
    ChipLogProgress(DeviceLayer, "BLEManagerImpl::SendReadRequest() not supported");
    return false;
}

bool BLEManagerImpl::SendReadResponse(BLE_CONNECTION_OBJECT conId, BLE_READ_REQUEST_CONTEXT requestContext,
                                      const ChipBleUUID * svcId, const ChipBleUUID * charId)
{
    ChipLogProgress(DeviceLayer, "BLEManagerImpl::SendReadResponse() not supported");
    return false;
}

/* @todo: move to RadioSwitch module */
void BLEManagerImpl::SwitchToIeee802154(void)
{
    int result = 0;

    ChipLogProgress(DeviceLayer, "BLEManagerImpl::Switch to IEEE802154");

    /* Stop BLE */
    StopAdvertising();

    /* Stop BLE task */
    k_thread_suspend(chipBleThread);

    /* Reset Radio */
    rf_radio_reset();

    /* Reset DMA */
    rf_reset_dma();

    const struct device * radio_dev = device_get_binding(CONFIG_NET_CONFIG_IEEE802154_DEV_NAME);
    __ASSERT(radio_dev != NULL, "Fail to get radio device");

    /* Init IEEE802154 */
    result = b91_ieee802154_init(radio_dev);
    __ASSERT(result == 0, "Fail to init IEEE802154 radio. Error: %d", result);
}

void BLEManagerImpl::NotifyChipConnectionClosed(BLE_CONNECTION_OBJECT conId) {}

uint16_t BLEManagerImpl::_NumConnections(void)
{
    return mNumConnections;
}

/// @todo implement multicinnection subscription
CHIP_ERROR BLEManagerImpl::SetSubscribed(uint16_t conId)
{

    mSubscribedConns[0] = true;

    return CHIP_NO_ERROR;
}

/// @todo implement multicinnection subscription
bool BLEManagerImpl::UnsetSubscribed(uint16_t conId)
{

    mSubscribedConns[0] = false;

    return true;
}

/// @todo implement multicinnection subscription
bool BLEManagerImpl::IsSubscribed(uint16_t conId)
{
    return mSubscribedConns[0];
}

CHIP_ERROR BLEManagerImpl::HandleGAPConnect(const ChipDeviceEvent * event)
{
    const BleConnEventType * connEvent = &event->Platform.BleConnEvent;

    /* Increase number of connections */
    mNumConnections++;

    ChipLogProgress(DeviceLayer, "BLE connection established (ConnId: 0x%02x)", connEvent->connHandle);
    ChipLogProgress(DeviceLayer, "Current number of connections: %u/%u", NumConnections(), kMaxConnections);

    return CHIP_NO_ERROR;
}

CHIP_ERROR BLEManagerImpl::HandleGAPDisconnect(const ChipDeviceEvent * event)
{
    const BleConnEventType * connEvent = &event->Platform.BleConnEvent;
    ChipDeviceEvent disconnectEvent;

    /* Decrease number of connections */
    mNumConnections--;

    ChipLogProgress(DeviceLayer, "BLE GAP connection terminated (reason 0x%02x)", connEvent->HciResult);
    ChipLogProgress(DeviceLayer, "Current number of connections: %u/%u", NumConnections(), kMaxConnections);

    /* Unsubscribe */
    if (UnsetSubscribed(connEvent->connHandle))
    {
        HandleUnsubscribeReceived(connEvent->connHandle, &CHIP_BLE_SVC_ID, &chipUUID_CHIPoBLEChar_TX);
    }

    /* Send Connection close event */
    disconnectEvent.Type = DeviceEventType::kCHIPoBLEConnectionClosed;
    ReturnErrorOnFailure(PlatformMgr().PostEvent(&disconnectEvent));

    return CHIP_NO_ERROR;
}

CHIP_ERROR BLEManagerImpl::HandleDisconnectRequest(const ChipDeviceEvent * event)
{
    ble_sts_t status = BLE_SUCCESS;
    uint16_t handle  = event->Platform.BleConnEvent.connHandle;
    uint8_t reason   = event->Platform.BleConnEvent.HciResult;

    ChipLogDetail(DeviceLayer, "HandleDisconnectRequest");

    /* Trigger disconnect. DisconnectCallback call occures on completion */
    status = blc_ll_disconnect(handle, reason);
    if (status != BLE_SUCCESS && status != LL_ERR_CONNECTION_NOT_ESTABLISH)
    {
        ChipLogError(DeviceLayer, "Fail to disconnect. Error %d", status);

        return CHIP_ERROR_INCORRECT_STATE;
    }

    return CHIP_NO_ERROR;
}

CHIP_ERROR BLEManagerImpl::HandleOperationalNetworkEnabled(const ChipDeviceEvent * event)
{
    ChipDeviceEvent disconnectEvent;

    ChipLogDetail(DeviceLayer, "HandleOperationalNetworkEnabled");

    disconnectEvent.Type                             = DeviceEventType::kPlatformTelinkBleDisconnectRequest;
    disconnectEvent.Platform.BleConnEvent.connHandle = BLS_CONN_HANDLE;
    disconnectEvent.Platform.BleConnEvent.HciResult  = CHIP_BLE_DISCONNECT_REASON;
    ReturnErrorOnFailure(PlatformMgr().PostEvent(&disconnectEvent));

    return CHIP_NO_ERROR;
}

CHIP_ERROR BLEManagerImpl::HandleThreadStateChange(const ChipDeviceEvent * event)
{
    CHIP_ERROR error = CHIP_NO_ERROR;

    ChipLogDetail(DeviceLayer, "HandleThreadStateChange");

    if (event->Type == DeviceEventType::kThreadStateChange && event->ThreadStateChange.RoleChanged)
    {
        ChipDeviceEvent attachEvent;
        attachEvent.Type                            = DeviceEventType::kThreadConnectivityChange;
        attachEvent.ThreadConnectivityChange.Result = kConnectivity_Established;

        error = PlatformMgr().PostEvent(&attachEvent);
        VerifyOrExit(error == CHIP_NO_ERROR,
                     ChipLogError(DeviceLayer, "Failed to post Thread connectivity change: %" CHIP_ERROR_FORMAT, error.Format()));
    }

exit:
    return error;
}

CHIP_ERROR BLEManagerImpl::HandleBleConnectionClosed(const ChipDeviceEvent * event)
{
    /* It is time to swich to IEEE802154 radio if it is provisioned */
    if (ConnectivityMgr().IsThreadProvisioned())
    {
        SwitchToIeee802154();
    }

    return CHIP_NO_ERROR;
}

CHIP_ERROR BLEManagerImpl::HandleRXCharWrite(const ChipDeviceEvent * event)
{
    const BleRXWriteEventType * writeEvent = &event->Platform.BleRXWriteEvent;

    ChipLogDetail(DeviceLayer, "Write request received for CHIPoBLE RX (ConnId 0x%02x)", writeEvent->connHandle);

    HandleWriteReceived(writeEvent->connHandle, &CHIP_BLE_SVC_ID, &chipUUID_CHIPoBLEChar_RX,
                        PacketBufferHandle::Adopt(writeEvent->Data));

    return CHIP_NO_ERROR;
}

CHIP_ERROR BLEManagerImpl::HandleTXCharComplete(const ChipDeviceEvent * event)
{
    const BleTXCompleteEventType * completeEvent = &event->Platform.BleTXCompleteEvent;

    ChipLogDetail(DeviceLayer, "Notification for CHIPoBLE TX done (ConnId 0x%02x)", completeEvent->connHandle);

    // Signal the BLE Layer that the outstanding notification is complete.
    HandleIndicationConfirmation(completeEvent->connHandle, &CHIP_BLE_SVC_ID, &chipUUID_CHIPoBLEChar_TX);

    return CHIP_NO_ERROR;
}

CHIP_ERROR BLEManagerImpl::HandleTXCharCCCDWrite(const ChipDeviceEvent * event)
{
    const BleCCCWriteEventType * writeEvent = &event->Platform.BleCCCWriteEvent;

    ChipLogDetail(DeviceLayer, "ConnId: 0x%02x, New CCCD value: 0x%04x", writeEvent->connHandle, writeEvent->Value);

    /* If the client has requested to enable notifications and if it is not yet subscribed */
    if (writeEvent->Value != 0 && SetSubscribed(writeEvent->connHandle) == CHIP_NO_ERROR)
    {
        /* Alert the BLE layer that CHIPoBLE "subscribe" has been received and increment the bt_conn reference counter. */
        HandleSubscribeReceived(writeEvent->connHandle, &CHIP_BLE_SVC_ID, &chipUUID_CHIPoBLEChar_TX);

        ChipLogProgress(DeviceLayer, "CHIPoBLE connection established (ConnId: 0x%02x, GATT MTU: %d)", writeEvent->connHandle,
                        GetMTU(writeEvent->connHandle));

        /* Post a CHIPoBLEConnectionEstablished event to the DeviceLayer and the application. */
        {
            ChipDeviceEvent conEstEvent;
            conEstEvent.Type = DeviceEventType::kCHIPoBLEConnectionEstablished;
            ReturnErrorOnFailure(PlatformMgr().PostEvent(&conEstEvent));
        }
    }
    else
    {
        if (UnsetSubscribed(writeEvent->connHandle))
        {
            HandleUnsubscribeReceived(writeEvent->connHandle, &CHIP_BLE_SVC_ID, &chipUUID_CHIPoBLEChar_TX);
        }
    }

    return CHIP_NO_ERROR;
}

} // namespace Internal
} // namespace DeviceLayer
} // namespace chip

#endif // CHIP_DEVICE_CONFIG_ENABLE_CHIPOBLE
