/*
 * Copyright (C) 2021 Huawei Device Co., Ltd.
 * 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.
 */
#include "avctp_br_act.h"
#include "avctp_st.h"
#include "avctp_conn.h"
#include "avctp_gap.h"
#include "avctp_ctrl.h"
#include "avctp_ctrl_act.h"
#include "avctp_br.h"
#include "l2cap_if.h"
#include "log.h"

/*****************************************************************************
 * Globle Data  Define
 ****************************************************************************/
/*
 * Function     AvctCbBrChConn
 * Description  This function is called to Open browser channel link.
 * Param[in]    *cbDev  The point to the device block.
 * Param[in]    *data  Event data
 * Return       AVCT_SUCCESS. Others: Fail
 */
uint16_t AvctCbBrChConn(AvctCbDev *cbDev, const AvctEvtData *data)
{
    LOG_DEBUG("[AVCT] %{public}s:", __func__);
    GAP_Service serviceId;
    /* Request servcie security */
    if (g_avctMng.role == AVCT_CT) {
        serviceId = AVRCP_CT_BROWSING;
    } else {
        serviceId = AVRCP_TG_BROWSING;
    }
    AvctRequestSecurity(cbDev, serviceId, AVCT_BR_PSM);
    return AVCT_SUCCESS;
}

/*
 * Function     AvctCbBrChConnFail
 * Description  This function is called when Open browser channel link failed.
 * Param[in]    *cbDev  The point to the device block.
 * Param[in]    *data  Event data
 * Return       AVCT_SUCCESS. Others: Fail
 */
uint16_t AvctCbBrChConnFail(AvctCbDev *cbDev, const AvctEvtData *data)
{
    LOG_DEBUG("[AVCT] %{public}s:", __func__);
    AvctCbConn *cbConn = NULL;
    for (uint8_t i = 0; i < AVCT_MAX_CONNECTS; i++) {
        cbConn = &g_avctMng.cbConn[i];
        if ((cbConn->status == AVCT_CONN_BIND) && (cbConn->cbDev == cbDev)) {
            if (cbDev->chLink & AVCT_ALLOC_BR) {
                AvctCbConnEvtCallback(cbConn, AVCT_BR_CONNECT_CFM_EVT, data->result, &(cbConn->cbDev->peerAddr));
            }
        }
    }
    AvctCbChDealloc(&(cbDev->cbBr));
    cbDev->chLink &= ~AVCT_ALLOC_BR;
    return AVCT_SUCCESS;
}

/*
 * Function     AvctCbBrChBind
 * Description  This function is called when the browser channel had been
 *              created, only bind it.
 * Param[in]    *cbDev  The point to the device block.
 * Param[in]    *data  Event data
 * Return       AVCT_SUCCESS. Others: Fail
 */
uint16_t AvctCbBrChBind(AvctCbDev *cbDev, const AvctEvtData *data)
{
    LOG_DEBUG("[AVCT] %{public}s:", __func__);
    /* Send callback event to app */
    AvctCbConnEvtCallback(data->cbConn, AVCT_BR_CONNECT_CFM_EVT, AVCT_SUCCESS, &(data->cbConn->cbDev->peerAddr));
    return AVCT_SUCCESS;
}

/*
 * Function     AvctCbBrChBindFail
 * Description  This function is called when the browser channel had been
 *              created, try to bind it failed.
 * Param[in]    *cbDev  The point to the device block.
 * Param[in]    *data  Event data
 * Return       AVCT_SUCCESS. Others: Fail
 */
uint16_t AvctCbBrChBindFail(AvctCbDev *cbDev, const AvctEvtData *data)
{
    LOG_DEBUG("[AVCT] %{public}s:", __func__);
    /* send connect cfm event ,result failed */
    AvctCbConnEvtCallback(data->cbConn, AVCT_BR_CONNECT_CFM_EVT, AVCT_FAILED, &(data->cbConn->cbDev->peerAddr));
    return AVCT_SUCCESS;
}

/*
 * Function     AvctCbBrChDisconn
 * Description  This function is called to disconnect the browser channel .
 * Param[in]    *cbDev  The point to the device block.
 * Param[in]    *data  Event data
 * Return       AVCT_SUCCESS. Others: Fail
 */
uint16_t AvctCbBrChDisconn(AvctCbDev *cbDev, const AvctEvtData *data)
{
    LOG_DEBUG("[AVCT] %{public}s:", __func__);
    L2CIF_DisconnectionReq(cbDev->cbBr->chId, NULL);
    return AVCT_SUCCESS;
}

/*
 * Function     AvctCbBrChUnbind
 * Description  This function is called to unbind and disconnect the browser
 *              channel.
 * Param[in]    *cbDev  The point to the device block.
 * Param[in]    *data  Event data
 * Return       AVCT_SUCCESS. Others: Fail
 */
uint16_t AvctCbBrChUnbind(AvctCbDev *cbDev, const AvctEvtData *data)
{
    LOG_DEBUG("[AVCT] %{public}s:", __func__);
    /* send disconnect cfm event ,result failed */
    AvctCbConnEvtCallback(data->cbConn, AVCT_BR_DISCONNECT_CFM_EVT, AVCT_SUCCESS, &(data->cbConn->cbDev->peerAddr));
    return AVCT_SUCCESS;
}

/*
 * Function     AvctCbBrChCheckDisconn
 * Description  This function is called to check if the connect is the last one which is using the br channel.
 *              If it is the last one, disconnect the channel.
 * Param[in]    *cbDev  The point to the device block.
 * Param[in]    *data  Event data
 * Return       AVCT_SUCCESS. Others: Fail
 */
uint16_t AvctCbBrChCheckDisconn(AvctCbDev *cbDev, const AvctEvtData *data)
{
    LOG_DEBUG("[AVCT] %{public}s:", __func__);
    /* check if the connect is the last one which is using the br channel */
    if (cbDev->bindCnt > 1) {
        /* unbind */
        AvctCbBrChUnbind(cbDev, data);
    } else {
        /* if the browser channel is exit, disconnect too */
        if (cbDev->cbBr != NULL) {
            AvctCbBrEvt(cbDev, AVCT_DISCONN_EVT, data);
        }
    }
    return AVCT_SUCCESS;
}

/*
 * Function     AvctCbBrChConnInd
 * Description  This function is called when receive connect ind.
 * Param[in]    *cbDev  The point to the device block.
 * Param[in]    *data  Event data
 * Return       AVCT_SUCCESS. Others: Fail
 */
uint16_t AvctCbBrChConnInd(AvctCbDev *cbDev, const AvctEvtData *data)
{
    LOG_DEBUG("[AVCT] %{public}s:", __func__);
    AvctCbConn *cbConn = NULL;
    bool bind = false;
    /* find the conn for the connection */
    for (uint8_t i = 0; i < AVCT_MAX_CONNECTS; i++) {
        cbConn = &g_avctMng.cbConn[i];
        if ((cbConn->status == AVCT_CONN_BIND) && (cbConn->cbDev == cbDev)) {
            /* find the connection to the device and send cfm event to app */
            bind = true;
            if (cbDev->cbBr->ia == AVCT_ACPT) {
                AvctCbConnEvtCallback(cbConn, AVCT_BR_CONNECT_IND_EVT, AVCT_SUCCESS, &(cbConn->cbDev->peerAddr));
            } else {
                AvctCbConnEvtCallback(cbConn, AVCT_BR_CONNECT_CFM_EVT, AVCT_SUCCESS, &(cbConn->cbDev->peerAddr));
            }
        }
    }
    /* can not bind to conn ,send disconnect */
    if (!bind) {
        AvctCbBrEvt(cbDev, AVCT_DISCONN_EVT, data);
    }
    return AVCT_SUCCESS;
}

/*
 * Function     AvctCbBrChCloseInd
 * Description  This function is called when receive close ind.
 * Param[in]    *cbDev  The point to the device block.
 * Param[in]    *data  Event data
 * Return       AVCT_SUCCESS. Others: Fail
 */
uint16_t AvctCbBrChCloseInd(AvctCbDev *cbDev, const AvctEvtData *data)
{
    LOG_DEBUG("[AVCT] %{public}s:", __func__);
    AvctCbConn *cbConn = NULL;
    /* find conn which connected to the device, send callback event,relase memory */
    for (uint8_t i = 0; i < AVCT_MAX_CONNECTS; i++) {
        if (g_avctMng.cbConn[i].cbDev == cbDev) {
            cbConn = &g_avctMng.cbConn[i];
            /* Send AVCT_DISCONNECT_IND_EVT */
            AvctCbConnEvtCallback(cbConn, AVCT_BR_DISCONNECT_IND_EVT, AVCT_SUCCESS, &(cbConn->cbDev->peerAddr));
        }
    }
    AvctCbChDealloc(&(cbDev->cbBr));
    cbDev->chLink &= ~AVCT_ALLOC_BR;
    return AVCT_SUCCESS;
}

/*
 * Function     AvctCbBrChCloseCfm
 * Description  This function is called when receive close confirm.
 * Param[in]    *cbDev  The point to the device block.
 * Param[in]    *data  Event data
 * Return       AVCT_SUCCESS. Others: Fail
 */
uint16_t AvctCbBrChCloseCfm(AvctCbDev *cbDev, const AvctEvtData *data)
{
    LOG_DEBUG("[AVCT] %{public}s:", __func__);
    AvctCbConn *cbConn = NULL;
    for (uint8_t i = 0; i < AVCT_MAX_CONNECTS; i++) {
        cbConn = &g_avctMng.cbConn[i];
        if ((cbConn->status == AVCT_CONN_BIND) && (cbConn->cbDev == cbDev)) {
            /* Send AVCT_BR_DISCONNECT_CFM_EVT */
            AvctCbConnEvtCallback(cbConn, AVCT_BR_DISCONNECT_CFM_EVT, AVCT_SUCCESS, &(cbConn->cbDev->peerAddr));
        }
    }
    /* Free resource */
    AvctCbChDealloc(&(cbDev->cbBr));
    cbDev->chLink &= ~AVCT_ALLOC_BR;
    return AVCT_SUCCESS;
}

/*
 * Function     AvctCbBrChBusyInd
 * Description  This function is called when receive channel busy.
 * Param[in]    *cbDev  The point to the device block.
 * Param[in]    *data  Event data
 * Return       AVCT_SUCCESS. Others: Fail
 */
uint16_t AvctCbBrChBusyInd(AvctCbDev *cbDev, const AvctEvtData *data)
{
    LOG_DEBUG("[AVCT] %{public}s:", __func__);
    AvctCbConn *cbConn = NULL;
    uint8_t event = AVCT_BR_CHANNEL_UNBUSY_EVT;
    if (data->result == AVCT_BUSY_ST) {
        event = AVCT_BR_CHANNEL_BUSY_EVT;
    }
    for (uint8_t i = 0; i < AVCT_MAX_CONNECTS; i++) {
        cbConn = &g_avctMng.cbConn[i];
        if ((cbConn->status == AVCT_CONN_BIND) && (cbConn->cbDev == cbDev)) {
            /* Send AVCT_BR_CHANNEL_BUSY_EVT */
            AvctCbConnEvtCallback(cbConn, event, AVCT_SUCCESS, &(cbConn->cbDev->peerAddr));
        }
    }
    return AVCT_SUCCESS;
}

/*
 * Function     AvctCbBrDiscardMsg
 * Description  This function is called when discard received message.
 * Param[in]    *cbDev  The point to the device block.
 * Param[in]    *data  Event data
 * Return       AVCT_SUCCESS. Others: Fail
 */
uint16_t AvctCbBrDiscardMsg(AvctCbDev *cbDev, const AvctEvtData *data)
{
    LOG_DEBUG("[AVCT] %{public}s:", __func__);
    return AVCT_FAILED;
}

/*
 * Function     AvctCbBrSendMsg
 * Description  This function is called to send message.
 * Param[in]    *cbDev  The point to the device block.
 * Param[in]    *data  Event data
 * Return       AVCT_SUCCESS. Others: Fail
 */
uint16_t AvctCbBrSendMsg(AvctCbDev *cbDev, const AvctEvtData *data)
{
    LOG_DEBUG("[AVCT] %{public}s:", __func__);
    uint16_t ret = AVCT_SUCCESS;
    AvctTxMsg txMsg = data->txMsg;
    AvctCbCh *cbBr = cbDev->cbBr;
    /* Malloc Head */
    Packet *pkt = PacketInheritMalloc(txMsg.buf, AVCT_PKG_HDR_LEN_SINGLE, 0);
    if (pkt == NULL) {
        LOG_ERROR("[AVCT] %{public}s: PacketInheritMalloc failed!", __func__);
        ret = AVCT_ERR_NO_RESOURCES;
    } else {
        /* Make Packet Head */
        AvctMakeSignleMsgHead(pkt, txMsg.label, txMsg.cr, txMsg.cbConn->connParam.pid);
        /* Send Packet */
        int lRet = L2CIF_SendData(cbBr->chId, pkt, NULL);
        if (lRet) {
            LOG_WARN("[AVCT] %{public}s:L2CIF_SendData failed.error is %{public}d", __func__, lRet);
        }
        PacketFree(pkt);
    }
    return ret;
}

/*
 * Function     AvctCbBrRevMsg
 * Description  This function is called when received message.
 * Param[in]    *cbDev  The point to the device block.
 * Param[in]    *data  Event data
 * Return       AVCT_SUCCESS. Others: Fail
 */
uint16_t AvctCbBrRevMsg(AvctCbDev *cbDev, const AvctEvtData *data)
{
    LOG_DEBUG("[AVCT] %{public}s:", __func__);
    uint16_t ret = AVCT_SUCCESS;
    uint8_t sHead[AVCT_PKG_HDR_LEN_SINGLE] = {0};
    Packet *pkt = AvctMsgAsmbl(cbDev->cbBr, data->buf);
    if (pkt == NULL) {
        return ret;
    }
    /* Extract signle pkt head info */
    PacketExtractHead(pkt, sHead, AVCT_PKG_HDR_LEN_SINGLE);
    uint8_t label;
    uint8_t cr;
    uint8_t ipid;
    uint16_t pid;
    AVCT_PARSE_SIGNLE_HDR(sHead, label, cr, ipid, pid);
    if (ipid == 1) {
        /* an invalid Profile Identifier received */
        LOG_WARN("[AVCT] %{public}s: Invalid ipid!", __func__);
        return ret;
    }
    /* call all cb */
    uint8_t conId = 255; //invalid value
    for (uint8_t i = 0; i <= AVCT_MAX_CONNECTS;) {
        AvctCbConn *cbConn = AvctGetCbConidConn(cbDev, pid, i, &conId);
        if ((cbConn != NULL) && (cbConn->connParam.msgCallback != NULL)) {
            (*cbConn->connParam.msgCallback)(cbConn->connId, label, cr, AVCT_DATA_BR, pkt, cbConn->connParam.context);
            i = conId + 1;
            continue;
        } else if (conId < AVCT_MAX_CONNECTS) {
            return ret;
        }
        break;
    }
    /* Don't find pid connection,send reject */
    if (cr == AVCT_CMD) {
        Packet *sndPkg = PacketMalloc(AVCT_PKG_HDR_LEN_SINGLE, 0, 0);
        if (sndPkg == NULL) {
            LOG_ERROR("[AVCT] %{public}s: PacketMalloc failed!", __func__);
            ret = AVCT_ERR_NO_RESOURCES;
        } else {
            AvctMakeRejMsgHead(sndPkg, label, pid);
            int lRet = L2CIF_SendData(cbDev->cbBr->chId, sndPkg, NULL);
            if (lRet) {
                LOG_WARN("[AVCT] %{public}s:L2CIF_SendData failed.error is %{public}d", __func__, lRet);
            }
            PacketFree(sndPkg);
        }
    }
    return ret;
}

/*
 * Function     AvctCbBrDiscardRevMsg
 * Description  This function is called to discard received message.
 * Param[in]    *cbDev  The point to the device block.
 * Param[in]    *data  Event data
 * Return       AVCT_SUCCESS. Others: Fail
 */

uint16_t AvctCbBrDiscardRevMsg(AvctCbDev *cbDev, const AvctEvtData *data)
{
    LOG_DEBUG("[AVCT] %{public}s:", __func__);
    return AVCT_SUCCESS;
}