/**
 * Copyright (c) 2025 Huawei Technologies Co., Ltd.
 * This program is free software, you can redistribute it and/or modify it under the terms and conditions of
 * CANN Open Software License Agreement Version 2.0 (the "License").
 * Please refer to the License for details. You may not use this file except in compliance with the License.
 * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, EITHER EXPRESS OR IMPLIED,
 * INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, MERCHANTABILITY, OR FITNESS FOR A PARTICULAR PURPOSE.
 * See LICENSE in the root of the software repository for the full text of the License.
 */
#include "dmc/dmc_share_log.h"
#include "dmc/dmc_log_user.h"
#include "securec.h"

#include <sys/mman.h>
#include <stdbool.h>

#ifdef STATIC_SKIP
#  define STATIC
#else
#  define STATIC    static
#endif

#define SHARE_LOG_NO_INIT          (0U)
#define SHARE_LOG_INIT             (1U)
#define SHARE_LOG_MAGIC_LENGTH     (24U)
#define SHARE_LOG_RECORD_OFFSET    (100U)
#define SHARE_LOG_MAGIC            ("drvshartlogab90cd78ef56")

#ifdef LOG_UT
#define LOG_PRINT_WARN(fmt, ...)   printf("[WARN][%s %d]" fmt, __func__, __LINE__, ##__VA_ARGS__)
#else
#define LOG_PRINT_WARN(fmt, ...)   DRV_WARN(HAL_MODULE_TYPE_LOG, fmt, ##__VA_ARGS__)
#endif
struct share_log_info {
    char magic[SHARE_LOG_MAGIC_LENGTH];
    int32_t total_size;
    void *record_base;
    int32_t record_size;
    int32_t read;
    int32_t write;
};

struct share_log_module_mng {
    void *start;
    int32_t init_flag;
};

STATIC struct share_log_module_mng g_module_mng[HAL_MODULE_TYPE_MAX][SHARE_LOG_TYPE_MAX] = {
    [HAL_MODULE_TYPE_DEVMM] = {
        {(void *)(uintptr_t)DEVMM_SHARE_LOG_START, SHARE_LOG_NO_INIT},
        {(void *)(uintptr_t)DEVMM_SHARE_LOG_RUNINFO_START, SHARE_LOG_NO_INIT}
    },
    [HAL_MODULE_TYPE_TS_DRIVER] = {
        {(void *)(uintptr_t)TSDRV_SHARE_LOG_START, SHARE_LOG_NO_INIT},
        {(void *)(uintptr_t)TSDRV_SHARE_LOG_RUNINFO_START, SHARE_LOG_NO_INIT}
    },
    [HAL_MODULE_TYPE_DEV_MANAGER] = {
        {(void *)(uintptr_t)DEVMNG_SHARE_LOG_START, SHARE_LOG_NO_INIT},
        {(void *)(uintptr_t)DEVMNG_SHARE_LOG_RUNINFO_START, SHARE_LOG_NO_INIT}
    },
    [HAL_MODULE_TYPE_HDC] = {
        {(void *)(uintptr_t)HDC_SHARE_LOG_START, SHARE_LOG_NO_INIT},
        {(void *)(uintptr_t)HDC_SHARE_LOG_RUNINFO_START, SHARE_LOG_NO_INIT}
    },
    [HAL_MODULE_TYPE_EVENT_SCHEDULE] = {
        {(void *)(uintptr_t)ESCHED_SHARE_LOG_START, SHARE_LOG_NO_INIT},
        {(void *)(uintptr_t)ESCHED_SHARE_LOG_RUNINFO_START, SHARE_LOG_NO_INIT}
    },
    [HAL_MODULE_TYPE_BUF_MANAGER] = {
        {(void *)(uintptr_t)XSMEM_SHARE_LOG_START, SHARE_LOG_NO_INIT},
        {(void *)(uintptr_t)XSMEM_SHARE_LOG_RUNINFO_START, SHARE_LOG_NO_INIT}
    },
    [HAL_MODULE_TYPE_QUEUE_MANAGER] = {
        {(void *)(uintptr_t)QUEUE_SHARE_LOG_START, SHARE_LOG_NO_INIT},
        {(void *)(uintptr_t)QUEUE_SHARE_LOG_RUNINFO_START, SHARE_LOG_NO_INIT}
    },
    [HAL_MODULE_TYPE_COMMON] = {
        {(void *)(uintptr_t)COMMON_SHARE_LOG_START, SHARE_LOG_NO_INIT},
        {(void *)(uintptr_t)COMMON_SHARE_LOG_RUNINFO_START, SHARE_LOG_NO_INIT}
    },
    [HAL_MODULE_TYPE_APM] = {
 	    {(void *)(uintptr_t)APM_SHARE_LOG_START, SHARE_LOG_NO_INIT},
 	    {(void *)(uintptr_t)APM_SHARE_LOG_RUNINFO_START, SHARE_LOG_NO_INIT}
 	},
};

STATIC pthread_mutex_t share_log_mutex[SHARE_LOG_TYPE_MAX] = {PTHREAD_MUTEX_INITIALIZER, PTHREAD_MUTEX_INITIALIZER};

STATIC void share_log_create_single_type(enum devdrv_module_type module_type,
    enum share_log_type_enum log_type, uint32_t size)
{
    struct share_log_info *info = NULL;
    int32_t len;

    (void)pthread_mutex_lock(&share_log_mutex[log_type]);
    if ((module_type >= HAL_MODULE_TYPE_MAX) || (size > (uint32_t)SHARE_LOG_MAX_SIZE) ||
        (g_module_mng[module_type][log_type].init_flag == SHARE_LOG_INIT)) {
        (void)pthread_mutex_unlock(&share_log_mutex[log_type]);
        LOG_PRINT_WARN("Invalid input or share_log has been inited. (module_type=%d; log_type=%d; size=%u)\n",
            module_type, log_type, size);
        return;
    }

    info = (struct share_log_info *)mmap(g_module_mng[module_type][log_type].start, size,
        PROT_READ | PROT_WRITE, MAP_PRIVATE | MAP_ANONYMOUS, -1, 0);
    if (info != (struct share_log_info *)g_module_mng[module_type][log_type].start) {
        (void)pthread_mutex_unlock(&share_log_mutex[log_type]);
#ifndef LOG_UT
        LOG_PRINT_WARN("Share_log mmap addr not match. (errno=%d; reason=\"%s\")\n", errno, strerror(errno));
        if (info != MAP_FAILED) {
            (void)munmap(info, size);
        }
#endif
        return;
    }
    (void)memset_s(info, size, 0, size);
    len = snprintf_s(info->magic, SHARE_LOG_MAGIC_LENGTH, SHARE_LOG_MAGIC_LENGTH - 1, "%s", SHARE_LOG_MAGIC);
    if (len < 0) {
        (void)munmap(g_module_mng[module_type][log_type].start, size);
        (void)pthread_mutex_unlock(&share_log_mutex[log_type]);
        LOG_PRINT_WARN("Snprintf_s unsuccessfully. (len=%d)\n", len);
        return;
    }

    info->magic[SHARE_LOG_MAGIC_LENGTH - 1] = '\0';
    info->record_base = (char *)g_module_mng[module_type][log_type].start + SHARE_LOG_RECORD_OFFSET;
    info->total_size = (int32_t)size;
    info->record_size = (int32_t)(size - SHARE_LOG_RECORD_OFFSET);
    info->read = 0;
    info->write = 0;
    g_module_mng[module_type][log_type].init_flag = SHARE_LOG_INIT;
    (void)pthread_mutex_unlock(&share_log_mutex[log_type]);

    return;
}

void share_log_create(enum devdrv_module_type module_type, uint32_t size)
{
    enum share_log_type_enum i = SHARE_LOG_ERR;
    for (i = SHARE_LOG_ERR; i < SHARE_LOG_TYPE_MAX; i++) {
        share_log_create_single_type(module_type, i, size);
    }
}

STATIC bool share_log_magic_check(const char *magic)
{
    if (strcmp(magic, SHARE_LOG_MAGIC) == 0) {
        return true;
    } else {
        return false;
    }
}

STATIC void share_log_destroy_single_type(enum devdrv_module_type module_type, enum share_log_type_enum log_type)
{
    struct share_log_info *info = NULL;

    if (module_type >= HAL_MODULE_TYPE_MAX) {
        return;
    }

    share_log_read(HAL_MODULE_TYPE_COMMON);
    (void)pthread_mutex_lock(&share_log_mutex[log_type]);
    info = (struct share_log_info *)g_module_mng[module_type][log_type].start;
    if ((g_module_mng[module_type][log_type].init_flag == SHARE_LOG_INIT) &&
        (share_log_magic_check(info->magic) == true)) {
        size_t size = (size_t)info->total_size;
        (void)memset_s(info, size, 0, size);
        (void)munmap(g_module_mng[module_type][log_type].start, size);
        g_module_mng[module_type][log_type].init_flag = SHARE_LOG_NO_INIT;
    }
    (void)pthread_mutex_unlock(&share_log_mutex[log_type]);

    return;
}

void share_log_destroy(enum devdrv_module_type type)
{
    enum share_log_type_enum i = SHARE_LOG_ERR;
    for (i = SHARE_LOG_ERR; i < SHARE_LOG_TYPE_MAX; i++) {
        share_log_destroy_single_type(type, i);
    }
}

static void share_log_read_in_single_module(enum share_log_type_enum log_type, enum devdrv_module_type module_type)
{
    struct share_log_info *info = NULL;
    int32_t tmp_read, tmp_write;

    if (module_type >= HAL_MODULE_TYPE_MAX) {
        return;
    }

    (void)pthread_mutex_lock(&share_log_mutex[log_type]);
    info = (struct share_log_info *)g_module_mng[module_type][log_type].start;
    if ((g_module_mng[module_type][log_type].init_flag == SHARE_LOG_NO_INIT) ||
        (share_log_magic_check(info->magic) == false) ||
        (info->write == info->read) || (info->write > info->record_size) ||
        (info->record_size <= 0) || ((unsigned int)(info->record_size) > (SHARE_LOG_MAX_SIZE - SHARE_LOG_RECORD_OFFSET))) {
        (void)pthread_mutex_unlock(&share_log_mutex[log_type]);
        return;
    }

    tmp_read = info->read;
    tmp_write = info->write;

    if (tmp_write > tmp_read) {
#ifndef LOG_UT
        uint32_t i;
        char *read_buff = malloc((unsigned int)info->record_size);
        if (read_buff == NULL) {
            (void)pthread_mutex_unlock(&share_log_mutex[log_type]);
            return;
        }
        size_t out_size = (size_t)(tmp_write - tmp_read);
        int32_t ret = memcpy_s(read_buff, (unsigned int)info->record_size, (char *)info->record_base + tmp_read,
                               out_size);
        if (ret != 0) {
            free(read_buff);
            (void)pthread_mutex_unlock(&share_log_mutex[log_type]);
            return;
        }

        for (i = 0; i < out_size - 1; i++) {
            if (read_buff[i] == '\n') {
                read_buff[i] = ' ';
            }
        }
        read_buff[out_size] = '\0';
        if (log_type == SHARE_LOG_ERR) {
            DRV_ERR(module_type, "%s", read_buff);
        } else {
            DRV_RUN_INFO(module_type, "%s", read_buff);
        }

        free(read_buff);
        info->read = tmp_write;
#endif
    }
    (void)pthread_mutex_unlock(&share_log_mutex[log_type]);

    return;
}

void share_log_read(enum devdrv_module_type module_type)
{
    share_log_read_in_single_module(SHARE_LOG_ERR, module_type);
    share_log_read_in_single_module(SHARE_LOG_ERR, HAL_MODULE_TYPE_COMMON);
}

void share_log_read_err(enum devdrv_module_type module_type)
{
    share_log_read_in_single_module(SHARE_LOG_ERR, module_type);
    share_log_read_in_single_module(SHARE_LOG_ERR, HAL_MODULE_TYPE_COMMON);
}

void share_log_read_run_info(enum devdrv_module_type module_type)
{
    share_log_read_in_single_module(SHARE_LOG_RUN_INFO, module_type);
    share_log_read_in_single_module(SHARE_LOG_RUN_INFO, HAL_MODULE_TYPE_COMMON);
}