//
// Created by xajhuang on 2023/2/9.
//
#include <uv.h>
#include "uthash/uthash.h"
#include "misc.h"
#include "common.h"
#include "user_errno.h"
#include "zlog_module.h"
#include "task_manager.h"
#include "fs_watch.h"

typedef struct {
    char     watchPath[MAX_PATH];
    watch_cb pCb;

    uv_fs_event_t *uv_event;
    UT_hash_handle hh;    ///< UT Hash handle
} WATCH_CONTENT, *PWATCH_CONTENT;

PWATCH_CONTENT g_watchContent = NULL;

static void monitor_cb(uv_fs_event_t *handle, const char *filename, int events, int status) {
    PWATCH_CONTENT pCtx = (PWATCH_CONTENT)handle->data;
    char           path[1024];
    size_t         size = 1023;

    uv_fs_event_getpath(handle, path, &size);
    path[size] = '\0';

    if (pCtx && pCtx->pCb) {
        pCtx->pCb(path, filename, events, pCtx->watchPath);
    }
#if 1
    fprintf(stderr, "Change detected in %s: ", path);
    if (events & UV_RENAME) {
        fprintf(stderr, "renamed");
    }
    if (events & UV_CHANGE) {
        fprintf(stderr, "changed");
    }

    fprintf(stderr, " %s\n", filename ? filename : "");
#endif
}

int add_watch_path(const char *pFsPath, watch_cb cb) {
    PWATCH_CONTENT pCtx;

    if (cb == NULL || pFsPath == NULL || !file_exists(pFsPath)) {
        LOG_MOD(debug, ZLOG_MOD_WATCH, "Watch path [%s] notify function <%p>\n", pFsPath ? pFsPath : "<NULL>", cb);
        return -ERR_INPUT_PARAMS;
    }

    HASH_FIND_STR(g_watchContent, pFsPath, pCtx);

    if (pCtx != NULL) {
        return -ERR_ITEM_EXISTS;
    }

    pCtx = (PWATCH_CONTENT)malloc(sizeof(WATCH_CONTENT));

    if (pCtx == NULL) {
        LOG_MOD(error, ZLOG_MOD_WATCH, "Malloc memory error of %ld size\n", sizeof(WATCH_CONTENT));
        return -ERR_MALLOC_MEMORY;
    }

    pCtx->uv_event = (uv_fs_event_t *)malloc(sizeof(uv_fs_event_t));

    if (pCtx->uv_event == NULL) {
        LOG_MOD(error, ZLOG_MOD_WATCH, "Malloc memory error of %ld size\n", sizeof(uv_fs_event_t));
        free(pCtx);
        return -ERR_MALLOC_MEMORY;
    }

    memset(pCtx, 0, sizeof(WATCH_CONTENT));

    strcpy(pCtx->watchPath, pFsPath);
    pCtx->pCb            = cb;
    pCtx->uv_event->data = pCtx;

    uv_fs_event_init(get_task_manager(), pCtx->uv_event);
    uv_fs_event_start(pCtx->uv_event, monitor_cb, pFsPath, UV_FS_EVENT_RECURSIVE);

    HASH_ADD_STR(g_watchContent, watchPath, pCtx);
    return ERR_SUCCESS;
}

int remove_watch_path(const char *pFsPath) {
    PWATCH_CONTENT pCtx;

    if (pFsPath == NULL || !file_exists(pFsPath)) {
        LOG_MOD(debug, ZLOG_MOD_WATCH, "Remove Watch path [%s]\n", pFsPath ? pFsPath : "<NULL>");
        return -ERR_INPUT_PARAMS;
    }

    HASH_FIND_STR(g_watchContent, pFsPath, pCtx);

    if (pCtx != NULL) {
        HASH_DEL(g_watchContent, pCtx);
        uv_fs_event_stop(pCtx->uv_event);
        free(pCtx->uv_event);
        free(pCtx);
    }

    return ERR_SUCCESS;
}