/* -------------------------------------------------------------------------
 *  This file is part of the oGRAC project.
 * Copyright (c) 2024 Huawei Technologies Co.,Ltd.
 *
 * oGRAC is licensed under Mulan PSL v2.
 * You can use this software according to the terms and conditions of the Mulan PSL v2.
 * You may obtain a copy of Mulan PSL v2 at:
 *
 *          http://license.coscl.org.cn/MulanPSL2
 *
 * 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 FIT FOR A PARTICULAR PURPOSE.
 * See the Mulan PSL v2 for more details.
 * -------------------------------------------------------------------------
 *
 * cm_file_iofence.c
 *
 *
 * IDENTIFICATION
 * src/common/cm_file_iofence.c
 *
 * -------------------------------------------------------------------------
 */

#include "cm_common_module.h"
#include "cm_file.h"
#include "cm_log.h"
#include "cm_error.h"
#include "cm_file_iofence.h"

#define IO_FENCE_FILE_NAME_BUFFER_SIZE 256

typedef struct {
    uint32 cluster_id;
    uint32 termid;
    char file_path[IO_FENCE_FILE_NAME_BUFFER_SIZE];
} cm_file_iof_cfg_s;

cm_file_iof_cfg_s g_file_cfg;

status_t cm_set_file_iof_cfg(uint32 cluster_id, uint32 termid, const char *file_path)
{
    errno_t err;
    g_file_cfg.cluster_id = cluster_id;
    g_file_cfg.termid = termid;
    err = strncpy_s(g_file_cfg.file_path, IO_FENCE_FILE_NAME_BUFFER_SIZE, file_path, strlen(file_path));
    if (err != EOK) {
        OG_LOG_RUN_ERR("Set file iof cfg failed, strncpy err, %d:%s.", errno, strerror(errno));
        return OG_ERROR;
    }
    OG_LOG_RUN_INF("File set iof cfg succ, file path %s, cluster id %u.", g_file_cfg.file_path, g_file_cfg.cluster_id);
    return OG_SUCCESS;
}

static void iof_file_watcher(thread_t *thread)
{
    char *file_name = (char *)thread->argument;
    cm_set_thread_name("Iof_file_watcher");
    OG_LOG_RUN_INF("Iof file watcher, file name(%s).", file_name);
    for (;;) {
        if (cm_access_file(file_name, F_OK) != 0) {
            OG_LOG_RUN_ERR("Failed to access file(%s).", file_name);
            break;
        }
        cm_sleep(1);
    }
    CM_FREE_PTR(file_name);
    CM_ABORT_REASONABLE(0, "ABORT INFO: self abort, iofence.");
}

status_t cm_file_iof_register(uint32 inst_id, thread_t *file_iof_thd)
{
    if (strlen(g_file_cfg.file_path) == 0) {
        OG_LOG_RUN_WAR("Iof file path is empty, no need iofence.");
        return OG_SUCCESS;
    }

    errno_t err;
    int iof_fd;
    char *file_name = malloc(sizeof(char) * IO_FENCE_FILE_NAME_BUFFER_SIZE);
    if (file_name == NULL) {
        OG_LOG_RUN_ERR("Failed to malloc file name buffer(%u), %d:%s.", IO_FENCE_FILE_NAME_BUFFER_SIZE, errno,
                       strerror(errno));
        return OG_ERROR;
    }
    err = snprintf_s(file_name, IO_FENCE_FILE_NAME_BUFFER_SIZE, IO_FENCE_FILE_NAME_BUFFER_SIZE - 1, "%s/%u_%u_iof",
                     g_file_cfg.file_path, g_file_cfg.cluster_id, inst_id);
    if (err == -1) {
        OG_LOG_RUN_ERR("Failed to snprintf file name, %d:%s.", errno, strerror(errno));
        return OG_ERROR;
    }
    OG_RETURN_IFERR(cm_open_file(file_name, O_CREAT | O_RDWR | O_BINARY | O_CLOEXEC | O_SYNC | O_DIRECT, &iof_fd));
    cm_close_file(iof_fd);

    if (OG_SUCCESS != cm_create_thread(iof_file_watcher, 0, file_name, file_iof_thd)) {
        OG_LOG_RUN_ERR("Failed to create file(%s) watcher thread.", file_name);
        return OG_ERROR;
    }
    OG_LOG_RUN_INF("File iof register succ, file name %s, cluster id %u, inst id %u.", file_name, g_file_cfg.cluster_id,
                   inst_id);
    return OG_SUCCESS;
}

status_t cm_file_iof_kick_by_inst_id(uint32 inst_id)
{
    if (strlen(g_file_cfg.file_path) == 0) {
        OG_LOG_RUN_WAR("Iof file path is empty, no need iofence.");
        return OG_SUCCESS;
    }
    errno_t err;
    char file_name[IO_FENCE_FILE_NAME_BUFFER_SIZE] = { 0 };
    err = snprintf_s(file_name, IO_FENCE_FILE_NAME_BUFFER_SIZE, IO_FENCE_FILE_NAME_BUFFER_SIZE - 1, "%s/%u_%u_iof",
                     g_file_cfg.file_path, g_file_cfg.cluster_id, inst_id);
    if (err == -1) {
        OG_LOG_RUN_ERR("Failed to snprintf file name, %d:%s.", errno, strerror(errno));
        return OG_ERROR;
    }
    if (cm_access_file(file_name, F_OK) != 0) {
        OG_LOG_RUN_WAR("Iof file not exist, file(%s).", file_name);
        return OG_SUCCESS;
    }
    if (cm_remove_file(file_name) != OG_SUCCESS) {
        OG_LOG_RUN_ERR("Failed to remove file(%s).", file_name);
        return OG_ERROR;
    }
    OG_LOG_RUN_INF("File iof kick succ, file name %s, cluster id %u, inst id %u.", file_name, g_file_cfg.cluster_id,
                   inst_id);
    return OG_SUCCESS;
}