/**
 * @file    myCmd.cpp
 * @brief   命令处理框架实现 — 命令注册/分发 + 内置 linenoise 行编辑
 */
#include "myCmd.h"
#include <vector>
#include <cstring>
#include <cstdio>
#include <cstdlib>
#include <unistd.h>
#include <termios.h>
#include <signal.h>

extern "C"
{

/* ===== 内置命令 ===== */
CMD_REGISTER("help", cmd_help, "显示所有可用命令")

/* ===== 命令注册表 ===== */

static std::vector<stru_cmd> &get_cmd_list(void)
{
    static std::vector<stru_cmd> s_cmd_list;

    return s_cmd_list;
}

void cmd_manager_add_command(const char *name, void (*func)(int argc, char *argv[]),
                              const char *desc, void (*complete)(const char *, char ***, int *))
{
    if (!name || !func || !desc)
    {
        return;
    }

    auto &list = get_cmd_list();

    list.push_back({name, func, desc, complete});
}

stru_cmd *cmd_manager_get_commands(unsigned int *out_count)
{
    auto &list = get_cmd_list();

    if (out_count)
    {
        *out_count = (unsigned int)list.size();
    }

    if (list.empty())
    {
        return NULL;
    }

    return list.data();
}

stru_cmd *cmd_find(const char *name)
{
    if (!name)
    {
        return NULL;
    }

    auto &list = get_cmd_list();

    for (auto &cmd : list)
    {
        if (0 == strcmp(cmd.name, name))
        {
            return &cmd;
        }
    }

    return NULL;
}

void cmd_complete(const char *buf, char ***completions, int *ncomp)
{
    auto &list = get_cmd_list();

    /* 判断是否有空格 —— 有空格则进入子命令补全 */
    const char *space = strchr(buf, ' ');

    if (space)
    {
        size_t cmd_len = (size_t)(space - buf);

        for (auto &cmd : list)
        {
            if (0 == strncmp(cmd.name, buf, cmd_len) && '\0' == cmd.name[cmd_len])
            {
                if (cmd.complete)
                {
                    cmd.complete(buf, completions, ncomp);

                    return;
                }

                break;
            }
        }

        *ncomp = 0;
        *completions = NULL;

        return;
    }

    /* 无空格：命令名前缀匹配 */
    int cnt = 0;
    char **c = NULL;

    for (auto &cmd : list)
    {
        if (!strncmp(cmd.name, buf, strlen(buf)))
        {
            if (!c)
            {
                c = (char **)malloc(sizeof(char *) * (list.size() + 1));
            }

            c[cnt] = strdup(cmd.name);
            cnt++;
        }
    }

    if (!c)
    {
        *ncomp = 0;
        *completions = NULL;
    }
    else
    {
        *ncomp = cnt;
        *completions = c;
    }
}

void cmd_sub_complete(const char *buf, char ***completions, int *ncomp,
                       const char **subs, int sub_count)
{
    const char *prefix = strrchr(buf, ' ');

    if (prefix)
    {
        prefix++;
    }
    else
    {
        prefix = buf;
    }

    int cnt = 0;
    char **c = NULL;
    size_t prefix_len = strlen(prefix);

    for (int i = 0; i < sub_count; i++)
    {
        if (0 == strncmp(subs[i], prefix, prefix_len))
        {
            if (!c)
            {
                c = (char **)malloc(sizeof(char *) * (size_t)(sub_count + 1));
            }

            c[cnt] = strdup(subs[i]);
            cnt++;
        }
    }

    *completions = c;
    *ncomp = cnt;
}

void cmd_help(int argc, char *argv[])
{
    (void)argc;
    (void)argv;

    auto &list = get_cmd_list();

    for (auto &cmd : list)
    {
        printf("%-24s [%s]\n", cmd.name, cmd.desc);
    }

    fflush(stdout);
}

/* ===== 内置 linenoise ===== */

#define SAFE_WRITE(fd, buf, sz) \
    do \
    { \
        ssize_t _r = write(fd, buf, sz); \
        (void)_r; \
    } while (0)

static char *g_history[100];
static int   g_history_len = 0;
static void (*g_completion_cb)(const char *, char ***, int *) = NULL;

static struct termios g_orig_termios;
static int g_raw_enabled = 0;
static void (*g_old_sigint)(int) = NULL;

static void disable_raw(void)
{
    if (!g_raw_enabled)
    {
        return;
    }

    tcsetattr(STDIN_FILENO, TCSAFLUSH, &g_orig_termios);
    g_raw_enabled = 0;

    /* restore SIGINT handler */
    if (g_old_sigint)
    {
        signal(SIGINT, g_old_sigint);
        g_old_sigint = NULL;
    }
}

static void sigint_handler(int sig)
{
    void (*old)(int) = g_old_sigint;

    disable_raw();

    /* 先恢复 raw mode 再调用原始 handler（main.c 的 sig_handler 设 g_running=0） */
    if (old)
    {
        old(sig);
    }
}

static int enable_raw(void)
{
    g_old_sigint = signal(SIGINT, sigint_handler);
    tcgetattr(STDIN_FILENO, &g_orig_termios);
    atexit(disable_raw);

    struct termios raw = g_orig_termios;

    raw.c_lflag &= ~(ECHO | ICANON);
    raw.c_cc[VMIN] = 1;
    raw.c_cc[VTIME] = 0;

    int ret = tcsetattr(STDIN_FILENO, TCSAFLUSH, &raw);

    if (0 == ret)
    {
        g_raw_enabled = 1;
    }

    return ret;
}

static int linenoise_edit(char *buf, int bufsize, const char *prompt)
{
    int pos = 0;
    int len = 0;

    memset(buf, 0, (size_t)bufsize);
    SAFE_WRITE(STDOUT_FILENO, prompt, strlen(prompt));

    int history_idx = g_history_len;

    while (1)
    {
        char c;
        ssize_t n = read(STDIN_FILENO, &c, 1);

        if (n <= 0)
        {
            return -1;
        }

        if (c == '\n' || c == '\r')
        {
            buf[len] = '\0';
            SAFE_WRITE(STDOUT_FILENO, "\n", 1);

            return len;
        }

        if (c == 127 || c == '\b')
        {
            if (len > 0)
            {
                len--;
                pos--;
                buf[len] = '\0';
                SAFE_WRITE(STDOUT_FILENO, "\b \b", 3);
                history_idx = g_history_len;
            }

            continue;
        }

        if (c == 27)
        {
            char seq[2];

            if (read(STDIN_FILENO, &seq[0], 1) == -1)
            {
                break;
            }

            if (read(STDIN_FILENO, &seq[1], 1) == -1)
            {
                break;
            }

            if (seq[0] == '[')
            {
                if (seq[1] == 'A')
                {
                    if (g_history_len == 0)
                    {
                        continue;
                    }

                    history_idx--;

                    if (history_idx < 0)
                    {
                        history_idx = 0;
                    }

                    const char *hist = g_history[history_idx];

                    strncpy(buf, hist, (size_t)(bufsize - 1));
                    len = pos = (int)strlen(buf);
                    SAFE_WRITE(STDOUT_FILENO, "\r\033[K", 4);
                    SAFE_WRITE(STDOUT_FILENO, prompt, strlen(prompt));
                    SAFE_WRITE(STDOUT_FILENO, buf, (size_t)len);
                }
                else if (seq[1] == 'B')
                {
                    if (g_history_len == 0)
                    {
                        continue;
                    }

                    history_idx++;

                    if (history_idx >= g_history_len)
                    {
                        history_idx = g_history_len;
                        memset(buf, 0, (size_t)bufsize);
                        len = pos = 0;
                    }
                    else
                    {
                        const char *hist = g_history[history_idx];

                        strncpy(buf, hist, (size_t)(bufsize - 1));
                        len = pos = (int)strlen(buf);
                    }

                    SAFE_WRITE(STDOUT_FILENO, "\r\033[K", 4);
                    SAFE_WRITE(STDOUT_FILENO, prompt, strlen(prompt));
                    SAFE_WRITE(STDOUT_FILENO, buf, (size_t)len);
                }
            }

            continue;
        }

        if (c == 9)
        {
            char **completions = NULL;
            int    ncomp = 0;

            if (g_completion_cb)
            {
                g_completion_cb(buf, &completions, &ncomp);

                if (ncomp > 0)
                {
                    char *last_space = NULL;

                    for (int i = pos - 1; i >= 0; i--)
                    {
                        if (' ' == buf[i])
                        {
                            last_space = &buf[i];
                            break;
                        }
                    }

                    if (last_space)
                    {
                        int keep_len = (int)(last_space - buf + 1);

                        strncpy(last_space + 1, completions[0],
                                (size_t)(bufsize - keep_len - 1));
                        buf[bufsize - 1] = '\0';
                    }
                    else
                    {
                        strncpy(buf, completions[0], (size_t)(bufsize - 1));
                    }

                    len = pos = (int)strlen(buf);
                    SAFE_WRITE(STDOUT_FILENO, "\r\033[K", 4);
                    SAFE_WRITE(STDOUT_FILENO, prompt, strlen(prompt));
                    SAFE_WRITE(STDOUT_FILENO, buf, (size_t)len);

                    for (int j = 0; j < ncomp; j++)
                    {
                        free(completions[j]);
                    }

                    free(completions);
                }
            }

            history_idx = g_history_len;

            continue;
        }

        if (len < bufsize - 1)
        {
            buf[len++] = c;
            pos++;
            SAFE_WRITE(STDOUT_FILENO, &c, 1);
            history_idx = g_history_len;
        }
    }

    return 0;
}

void linenoiseSetCompletionCallback(void (*cb)(const char *, char ***, int *))
{
    g_completion_cb = cb;
}

char *linenoise(const char *prompt)
{
    char buf[1024];

    /* 默认补全回调（首次调用或外部未设置时生效） */
    if (!g_completion_cb)
    {
        g_completion_cb = cmd_complete;
    }

    if (0 != enable_raw())
    {
        /* stdin is not a TTY (pipe/redirect) — fallback to fgets */
        if (prompt) SAFE_WRITE(STDOUT_FILENO, prompt, strlen(prompt));
        if (!fgets(buf, (int)sizeof(buf), stdin))
        {
            return NULL; /* EOF */
        }
        /* strip trailing newline */
        size_t len = strlen(buf);
        while (len > 0 && (buf[len - 1] == '\n' || buf[len - 1] == '\r'))
        {
            buf[--len] = '\0';
        }
        return strdup(buf);
    }

    int n = linenoise_edit(buf, (int)sizeof(buf), prompt);

    disable_raw();

    if (n < 0)
    {
        return NULL; /* read error or EOF */
    }

    /* empty line: return allocated empty string (not NULL) */
    char *copy = strdup(buf);

    return copy;
}

void linenoiseFree(void *ptr)
{
    free(ptr);
}

int linenoiseHistoryAdd(const char *line)
{
    if (g_history_len >= 100)
    {
        return 0;
    }

    g_history[g_history_len] = strdup(line);
    g_history_len++;

    return 1;
}

void linenoiseHistoryFree(void)
{
    for (int j = 0; j < g_history_len; j++)
    {
        free(g_history[j]);
    }

    g_history_len = 0;
}

void lineniseSetCompletionCallback(void (*cb)(const char *, char ***, int *))
{
    g_completion_cb = cb;
}

} /* extern "C" */
