/*
 * 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 "l2cap.h"

#include <stdio.h>
#include <stdlib.h>
#include <string.h>

#include "log.h"

#include "l2cap_cmn.h"
#include "l2cap_core.h"
#include "l2cap_inst.h"
#include "l2cap_le.h"

int L2CAP_ConnectReq(const BtAddr *addr, uint16_t lpsm, uint16_t rpsm, uint16_t *lcid)
{
    L2capConnection *conn = NULL;
    L2capChannel *chan = NULL;
    L2capPsm *psm = NULL;

    LOG_INFO("%{public}s:%{public}d enter, lpsm = 0x%04X, rpsm = 0x%04X", __FUNCTION__, __LINE__, lpsm, rpsm);

    if (L2capInitialized() != BT_SUCCESS) {
        return BT_BAD_STATUS;
    }

    if ((addr == NULL) || (lcid == NULL)) {
        return BT_BAD_PARAM;
    }

    psm = L2capGetPsm(lpsm);
    if (psm == NULL) {
        return BT_BAD_PARAM;
    }

    conn = L2capGetConnection2(addr);
    if (conn == NULL) {
        conn = L2capNewConnection(addr, 0);
    }

    chan = L2capNewChannel(conn, lpsm, rpsm);
    *lcid = chan->lcid;

    if (conn->state == L2CAP_CONNECTION_IDLE) {
        conn->state = L2CAP_CONNECTION_CONNECTING;
        if (L2capConnectBdr(addr) != BT_SUCCESS) {
            L2capDeleteConnection(conn);
            return BT_OPERATION_FAILED;
        }

        return BT_SUCCESS;
    }

    if ((conn->state == L2CAP_CONNECTION_CONNECTING) || (conn->state == L2CAP_CONNECTION_DISCONNECTING)) {
        return BT_SUCCESS;
    }

    if (conn->info.state == L2CAP_INFO_STATE_NONE) {
        L2capSendInformationReq(conn, L2CAP_INFORMATION_TYPE_EXTENDED_FEATURE);
    } else if (conn->info.state == L2CAP_INFO_STATE_DONE) {
        L2capSendConnectionReq(conn, chan);
    }

    return BT_SUCCESS;
}

int L2CAP_ConnectRsp(uint16_t lcid, uint8_t id, uint16_t result, uint16_t status)
{
    L2capConnection *conn = NULL;
    L2capChannel *chan = NULL;

    LOG_INFO("%{public}s:%{public}d enter, lcid = 0x%04X, id = %{public}d, result = %{public}d, status = %{public}d",
        __FUNCTION__,
        __LINE__,
        lcid,
        id,
        result,
        status);

    if (L2capInitialized() != BT_SUCCESS) {
        return BT_BAD_STATUS;
    }

    L2capGetChannel2(lcid, &conn, &chan);
    if (chan == NULL) {
        return BT_BAD_PARAM;
    }

    if (chan->state != L2CAP_CHANNEL_CONNECT_IN_REQ) {
        return BT_BAD_STATUS;
    }

    L2capSendConnectionRsp(conn, chan, id, result, status);

    if ((result != L2CAP_CONNECTION_SUCCESSFUL) && (result != L2CAP_CONNECTION_PENDING)) {
        L2capDeleteChannel(conn, chan, 0);
    }

    return BT_SUCCESS;
}

int L2CAP_ConfigReq(uint16_t lcid, const L2capConfigInfo *cfg)
{
    L2capConnection *conn = NULL;
    L2capChannel *chan = NULL;

    LOG_INFO("%{public}s:%{public}d enter, lcid = 0x%04X", __FUNCTION__, __LINE__, lcid);

    if (L2capInitialized() != BT_SUCCESS) {
        return BT_BAD_STATUS;
    }

    if (cfg == NULL) {
        return BT_BAD_PARAM;
    }

    if ((cfg->mtu < L2CAP_MIN_MTU) || (cfg->flushTimeout == 0)) {
        return BT_BAD_PARAM;
    }

    L2capGetChannel2(lcid, &conn, &chan);
    if (chan == NULL) {
        return BT_BAD_PARAM;
    }

    if ((chan->state != L2CAP_CHANNEL_CONFIGING) && (chan->state != L2CAP_CHANNEL_CONNECTED)) {
        return BT_BAD_STATUS;
    }

    chan->lcfg.mtu = cfg->mtu;
    chan->lcfg.flushTimeout = cfg->flushTimeout;
    chan->lcfg.fcs = cfg->fcs;
    chan->lcfg.rfc.mode = cfg->rfc.mode;

    if (chan->lcfg.rfc.mode == L2CAP_ENHANCED_RETRANSMISSION_MODE) {
        if (cfg->rfc.rxWindowSize != 0) {
            chan->lcfg.rfc.rxWindowSize = cfg->rfc.rxWindowSize;
        }

        if (cfg->rfc.maxTransmit != 0) {
            chan->lcfg.rfc.maxTransmit = cfg->rfc.maxTransmit;
        }

        if (chan->lcfg.rfc.mps > cfg->mtu) {
            chan->lcfg.rfc.mps = cfg->mtu;
        }
    } else if (chan->lcfg.rfc.mode == L2CAP_STREAM_MODE) {
        chan->lcfg.rfc.maxTransmit = 0;
        chan->lcfg.rfc.retransmissionTimeout = 0;
        chan->lcfg.rfc.monitorTimeout = 0;
        chan->lcfg.rfc.txWindowSize = 0;
        chan->lcfg.rfc.rxWindowSize = 0;

        if (chan->lcfg.rfc.mps > cfg->mtu) {
            chan->lcfg.rfc.mps = cfg->mtu;
        }
    }

    if (cfg->rfc.mode == L2CAP_BASIC_MODE) {
        chan->lcfg.fcs = 0x01;
    }

    L2capSendConfigurationReq(conn, chan);
    return BT_SUCCESS;
}

int L2CAP_ConfigRsp(uint16_t lcid, uint8_t id, const L2capConfigInfo *cfg, uint16_t result)
{
    L2capConnection *conn = NULL;
    L2capChannel *chan = NULL;

    LOG_INFO("%{public}s:%{public}d enter, lcid = 0x%04X, id = %{public}d, result = %{public}d", __FUNCTION__, __LINE__, lcid, id, result);

    if (L2capInitialized() != BT_SUCCESS) {
        return BT_BAD_STATUS;
    }

    if (cfg == NULL) {
        return BT_BAD_PARAM;
    }

    if (cfg->mtu < L2CAP_MIN_MTU) {
        return BT_BAD_PARAM;
    }

    if (cfg->flushTimeout == 0) {
        return BT_BAD_PARAM;
    }

    L2capGetChannel2(lcid, &conn, &chan);
    if (chan == NULL) {
        return BT_BAD_PARAM;
    }

    if ((chan->state != L2CAP_CHANNEL_CONFIGING) && (chan->state != L2CAP_CHANNEL_CONNECTED)) {
        return BT_BAD_STATUS;
    }

    if (cfg->rfc.mode == L2CAP_ENHANCED_RETRANSMISSION_MODE) {
        chan->lcfg.rfc.txWindowSize = chan->rcfg.rfc.rxWindowSize;
        chan->lcfg.rfc.retransmissionTimeout = L2CAP_DEFAULT_RETRANSMISSION_TIMEOUT;
        chan->lcfg.rfc.monitorTimeout = L2CAP_DEFAULT_MONITOR_TIMEOUT;

        if (chan->rcfg.rfc.mps > (L2capGetTxBufferSize() - L2CAP_SIZE_10)) {
            chan->rcfg.rfc.mps = (L2capGetTxBufferSize() - L2CAP_SIZE_10);
        }
    } else if (chan->lcfg.rfc.mode == L2CAP_STREAM_MODE) {
        chan->lcfg.rfc.maxTransmit = 0;
        chan->lcfg.rfc.retransmissionTimeout = 0;
        chan->lcfg.rfc.monitorTimeout = 0;
        chan->lcfg.rfc.txWindowSize = 0;
        chan->lcfg.rfc.rxWindowSize = 0;

        if (chan->rcfg.rfc.mps > (L2capGetTxBufferSize() - L2CAP_SIZE_10)) {
            chan->rcfg.rfc.mps = (L2capGetTxBufferSize() - L2CAP_SIZE_10);
        }
    }

    L2capSendConfigurationRsp(conn, chan, id, result, cfg);
    return BT_SUCCESS;
}

int L2CAP_DisconnectionReq(uint16_t lcid)
{
    L2capConnection *conn = NULL;
    L2capChannel *chan = NULL;

    LOG_INFO("%{public}s:%{public}d enter, lcid = 0x%04X", __FUNCTION__, __LINE__, lcid);

    if (L2capInitialized() != BT_SUCCESS) {
        return BT_BAD_STATUS;
    }

    L2capGetChannel2(lcid, &conn, &chan);
    if (chan == NULL) {
        return BT_BAD_PARAM;
    }

    if ((chan->state != L2CAP_CHANNEL_CONNECTED) && (chan->state != L2CAP_CHANNEL_CONFIGING)) {
        return BT_BAD_STATUS;
    }

    L2capSendDisconnectionReq(conn, chan);
    return BT_SUCCESS;
}

int L2CAP_DisconnectionRsp(uint16_t lcid, uint8_t id)
{
    L2capConnection *conn = NULL;
    L2capChannel *chan = NULL;

    LOG_INFO("%{public}s:%{public}d enter, lcid = 0x%04X, id = %{public}d", __FUNCTION__, __LINE__, lcid, id);

    if (L2capInitialized() != BT_SUCCESS) {
        return BT_BAD_STATUS;
    }

    L2capGetChannel2(lcid, &conn, &chan);
    if (chan == NULL) {
        return BT_BAD_PARAM;
    }

    if (chan->state != L2CAP_CHANNEL_DISCONNECT_IN_REQ) {
        return BT_BAD_STATUS;
    }

    L2capSendDisconnectionRsp(conn, chan, id);
    L2capDeleteChannel(conn, chan, 0);
    return BT_SUCCESS;
}

int L2CAP_LocalBusy(uint16_t lcid, uint8_t isBusy)
{
    L2capConnection *conn = NULL;
    L2capChannel *chan = NULL;

    LOG_INFO("%{public}s:%{public}d enter, lcid = 0x%04X, isBusy = %{public}d", __FUNCTION__, __LINE__, lcid, isBusy);

    if (L2capInitialized() != BT_SUCCESS) {
        return BT_BAD_STATUS;
    }

    L2capGetChannel2(lcid, &conn, &chan);
    if (chan == NULL) {
        return BT_BAD_PARAM;
    }

    if (chan->lcfg.rfc.mode != L2CAP_ENHANCED_RETRANSMISSION_MODE) {
        return BT_BAD_PARAM;
    }

    if (chan->state != L2CAP_CHANNEL_CONNECTED) {
        return BT_BAD_STATUS;
    }

    if (isBusy > 0) {
        isBusy = L2CAP_BUSY_LOCAL_BUSY;
    }

    if ((chan->erfc.busyState & L2CAP_BUSY_LOCAL_BUSY) == isBusy) {
        return BT_BAD_STATUS;
    }

    if (isBusy) {
        chan->erfc.busyState |= L2CAP_BUSY_LOCAL_BUSY;
        L2capSendSFrame(conn, chan, L2CAP_ERFC_PBIT_OFF, L2CAP_ERFC_FBIT_OFF, L2CAP_ERFC_RNR);
    } else {
        chan->erfc.busyState &= (~L2CAP_BUSY_LOCAL_BUSY);
        chan->erfc.busyState |= L2CAP_BUSY_WAIT_F;
        L2capSendSFrame(conn, chan, L2CAP_ERFC_PBIT_ON, L2CAP_ERFC_FBIT_OFF, L2CAP_ERFC_RR);

        chan->erfc.retryCount = 1;
        L2capErfcStartMonitorTimer(chan);
    }

    return BT_SUCCESS;
}

int L2CAP_SendData(uint16_t lcid, Packet *pkt)
{
    L2capConnection *conn = NULL;
    L2capChannel *chan = NULL;
    uint16_t length;

    if (pkt == NULL) {
        return BT_BAD_PARAM;
    }

    LOG_INFO("%{public}s:%{public}d enter, lcid = 0x%04X, pktLength = %{public}d", __FUNCTION__, __LINE__, lcid, PacketSize(pkt));

    if (L2capInitialized() != BT_SUCCESS) {
        return BT_BAD_STATUS;
    }

    L2capGetChannel2(lcid, &conn, &chan);
    if (chan == NULL) {
        return BT_BAD_PARAM;
    }

    length = PacketSize(pkt);
    if (length > chan->rcfg.mtu) {
        return BT_BAD_PARAM;
    }

    if (chan->state != L2CAP_CHANNEL_CONNECTED) {
        return BT_BAD_STATUS;
    }

    if (chan->lcfg.rfc.mode == L2CAP_BASIC_MODE) {
        Packet *tpkt = NULL;
        uint8_t *header = NULL;

        tpkt = PacketInheritMalloc(pkt, L2CAP_HEADER_LENGTH, 0);
        header = BufferPtr(PacketHead(tpkt));

        L2capCpuToLe16(header + 0, length);
        L2capCpuToLe16(header + L2CAP_OFFSET_2, chan->rcid);

        L2capSendPacket(conn->aclHandle, chan->lcfg.flushTimeout, tpkt);
    } else {
        L2capSendIFrame(conn, chan, pkt);
    }

    return BT_SUCCESS;
}

int L2CAP_RegisterEcho(const L2capEcho *echoCallback, void *context)
{
    L2capInstance *inst = NULL;

    LOG_INFO("%{public}s:%{public}d enter", __FUNCTION__, __LINE__);

    if (L2capInitialized() != BT_SUCCESS) {
        return BT_BAD_STATUS;
    }

    if (echoCallback == NULL) {
        return BT_BAD_PARAM;
    }

    inst = L2capGetInstance();
    inst->echo.ctx = context;
    (void)memcpy_s(&(inst->echo.cb), sizeof(L2capEcho), echoCallback, sizeof(L2capEcho));

    return BT_SUCCESS;
}

int L2CAP_DeregisterEcho()
{
    L2capInstance *inst = NULL;

    LOG_INFO("%{public}s:%{public}d enter", __FUNCTION__, __LINE__);

    if (L2capInitialized() != BT_SUCCESS) {
        return BT_BAD_STATUS;
    }

    inst = L2capGetInstance();
    (void)memset_s(&(inst->echo), sizeof(L2capEcho), 0, sizeof(L2capEcho));
    return BT_SUCCESS;
}

int L2CAP_EchoReq(uint16_t aclHandle, const uint8_t *data, uint16_t dataLen)
{
    L2capConnection *conn = NULL;

    LOG_INFO("%{public}s:%{public}d enter, aclHandle = %{public}d, dataLen = %{public}d", __FUNCTION__, __LINE__, aclHandle, dataLen);

    if (L2capInitialized() != BT_SUCCESS) {
        return BT_BAD_STATUS;
    }

    if ((data == NULL) && (dataLen > 0)) {
        return BT_BAD_PARAM;
    }

    conn = L2capGetConnection(aclHandle);
    if (conn == NULL) {
        return BT_BAD_PARAM;
    }

    L2capSendEchoReq(conn, data, dataLen);
    return BT_SUCCESS;
}

int L2CAP_EchoRsp(uint16_t aclHandle, uint8_t id, const uint8_t *data, uint16_t dataLen)
{
    L2capConnection *conn = NULL;

    LOG_INFO("%{public}s:%{public}d enter, aclHandle = %{public}d, id = %{public}d, dataLen = %{public}d", __FUNCTION__, __LINE__, aclHandle, id, dataLen);

    if (L2capInitialized() != BT_SUCCESS) {
        return BT_BAD_STATUS;
    }

    if ((dataLen > 0) && (data == NULL)) {
        return BT_BAD_PARAM;
    }

    conn = L2capGetConnection(aclHandle);
    if (conn == NULL) {
        return BT_BAD_PARAM;
    }

    L2capSendEchoRsp(conn, id, data, dataLen);
    return BT_SUCCESS;
}

int L2CAP_RegisterService(uint16_t lpsm, const L2capService *svc, void *context)
{
    L2capInstance *inst = NULL;
    L2capPsm *psm = NULL;

    LOG_INFO("%{public}s:%{public}d enter, psm = 0x%04X", __FUNCTION__, __LINE__, lpsm);

    if (L2capInitialized() != BT_SUCCESS) {
        return BT_BAD_STATUS;
    }

    if (svc == NULL) {
        return BT_BAD_PARAM;
    }

    // check whether the psm is valid
    if (!(lpsm & 0x0001) || (lpsm & 0x0100)) {
        return BT_BAD_PARAM;
    }

    psm = L2capGetPsm(lpsm);
    if (psm != NULL) {
        return BT_BAD_STATUS;
    }

    psm = L2capAlloc(sizeof(L2capPsm));
    if (psm == NULL) {
        return BT_NO_MEMORY;
    }

    inst = L2capGetInstance();

    psm->lpsm = lpsm;
    psm->ctx = context;
    (void)memcpy_s(&(psm->service), sizeof(L2capService), svc, sizeof(L2capService));
    ListAddFirst(inst->psmList, psm);

    return BT_SUCCESS;
}

int L2CAP_DeregisterService(uint16_t lpsm)
{
    L2capInstance *inst = NULL;
    L2capPsm *psm = NULL;
    L2capConnection *conn = NULL;
    L2capChannel *chan = NULL;
    ListNode *node = NULL;
    ListNode *nodeChan = NULL;

    LOG_INFO("%{public}s:%{public}d enter, psm = 0x%04X", __FUNCTION__, __LINE__, lpsm);

    if (L2capInitialized() != BT_SUCCESS) {
        return BT_BAD_STATUS;
    }

    psm = L2capGetPsm(lpsm);
    if (psm == NULL) {
        return BT_BAD_PARAM;
    }

    inst = L2capGetInstance();
    node = ListGetFirstNode(inst->connList);
    while (node != NULL) {
        conn = ListGetNodeData(node);
        nodeChan = ListGetFirstNode(conn->chanList);
        while (nodeChan != NULL) {
            chan = ListGetNodeData(nodeChan);
            // if any channel used the psm, return error
            if (chan->lpsm == lpsm) {
                return BT_BAD_STATUS;
            }

            nodeChan = ListGetNextNode(nodeChan);
        }

        node = ListGetNextNode(node);
    }

    ListRemoveNode(inst->psmList, psm);
    L2capFree(psm);
    return BT_SUCCESS;
}

void L2CAP_Initialize(int traceLevel)
{
    L2capInstance *inst = NULL;
    L2capBdrCallback cmnCallback = {0};

    LOG_INFO("%{public}s:%{public}d enter", __FUNCTION__, __LINE__);

    cmnCallback.aclConnected = L2capConnectComplete;
    cmnCallback.aclDisconnected = L2capDisconnectComplete;
    cmnCallback.recvL2capPacket = L2capReceivePacket;
    L2capRegisterBdr(&cmnCallback);

    inst = L2capGetInstance();
    inst->psmList = ListCreate(NULL);
    inst->connList = ListCreate(NULL);
    inst->nextLcid = L2CAP_MIN_CID;
    (void)memset_s(&(inst->echo), sizeof(L2capEcho), 0, sizeof(L2capEcho));

    L2CAP_LeInitialize(traceLevel);

    L2capCommonStartup();
    return;
}

void L2CAP_Finalize()
{
    L2capInstance *inst = NULL;
    ListNode *node = NULL;
    L2capConnection *conn = NULL;
    L2capPsm *psm = NULL;

    LOG_INFO("%{public}s:%{public}d enter", __FUNCTION__, __LINE__);

    L2capCommonShutdown();

    inst = L2capGetInstance();
    node = ListGetFirstNode(inst->connList);
    while (node != NULL) {
        conn = ListGetNodeData(node);
        L2capDeleteConnection(conn);

        node = ListGetFirstNode(inst->connList);
    }

    ListDelete(inst->connList);
    inst->connList = NULL;

    node = ListGetFirstNode(inst->psmList);
    while (node != NULL) {
        psm = ListGetNodeData(node);
        ListRemoveNode(inst->psmList, psm);
        L2capFree(psm);

        node = ListGetFirstNode(inst->psmList);
    }

    ListDelete(inst->psmList);
    inst->psmList = NULL;

    (void)memset_s(&(inst->echo), sizeof(L2capEcho), 0, sizeof(L2capEcho));

    L2CAP_LeFinalize();
    return;
}