/********************************** (C) COPYRIGHT *******************************
 * File Name          : peripheral.C
 * Author             : WCH
 * Version            : V1.0
 * Date               : 2018/12/10
 * Description        : 外设从机多连接应用程序，初始化广播连接参数，然后广播，连接主机后，
 *                      请求更新连接参数，通过自定义服务传输数据
 *********************************************************************************
 * Copyright (c) 2021 Nanjing Qinheng Microelectronics Co., Ltd.
 * Attention: This software (modified or not) and binary are used for
 * microcontroller manufactured by Nanjing Qinheng Microelectronics.
 *******************************************************************************/

/*********************************************************************
 * INCLUDES
 */
#include "CONFIG.h"
#include "devinfoservice.h"
#include "gattprofile.h"
#include "peripheral.h"

#include "bsp_valve.h"

#include "bsp_uart.h"
#include "log.h"

#include "bsp_i2c.h"
#include "bsp_adc.h"

#include "bsp_beep_led_emv.h"

#include "SLEEP.h"

#undef LOG_ENABLE
#define LOG_ENABLE 1

#undef LOG_TAG
#define LOG_TAG "peripheral"

volatile uint8_t mtu_flag = 0;

static volatile bool periodic_upload_block_flag = false;

void BSP_NeeedReplyCMdFirst(void)
{
    periodic_upload_block_flag = true;
}

void BSP_NoNeeedReplyCMd(void)
{
    periodic_upload_block_flag = false;
}

// How often to perform periodic event
#define SBP_PERIODIC_EVT_PERIOD (1600 * 60) // (160 = 100ms)

// How often to perform read rssi event
#define SBP_READ_RSSI_EVT_PERIOD (1600 * 3) // (160 = 100ms)

// Parameter update delay
#define SBP_PARAM_UPDATE_DELAY (1600 * 1)

// PHY update delay
#define SBP_PHY_UPDATE_DELAY (1600 * 2)

// What is the advertising interval when device is discoverable (units of 625us, 80=50ms)
#define DEFAULT_ADVERTISING_INTERVAL (160 * 8)

// Limited discoverable mode advertises for 30.72s, and then stops
// General discoverable mode advertises indefinitely
#define DEFAULT_DISCOVERABLE_MODE GAP_ADTYPE_FLAGS_GENERAL

// Minimum connection interval (units of 1.25ms, 6=7.5ms)
#define DEFAULT_DESIRED_MIN_CONN_INTERVAL (80 * 4) // (80 = 100ms)

// Maximum connection interval (units of 1.25ms, 100=125ms)
#define DEFAULT_DESIRED_MAX_CONN_INTERVAL (80 * 5)

// Slave latency to use parameter update
#define DEFAULT_DESIRED_SLAVE_LATENCY 0

// Supervision timeout value (units of 10ms, 100=1s)
#define DEFAULT_DESIRED_CONN_TIMEOUT (100 * 2)

// Company Identifier: WCH
#define WCH_COMPANY_ID 0x07D7

#define MAC_NAME       "TYQ-93:B4:8F:10:53:5C"
// TYQ-93:B4:8F:10:53:5C
#define MAC_NAME_LEN 22

uint8_t Peripheral_TaskID = INVALID_TASK_ID; // Task ID for internal task/event processing

// 蓝牙广播包的最大长度是37个字节，其中设备地址占用了6个字节，只有31个字节是可用的。
// TODO:响应体，名称加上MAC地址
// GAP - SCAN RSP data (max size = 31 bytes)
static uint8_t scanRspData[] = {
    // complete name
    0x12, // length of this data
    GAP_ADTYPE_LOCAL_NAME_COMPLETE,
    'B',
    'L',
    'E',
    'T',
    'Y',
    'Q',
    ' ',
    'P',
    'e',
    'r',
    'i',
    'p',
    'h',
    'e',
    'r',
    'a',
    'l',
    // connection interval range
    0x05, // length of this data
    GAP_ADTYPE_SLAVE_CONN_INTERVAL_RANGE,
    LO_UINT16(DEFAULT_DESIRED_MIN_CONN_INTERVAL), // 100ms
    HI_UINT16(DEFAULT_DESIRED_MIN_CONN_INTERVAL),
    LO_UINT16(DEFAULT_DESIRED_MAX_CONN_INTERVAL), // 1s
    HI_UINT16(DEFAULT_DESIRED_MAX_CONN_INTERVAL),

    // Tx power level
    0x02, // length of this data
    GAP_ADTYPE_POWER_LEVEL,
    0 // 0dBm
};

// GAP - Advertisement data (max size = 31 bytes, though this is
// best kept short to conserve power while advertising)
static uint8_t advertData[] = {
    // Flags; this sets the device to use limited discoverable
    // mode (advertises for 30 seconds at a time) instead of general
    // discoverable mode (advertises indefinitely)
    0x02, // length of this data
    GAP_ADTYPE_FLAGS,
    DEFAULT_DISCOVERABLE_MODE | GAP_ADTYPE_FLAGS_BREDR_NOT_SUPPORTED,

    // service UUID, to notify central devices what services are included
    // in this peripheral
    0x03,                  // length of this data
    GAP_ADTYPE_16BIT_MORE, // some of the UUID's, but not all
    LO_UINT16(SIMPLEPROFILE_SERV_UUID),
    HI_UINT16(SIMPLEPROFILE_SERV_UUID)};

// GAP GATT Attributes
static uint8_t attDeviceName[GAP_DEVICE_NAME_LEN] = "BLE_TYQ";

// Connection item list
static peripheralConnItem_t peripheralConnList;

static uint16_t peripheralMTU = ATT_MTU_SIZE;
/*********************************************************************
 * LOCAL FUNCTIONS
 */
static void Peripheral_ProcessTMOSMsg(tmos_event_hdr_t *pMsg);
static void peripheralStateNotificationCB(gapRole_States_t newState, gapRoleEvent_t *pEvent);
static void performPeriodicTask(void);
static void simpleProfileChangeCB(uint8_t paramID, uint8_t *pValue, uint16_t len);
static void peripheralParamUpdateCB(uint16_t connHandle, uint16_t connInterval,
                                    uint16_t connSlaveLatency, uint16_t connTimeout);
static void peripheralInitConnItem(peripheralConnItem_t *peripheralConnList);
static void peripheralRssiCB(uint16_t connHandle, int8_t rssi);
static void peripheralChar4Notify(uint8_t *pValue, uint16_t len);

/*********************************************************************
 * PROFILE CALLBACKS
 */

// GAP Role Callbacks
static gapRolesCBs_t Peripheral_PeripheralCBs = {
    peripheralStateNotificationCB, // Profile State Change Callbacks
    peripheralRssiCB,              // When a valid RSSI is read from controller (not used by application)
    peripheralParamUpdateCB};

// Broadcast Callbacks
static gapRolesBroadcasterCBs_t Broadcaster_BroadcasterCBs = {
    NULL, // Not used in peripheral role
    NULL  // Receive scan request callback
};

// GAP Bond Manager Callbacks
static gapBondCBs_t Peripheral_BondMgrCBs = {
    NULL, // Passcode callback (not used by application)
    NULL, // Pairing / Bonding state Callback (not used by application)
    NULL  // oob callback
};

// Simple GATT Profile Callbacks
static simpleProfileCBs_t Peripheral_SimpleProfileCBs = {
    simpleProfileChangeCB // Characteristic value change callback
};
/*********************************************************************
 * PUBLIC FUNCTIONS
 */

/*********************************************************************
 * @fn      Peripheral_Init
 *
 * @brief   Initialization function for the Peripheral App Task.
 *          This is called during initialization and should contain
 *          any application specific initialization (ie. hardware
 *          initialization/setup, table initialization, power up
 *          notificaiton ... ).
 *
 * @param   task_id - the ID assigned by TMOS.  This ID should be
 *                    used to send messages and set timers.
 *
 * @return  none
 */
void Peripheral_Init()
{
    Peripheral_TaskID = TMOS_ProcessEventRegister(Peripheral_ProcessEvent);

    // Setup the GAP Peripheral Role Profile
    {
        // 开启广播使能
        uint8_t initial_advertising_enable = TRUE;
        // 最小连接间隔
        uint16_t desired_min_interval = DEFAULT_DESIRED_MIN_CONN_INTERVAL;
        // 最大连接间隔
        uint16_t desired_max_interval = DEFAULT_DESIRED_MAX_CONN_INTERVAL;

        // Set the GAP Role Parameters
        GAPRole_SetParameter(GAPROLE_ADVERT_ENABLED, sizeof(uint8_t), &initial_advertising_enable);
        GAPRole_SetParameter(GAPROLE_SCAN_RSP_DATA, sizeof(scanRspData), scanRspData);
        GAPRole_SetParameter(GAPROLE_ADVERT_DATA, sizeof(advertData), advertData);

        GAPRole_SetParameter(GAPROLE_MIN_CONN_INTERVAL, sizeof(uint16_t), &desired_min_interval);
        GAPRole_SetParameter(GAPROLE_MAX_CONN_INTERVAL, sizeof(uint16_t), &desired_max_interval);
    }

    {
        uint16_t advInt = DEFAULT_ADVERTISING_INTERVAL;

        // Set advertising interval
        GAP_SetParamValue(TGAP_DISC_ADV_INT_MIN, advInt);
        GAP_SetParamValue(TGAP_DISC_ADV_INT_MAX, advInt);

        // https://www.cnblogs.com/debugdabiaoge/p/17871551.html
        // 默认从机在回复扫描请求后，会结束广播事件（默认广播应该是 37 38  39 在 3 个信道轮发）
        // 如下使能后，可以保证广播包不受扫描请求的影响
        // uint16_t adv_event_contnue=1<<1;
        // GAP_SetParamValue(TGAP_ADV_SCAN_REQ_NOTIFY, adv_event_contnue);

        // Enable scan req notify
        GAP_SetParamValue(TGAP_ADV_SCAN_REQ_NOTIFY, ENABLE);
    }

    // Setup the GAP Bond Manager
    {
        uint32_t passkey = 0; // passkey "000000"
        uint8_t pairMode = GAPBOND_PAIRING_MODE_WAIT_FOR_REQ;
        uint8_t mitm     = TRUE;
        uint8_t bonding  = TRUE;
        uint8_t ioCap    = GAPBOND_IO_CAP_DISPLAY_ONLY;
        GAPBondMgr_SetParameter(GAPBOND_PERI_DEFAULT_PASSCODE, sizeof(uint32_t), &passkey);
        GAPBondMgr_SetParameter(GAPBOND_PERI_PAIRING_MODE, sizeof(uint8_t), &pairMode);
        GAPBondMgr_SetParameter(GAPBOND_PERI_MITM_PROTECTION, sizeof(uint8_t), &mitm);
        GAPBondMgr_SetParameter(GAPBOND_PERI_IO_CAPABILITIES, sizeof(uint8_t), &ioCap);
        GAPBondMgr_SetParameter(GAPBOND_PERI_BONDING_ENABLED, sizeof(uint8_t), &bonding);
    }

    // Initialize GATT attributes
    GGS_AddService(GATT_ALL_SERVICES);           // GAP
    GATTServApp_AddService(GATT_ALL_SERVICES);   // GATT attributes
    DevInfo_AddService();                        // Device Information Service
    SimpleProfile_AddService(GATT_ALL_SERVICES); // Simple GATT Profile

    // Set the GAP Characteristics
    GGS_SetParameter(GGS_DEVICE_NAME_ATT, GAP_DEVICE_NAME_LEN, attDeviceName);

    // Setup the SimpleProfile Characteristic Values
    {
        uint8_t charValue1[SIMPLEPROFILE_CHAR1_LEN] = {0};
        uint8_t charValue2[SIMPLEPROFILE_CHAR2_LEN] = {0};
        uint8_t charValue3[SIMPLEPROFILE_CHAR3_LEN] = {0};
        uint8_t charValue4[SIMPLEPROFILE_CHAR4_LEN] = {0};
        uint8_t charValue5[SIMPLEPROFILE_CHAR5_LEN] = {0, 0, 0, 0, 0};

        SimpleProfile_SetParameter(SIMPLEPROFILE_CHAR1, SIMPLEPROFILE_CHAR1_LEN, charValue1);
        SimpleProfile_SetParameter(SIMPLEPROFILE_CHAR2, SIMPLEPROFILE_CHAR2_LEN, charValue2);
        SimpleProfile_SetParameter(SIMPLEPROFILE_CHAR3, SIMPLEPROFILE_CHAR3_LEN, charValue3);
        SimpleProfile_SetParameter(SIMPLEPROFILE_CHAR4, SIMPLEPROFILE_CHAR4_LEN, charValue4);
        SimpleProfile_SetParameter(SIMPLEPROFILE_CHAR5, SIMPLEPROFILE_CHAR5_LEN, charValue5);
    }

    // Init Connection Item
    peripheralInitConnItem(&peripheralConnList);

    // Register callback with SimpleGATTprofile
    SimpleProfile_RegisterAppCBs(&Peripheral_SimpleProfileCBs);

    // Register receive scan request callback
    GAPRole_BroadcasterSetCB(&Broadcaster_BroadcasterCBs);

    // Setup a delayed profile startup
    tmos_set_event(Peripheral_TaskID, SBP_START_DEVICE_EVT);

    // https://www.cnblogs.com/ZYL-FS/p/18061479
    GATT_InitClient();
}

/*********************************************************************
 * @fn      peripheralInitConnItem
 *
 * @brief   Init Connection Item
 *
 * @param   peripheralConnList -
 *
 * @return  NULL
 */
static void peripheralInitConnItem(peripheralConnItem_t *peripheralConnList)
{
    peripheralConnList->connHandle       = GAP_CONNHANDLE_INIT;
    peripheralConnList->connInterval     = 0;
    peripheralConnList->connSlaveLatency = 0;
    peripheralConnList->connTimeout      = 0;
}

/*********************************************************************
 * @fn      Peripheral_ProcessEvent
 *
 * @brief   Peripheral Application Task event processor.  This function
 *          is called to process all events for the task.  Events
 *          include timers, messages and any other user defined events.
 *
 * @param   task_id - The TMOS assigned task ID.
 * @param   events - events to process.  This is a bit map and can
 *                   contain more than one event.
 *
 * @return  events not processed
 */
uint16_t Peripheral_ProcessEvent(uint8_t task_id, uint16_t events)
{
    //  VOID task_id; // TMOS required parameter that isn't used in this function

    if (events & SYS_EVENT_MSG)
    {
        uint8_t *pMsg;

        if ((pMsg = tmos_msg_receive(Peripheral_TaskID)) != NULL)
        {
            Peripheral_ProcessTMOSMsg((tmos_event_hdr_t *)pMsg);
            // Release the TMOS message
            tmos_msg_deallocate(pMsg);
        }
        // return unprocessed events
        return (events ^ SYS_EVENT_MSG);
    }

    if (events & SBP_START_DEVICE_EVT)
    {
        // Start the Device
        GAPRole_PeripheralStartDevice(Peripheral_TaskID, &Peripheral_BondMgrCBs, &Peripheral_PeripheralCBs);

        return (events ^ SBP_START_DEVICE_EVT);
    }
    if (events & SBP_REPLY_CMD_EVT)
    {
        logDebug("SBP_REPLY_CMD_EVT");

        peripheralChar4Notify((uint8_t *)&RelyData.buf[0], RelyData.len);

        BSP_NoNeeedReplyCMd();
        return (events ^ SBP_REPLY_CMD_EVT);
    }
    if (events & SBP_PERIODIC_EVT)
    {
        // 防止主机下发指令，从机回复响应的时候，先传的是状态数据
        if (periodic_upload_block_flag == false && mtu_flag == 1)
        {
            // Perform periodic application task
            performPeriodicTask();
            // Restart timer
            tmos_start_task(Peripheral_TaskID, SBP_PERIODIC_EVT, SBP_PERIODIC_EVT_PERIOD);
        }
        else
        {
            // 需要等先回复了下发的指令，1s 之后再尝试回复状态数据
            tmos_start_task(Peripheral_TaskID, SBP_PERIODIC_EVT, MS1_TO_SYSTEM_TIME(1000));
        }
        return (events ^ SBP_PERIODIC_EVT);
    }

    if (events & SBP_PARAM_UPDATE_EVT)
    {
        // Send connect param update request
        GAPRole_PeripheralConnParamUpdateReq(peripheralConnList.connHandle,
                                             DEFAULT_DESIRED_MIN_CONN_INTERVAL,
                                             DEFAULT_DESIRED_MAX_CONN_INTERVAL,
                                             DEFAULT_DESIRED_SLAVE_LATENCY,
                                             DEFAULT_DESIRED_CONN_TIMEOUT,
                                             Peripheral_TaskID);

        return (events ^ SBP_PARAM_UPDATE_EVT);
    }

    if (events & SBP_PHY_UPDATE_EVT)
    {
        // start phy update
        logDebug("PHY Update %x...", GAPRole_UpdatePHY(peripheralConnList.connHandle, 0,
                                                       GAP_PHY_BIT_LE_2M, GAP_PHY_BIT_LE_2M, 0));

        return (events ^ SBP_PHY_UPDATE_EVT);
    }

    if (events & SBP_READ_RSSI_EVT)
    {
        GAPRole_ReadRssiCmd(peripheralConnList.connHandle);
        tmos_start_task(Peripheral_TaskID, SBP_READ_RSSI_EVT, SBP_READ_RSSI_EVT_PERIOD);
        return (events ^ SBP_READ_RSSI_EVT);
    }

    // Discard unknown events
    return 0;
}

/*********************************************************************
 * @fn      Peripheral_ProcessGAPMsg
 *
 * @brief   Process an incoming task message.
 *
 * @param   pMsg - message to process
 *
 * @return  none
 */
static void Peripheral_ProcessGAPMsg(gapRoleEvent_t *pEvent)
{
    switch (pEvent->gap.opcode)
    {
        case GAP_SCAN_REQUEST_EVENT:
        {
            logDebug("Receive scan req from %x %x %x %x %x %x  ..", pEvent->scanReqEvt.scannerAddr[0],
                     pEvent->scanReqEvt.scannerAddr[1], pEvent->scanReqEvt.scannerAddr[2], pEvent->scanReqEvt.scannerAddr[3],
                     pEvent->scanReqEvt.scannerAddr[4], pEvent->scanReqEvt.scannerAddr[5]);
            break;
        }

        case GAP_PHY_UPDATE_EVENT:
        {
            logDebug("Phy update Rx:%x Tx:%x ..", pEvent->linkPhyUpdate.connRxPHYS, pEvent->linkPhyUpdate.connTxPHYS);
            break;
        }

        default:
            break;
    }
}

/*********************************************************************
 * @fn      Peripheral_ProcessTMOSMsg
 *
 * @brief   Process an incoming task message.
 *
 * @param   pMsg - message to process
 *
 * @return  none
 */
static void Peripheral_ProcessTMOSMsg(tmos_event_hdr_t *pMsg)
{
    switch (pMsg->event)
    {
        case GAP_MSG_EVENT:
        {
            Peripheral_ProcessGAPMsg((gapRoleEvent_t *)pMsg);
            break;
        }

        case GATT_MSG_EVENT:
        {
            gattMsgEvent_t *pMsgEvent;

            pMsgEvent = (gattMsgEvent_t *)pMsg;
            if (pMsgEvent->method == ATT_MTU_UPDATED_EVENT)
            {
                peripheralMTU = pMsgEvent->msg.exchangeMTUReq.clientRxMTU;
                mtu_flag      = 1;
                logDebug("****mtu exchange: %d****", pMsgEvent->msg.exchangeMTUReq.clientRxMTU);
                // TODO：连上之后先给个状态回应
                // tmos_set_event(vavle_task_id, VAVLE_UPDATE_EVT);
                performPeriodicTask();
                tmos_set_event(led_task_id, CONNECT_BEEP_START_EVT);
            }
            break;
        }

        default:
            break;
    }
}

/*********************************************************************
 * @fn      Peripheral_LinkEstablished
 *
 * @brief   Process link established.
 *
 * @param   pEvent - event to process
 *
 * @return  none
 */
static void Peripheral_LinkEstablished(gapRoleEvent_t *pEvent)
{
    gapEstLinkReqEvent_t *event = (gapEstLinkReqEvent_t *)pEvent;

    // See if already connected
    if (peripheralConnList.connHandle != GAP_CONNHANDLE_INIT)
    {
        GAPRole_TerminateLink(pEvent->linkCmpl.connectionHandle);
        logDebug("Connection max...");
    }
    else
    {
        peripheralConnList.connHandle       = event->connectionHandle;
        peripheralConnList.connInterval     = event->connInterval;
        peripheralConnList.connSlaveLatency = event->connLatency;
        peripheralConnList.connTimeout      = event->connTimeout;
        peripheralMTU                       = ATT_MTU_SIZE;
        // peripheralMTU = BLE_BUFF_MAX_LEN - 4;
        // Set timer for periodic event
        tmos_start_task(Peripheral_TaskID, SBP_PERIODIC_EVT, SBP_PERIODIC_EVT_PERIOD);

        // Set timer for param update event
        tmos_start_task(Peripheral_TaskID, SBP_PARAM_UPDATE_EVT, SBP_PARAM_UPDATE_DELAY);

        // Start read rssi
        tmos_start_task(Peripheral_TaskID, SBP_READ_RSSI_EVT, SBP_READ_RSSI_EVT_PERIOD);

        // https://www.cnblogs.com/ZYL-FS/p/17759138.html
        // 使能通知
        enable_notify(peripheralConnList.connHandle, ENABLE);

        logDebug("Conn %x - Int %x= %.1fms", event->connectionHandle, event->connInterval, event->connInterval * 1.25);
    }
}

/*********************************************************************
 * @fn      Peripheral_LinkTerminated
 *
 * @brief   Process link terminated.
 *
 * @param   pEvent - event to process
 *
 * @return  none
 */
static void Peripheral_LinkTerminated(gapRoleEvent_t *pEvent)
{
    gapTerminateLinkEvent_t *event = (gapTerminateLinkEvent_t *)pEvent;

    if (event->connectionHandle == peripheralConnList.connHandle)
    {
        peripheralConnList.connHandle       = GAP_CONNHANDLE_INIT;
        peripheralConnList.connInterval     = 0;
        peripheralConnList.connSlaveLatency = 0;
        peripheralConnList.connTimeout      = 0;
        tmos_stop_task(Peripheral_TaskID, SBP_PERIODIC_EVT);
        tmos_stop_task(Peripheral_TaskID, SBP_READ_RSSI_EVT);

        // Restart advertising
        {
            // 三个广播通道全部打开
            // uint8_t init_adv_channel_map = GAP_ADVCHAN_ALL ;
            // GAPRole_SetParameter( GAPROLE_ADV_CHANNEL_MAP, sizeof( uint8_t ), &init_adv_channel_map);

            mtu_flag = 0;

            uint8_t advertising_enable = TRUE;
            GAPRole_SetParameter(GAPROLE_ADVERT_ENABLED, sizeof(uint8_t), &advertising_enable);
        }
    }
    else
    {
        logError("ERR..");
    }
}

/*********************************************************************
 * @fn      peripheralRssiCB
 *
 * @brief   RSSI callback.
 *
 * @param   connHandle - connection handle
 * @param   rssi - RSSI
 *
 * @return  none
 */
static void peripheralRssiCB(uint16_t connHandle, int8_t rssi)
{
    gValveData.rssi = rssi;
    logDebug("RSSI %d dB Conn  %x ",  gValveData.rssi, connHandle);
}

/*********************************************************************
 * @fn      peripheralParamUpdateCB
 *
 * @brief   Parameter update complete callback
 *
 * @param   connHandle - connect handle
 *          connInterval - connect interval
 *          connSlaveLatency - connect slave latency
 *          connTimeout - connect timeout
 *
 * @return  none
 */
static void peripheralParamUpdateCB(uint16_t connHandle, uint16_t connInterval,
                                    uint16_t connSlaveLatency, uint16_t connTimeout)
{
    if (connHandle == peripheralConnList.connHandle)
    {
        peripheralConnList.connInterval     = connInterval;
        peripheralConnList.connSlaveLatency = connSlaveLatency;
        peripheralConnList.connTimeout      = connTimeout;

        logDebug("ParamUpdateCB (Handle)%x - (connInt 0x%x=%.1fms) - (connSlaveLatency) %x - (connTimeout 0x%x=%dms) ", connHandle, connInterval, connInterval * 1.25, connSlaveLatency, connTimeout * 10, connTimeout * 10);
    }
    else
    {
        logError("ERR..");
    }
}

/*********************************************************************
 * @fn      peripheralStateNotificationCB
 *
 * @brief   Notification from the profile of a state change.
 *
 * @param   newState - new state
 *
 * @return  none
 */
static void peripheralStateNotificationCB(gapRole_States_t newState, gapRoleEvent_t *pEvent)
{
    switch (newState & GAPROLE_STATE_ADV_MASK)
    {
        case GAPROLE_STARTED:
            logDebug("Initialized..");
            break;

        case GAPROLE_ADVERTISING:
            if (pEvent->gap.opcode == GAP_LINK_TERMINATED_EVENT)
            {
                Peripheral_LinkTerminated(pEvent);
                logDebug("Disconnected.. Reason:%x", pEvent->linkTerminate.reason);
                logDebug("Advertising..");
            }
            else if (pEvent->gap.opcode == GAP_MAKE_DISCOVERABLE_DONE_EVENT)
            {
                logDebug("Advertising..");
            }
            break;

        case GAPROLE_CONNECTED:
            if (pEvent->gap.opcode == GAP_LINK_ESTABLISHED_EVENT)
            {
                Peripheral_LinkEstablished(pEvent);
                logDebug("Connected..");

                // 在蓝牙建立连接之后，调用 GATT ExchangeMTU 来修改 MTU 的值
                // Update MTU
                attExchangeMTUReq_t req = {
                    .clientRxMTU = BLE_BUFF_MAX_LEN - 4,
                };
                GATT_ExchangeMTU(peripheralConnList.connHandle, &req, Peripheral_TaskID);
                // Peripheral_SetMacName();
                // BSP_BlockSleep();
                // BSP_RequestBoost();
                // DelayMs(1);
                // // ShowLed();
                // BEEP_ON;
                // DelayMs(500);
            }
            break;

        case GAPROLE_CONNECTED_ADV:
            if (pEvent->gap.opcode == GAP_MAKE_DISCOVERABLE_DONE_EVENT)
            {
                logDebug("Connected Advertising..");
            }
            break;

        case GAPROLE_WAITING:
            if (pEvent->gap.opcode == GAP_END_DISCOVERABLE_DONE_EVENT)
            {
                logDebug("Waiting for advertising..");
            }
            else if (pEvent->gap.opcode == GAP_LINK_TERMINATED_EVENT)
            {
                Peripheral_LinkTerminated(pEvent);
                logDebug("Disconnected.. Reason:%x", pEvent->linkTerminate.reason);
            }
            else if (pEvent->gap.opcode == GAP_LINK_ESTABLISHED_EVENT)
            {
                if (pEvent->gap.hdr.status != SUCCESS)
                {
                    logDebug("Waiting for advertising..");
                }
                else
                {
                    logError("Err..");
                }
            }
            else
            {
                logError("Err..%x", pEvent->gap.opcode);
            }
            break;

        case GAPROLE_ERROR:
            logError("Err..");
            break;

        default:
            break;
    }
}

/*********************************************************************
 * @fn      performPeriodicTask
 *
 * @brief   Perform a periodic application task. This function gets
 *          called every five seconds as a result of the SBP_PERIODIC_EVT
 *          TMOS event. In this example, the value of the third
 *          characteristic in the SimpleGATTProfile service is retrieved
 *          from the profile, and then copied into the value of the
 *          the fourth characteristic.
 *
 * @param   none
 *
 * @return  none
 */
static void performPeriodicTask(void)
{
    gValveData.bat = BSP_ReadVbat();

#if 0
    BSP_BlockSleep();
    BSP_RequestBoost();
    // DelayMs(1);
    ShowLed();

    DelayMs(2);
    LED_ALL_OFF_DEINIT;
    // BEEP_OFF_DEINIT;

    BSP_NoNeedBoost();
    BSP_RequestSleep();
#endif
    // 电压过低就关阀
    if (gValveData.bat <= LOW_VABAT_CLOSE_VALUE)
    {
        gValveData.switch_status = kClosed;
        tmos_set_event(vavle_task_id, VAVLE_CLOSE_START_EVT);
    }
    else if (gValveData.bat <= LOW_VABAT_ALARM_VALUE)
    {
        tmos_set_event(vavle_task_id, VAVLE_LOW_VBAT_ALARM_EVT);
    }

    float _humi, _temp;
    if (BSP_ReadTempHumi(&_humi, &_temp) == 0)
    {
        gValveData.temp = (int8_t)_temp;
        gValveData.humi = (uint8_t)_humi;
    }
    else
    {
        logError("Read TempHumi Err");
        return;
    }
    #if 1
    logDebug("switch_status:%d Temp:%d Bat:%d Humi:%d%%  RSSI: %d"
        , gValveData.switch_status, gValveData.temp, gValveData.bat, gValveData.humi, gValveData.rssi);

    #endif
    TsRawFrameData RawData;

    BSP_VALVE_Generate_UploadData(&RawData);

    peripheralChar4Notify(&RawData.buf[0], RawData.len);
}

/*********************************************************************
 * @fn      peripheralChar4Notify
 *
 * @brief   Prepare and send simpleProfileChar4 notification
 *
 * @param   pValue - data to notify
 *          len - length of data
 *
 * @return  none
 */
static void peripheralChar4Notify(uint8_t *pValue, uint16_t len)
{
    #if 1
    attHandleValueNoti_t noti;
    if (len > (peripheralMTU - 3))
    {
        logDebug("Too large noti");
        return;
    }
    noti.len    = len;
    noti.pValue = GATT_bm_alloc(peripheralConnList.connHandle, ATT_HANDLE_VALUE_NOTI, noti.len, NULL, 0);
    if (noti.pValue)
    {
        tmos_memcpy(noti.pValue, pValue, noti.len);
        if (simpleProfile_Notify(peripheralConnList.connHandle, &noti) != SUCCESS)
        {
            logError("Noti Err");
            GATT_bm_free((gattMsg_t *)&noti, ATT_HANDLE_VALUE_NOTI);
        }
        else
        {
            logDebug("Noti Success");
        }
    }
    #endif
}

/*********************************************************************
 * @fn      simpleProfileChangeCB
 *
 * @brief   Callback from SimpleBLEProfile indicating a value change
 *
 * @param   paramID - parameter ID of the value that was changed.
 *          pValue - pointer to data that was changed
 *          len - length of data
 *
 * @return  none
 */
static void simpleProfileChangeCB(uint8_t paramID, uint8_t *pValue, uint16_t len)
{
    // tmos_event_hdr_t *test_message;
    // TsRawFrameData RawData;

    BSP_NeeedReplyCMdFirst();

    switch (paramID)
    {
        case SIMPLEPROFILE_CHAR1:
        {
            uint8_t newValue[SIMPLEPROFILE_CHAR1_LEN];
            tmos_memcpy(newValue, pValue, len);
            logDebug("profile ChangeCB CHAR1.. Start");
            for (uint8_t i = 0; i < len; i++)
            {
                logDebug("%02x ", newValue[i]);
                // BSP_UART1_TxLoop();
            }
            logDebug("\n profile ChangeCB CHAR1.. End");

            // test_message =(tmos_event_hdr_t*) tmos_msg_allocate(sizeof(tmos_event_hdr_t));
            // BSP_UART1_TxLoop();

            break;
        }

        case SIMPLEPROFILE_CHAR3:
        {
            // uint8_t newValue[SIMPLEPROFILE_CHAR3_LEN];
            // tmos_memset(newValue, 0, sizeof(newValue));
            // tmos_memcpy(newValue, pValue, len);
            logDebug("CHAR3 Start");
            // logHexDumpAll(newValue, len);
            // TODO:发送rev数据
            uint8_t *p_rev_msg;
            // TsValveMsg *ValveMsg;
            // ValveMsg = tmos_msg_allocate(len);
            // ValveMsg.data = tmos_msg_allocate(len);
            p_rev_msg = tmos_msg_allocate(len + 1);
            if ( p_rev_msg )
            {
                p_rev_msg[0] = len;
                tmos_memcpy(&p_rev_msg[1], pValue, len);
                int ret = tmos_msg_send(vavle_task_id, p_rev_msg);
                if (ret == SUCCESS)
                {
                    logDebug("p_rev_msg send ret = %d", ret);
                }
                else
                {
                    logError("p_rev_msg send ret = %d", ret);
                }
            }
            else
            {
                logError("内存不足");
            }
#if 0
            TsFrameData *HostFrameData = BSP_VAVLE_GetFrameData(newValue, len);
            if (HostFrameData != NULL)
            {
                // logHexDumpAll(&HostFrameData->data[0], HostFrameData->len); // 数据段
                // BSP_UART1_TxLoop();
                // HR_ProcessData(HostFrameData);
                switch (HostFrameData->cmd)
                {
                    case kCmdCfg:
                        // uint8_t data_buf[64] = {0};
                        // tmos_memset(data_buf, 0, sizeof(data_buf));
                        // 处理数据帧
                        logDebug("kCmdCfg");
                        break;
                    case kCmdCloseVavle:
                        logDebug("kCmdCloseVavle");

                        // TODO:注意这里
                        BSP_NeeedReplyCMdFirst();

                        // EMV_CHARGE
                        EMV_CHARGE_EN;
                        logDebug("EMV_CHARGE_EN");

                        // 开始一个定时event,1s后产生,当前语句只会产生一次event
                        // 可以在event产生后去开启event,可以是别的task的,也可以是当前task的event
                        tmos_start_task(vavle_task_id, VAVLE_CLOSE_ACTION_EVT, MS1_TO_SYSTEM_TIME(CHARGE_TIME_MS));

                        // BSP_VALVE_Generate_ValveResponse(&RawData, kCmdCloseVavle, 1);
                        // // TODO: 关阀动作
                        // peripheralChar4Notify((uint8_t *)&RawData.buf[0], RawData.len);
                        break;

                    case kCmdOpenVavle:
                        logDebug("kCmdOpenVavle");
                        BSP_VALVE_Generate_ValveResponse(&RawData, kCmdOpenVavle, 1);
                        peripheralChar4Notify((uint8_t *)&RawData.buf[0], RawData.len);
                        // BSP_UART1_TxLoop();
                        break;
                    default:
                        logError("无效的命令");
                        //                    logHexDumpAll(data, len);
                        break;
                }
            }
            else
            {
                logError("数据帧解析失败");
            }
            tmos_msg_deallocate((uint8_t *)HostFrameData);
            HostFrameData = NULL;
#endif
            logDebug("profile ChangeCB CHAR3.. End");
            break;
        }

        default:
            // should not reach here!
            break;
    }
}

#if 0


// TODO:应用加入广播数据更新部分状态数据
// 动态更新广播数据
// taskID： 请求更改广播任务的 ID
// adType：TRUE 更改广播数据，FALSE 更改扫描回复数据
// datalen：数据长度
// *pAdvertData：数据指针
void Peripheral_UpdateAdvertData(uint8_t adType, uint8_t dataLen, uint8_t *pAdvertData)
{
    GAP_UpdateAdvertisingData(Peripheral_TaskID, adType, dataLen, pAdvertData);
}

#include "stdio.h"
#include "stdarg.h"

// TODO:先不弄，后面改广播名，应用加入广播数据更新部分状态数据
void Peripheral_SetMacName(void)
{
    uint8_t mac_addr[6];
    GetMACAddress(mac_addr);
    // 手机APP显示是倒序的[5-0],不是[0-5]
    logDebug("MacAddr: %02X:%02X:%02X:%02X:%02X:%02X", mac_addr[0], mac_addr[1]
        , mac_addr[2], mac_addr[3], mac_addr[4], mac_addr[5]);

    // GAPRole_SetParameter(GAPROLE_ADVERT_DATA, sizeof(advertData), advertData);

    uint8_t scan_rsp_data[128];
    uint8_t len = 0;
    uint8_t name_len;
    // complete name
    scan_rsp_data[1] = GAP_ADTYPE_LOCAL_NAME_COMPLETE;
    name_len = sprintf(&scan_rsp_data[2], "TYQ-%02X:%02X:%02X:%02X:%02X:%02X", mac_addr[0], mac_addr[1]
        , mac_addr[2], mac_addr[3], mac_addr[4], mac_addr[5]);
    len = name_len + 1;
    scan_rsp_data[0] = len;
    logDebug("len:%d", len);
    // connection interval range
    scan_rsp_data[len++] = 0x05;
    scan_rsp_data[len++] = LO_UINT16(DEFAULT_DESIRED_MIN_CONN_INTERVAL);
    scan_rsp_data[len++] = HI_UINT16(DEFAULT_DESIRED_MIN_CONN_INTERVAL);
    scan_rsp_data[len++] = LO_UINT16(DEFAULT_DESIRED_MAX_CONN_INTERVAL);
    scan_rsp_data[len++] = HI_UINT16(DEFAULT_DESIRED_MAX_CONN_INTERVAL);

    // Tx power level
    scan_rsp_data[len++] = 0x02;
    scan_rsp_data[len++] = GAP_ADTYPE_POWER_LEVEL;
    scan_rsp_data[len] = 0;
    logDebug("len:%d", len);

    // logDebug("scan_rsp_data:");
    logHexDumpAll(scan_rsp_data, len);

    // // 更新扫描响应体
    GAP_UpdateAdvertisingData(Peripheral_TaskID, FALSE, scan_rsp_data, len);
}
#endif
