/*
 * Copyright (c) 2021-2023 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 "nstackx_dfinder_hievent.h"
#include "nstackx_dfinder_log.h"
#include "nstackx_statistics.h"
#include "nstackx_error.h"
#include "nstackx_common.h"
#include "nstackx_event.h"
#include "nstackx_util.h"
#include "securec.h"

#define TAG "nStackXDFinder"
#define STAT_EVT_PARA_NUM 4
#define STAT_EVT_NAME "DFINDER_STATS"

static void *g_softObj;
static DFinderEventFunc g_eventFunc;
static pthread_mutex_t g_eventFuncLock = PTHREAD_MUTEX_INITIALIZER;

static int g_statisticsIdx[STAT_EVT_PARA_NUM] = {
    STATS_INVALID_OPT_AND_PAYLOAD,
    STATS_BUILD_PKT_FAILED,
    STATS_INVALID_RESPONSE_MSG,
    STATS_OVER_DEVICE_LIMIT
};

static DFinderEventParam g_statisticsPara[STAT_EVT_PARA_NUM] = {
    {
        .type = DFINDER_PARAM_TYPE_UINT64,
        .name = "INVALID_OPTION_CNT"
    },
    {
        .type = DFINDER_PARAM_TYPE_UINT64,
        .name = "BUILD_PKT_FAIL_CNT"
    },
    {
        .type = DFINDER_PARAM_TYPE_UINT64,
        .name = "INVALID_RSP_CNT"
    },
    {
        .type = DFINDER_PARAM_TYPE_UINT64,
        .name = "OVER_DEVICE_LIMIT_CNT"
    }
};

static DFinderEventParam *CreateStatisticsEventParams(void)
{
    int i;
    const uint64_t *stat = GetStatistics();
    DFinderEventParam *para = (DFinderEventParam *)calloc(STAT_EVT_PARA_NUM, sizeof(DFinderEventParam));
    if (para == NULL) {
        return NULL;
    }

    for (i = 0; i < STAT_EVT_PARA_NUM; i++) {
        para[i] = g_statisticsPara[i];
        para[i].value.u64v = stat[g_statisticsIdx[i]];
    }
    return para;
}

void NotifyStatisticsEvent(void)
{
    int ret;
    DFinderEvent evt;

    if (g_eventFunc == NULL) {
        return;
    }

    (void)memset_s(&evt, sizeof(evt), 0, sizeof(evt));
    evt.type = DFINDER_EVENT_TYPE_STATISTIC;
    evt.level = DFINDER_EVENT_LEVEL_MINOR;
    evt.paramNum = STAT_EVT_PARA_NUM;
    ret = memcpy_s(evt.eventName, DFINDER_EVENT_NAME_LEN, STAT_EVT_NAME, strlen(STAT_EVT_NAME));
    if (ret != EOK) {
        DFINDER_LOGE(TAG, "memcpy_s eventName failed");
        return;
    }

    evt.params = CreateStatisticsEventParams();
    if (evt.params == NULL) {
        DFINDER_LOGE(TAG, "create statistics params failed");
        return;
    }
    g_eventFunc(g_softObj, &evt);
    free(evt.params);
    DFINDER_LOGD(TAG, "report statistics event");
}

typedef struct {
    void *softobj;
    DFinderEventFunc func;
} DFinderEventMsg;

static void SetEventFuncInner(void *arg)
{
    DFinderEventMsg *msg = (DFinderEventMsg *)arg;
    g_softObj = msg->softobj;
    g_eventFunc = msg->func;
    free(msg);
}

int SetEventFunc(void *softobj, DFinderEventFunc func)
{
    DFinderEventMsg *msg = (DFinderEventMsg *)malloc(sizeof(DFinderEventMsg));
    if (msg == NULL) {
        return NSTACKX_EFAILED;
    }

    msg->softobj = softobj;
    msg->func = func;
    if (PostEvent(GetEventNodeChain(), GetEpollFD(), SetEventFuncInner, msg) != NSTACKX_EOK) {
        free(msg);
        return NSTACKX_EFAILED;
    }

    return NSTACKX_EOK;
}

int SetEventFuncDirectly(void *softobj, DFinderEventFunc func)
{
    if (PthreadMutexLock(&g_eventFuncLock) != 0) {
        DFINDER_LOGE(TAG, "failed to lock");
        return NSTACKX_EFAILED;
    }
    g_softObj = softobj;
    g_eventFunc = func;
    if (PthreadMutexUnlock(&g_eventFuncLock) != 0) {
        DFINDER_LOGE(TAG, "failed to unlock");
        return NSTACKX_EFAILED;
    }
    return NSTACKX_EOK;
}

void ResetEventFunc(void)
{
    if (PthreadMutexLock(&g_eventFuncLock) != 0) {
        DFINDER_LOGE(TAG, "failed to lock");
        return;
    }
    g_softObj = NULL;
    g_eventFunc = NULL;
    if (PthreadMutexUnlock(&g_eventFuncLock) != 0) {
        DFINDER_LOGE(TAG, "failed to unlock");
    }
}