#include <node.h>
#include <stdlib.h> // 为了使用 posix_openpt
#include <fcntl.h>  // 为了使用 O_RDWR 标志
#include <v8.h>
#include <unistd.h>
#include <spawn.h>
#include <sys/ioctl.h>
#include <sys/types.h>
#include <iostream>
#include <napi.h>
#include <cstring>
#include <cerrno>

#if defined(__linux__)
#include <pty.h>
#elif defined(__APPLE__)
#include <util.h>
#endif
struct termios *configAndGetTermios();

struct DelBuf
{
    int len;
    DelBuf(int len) : len(len) {}
    void operator()(char **p)
    {
        if (p == nullptr)
            return;
        for (int i = 0; i < len; i++)
            free(p[i]);
        delete[] p;
    }
};

// 输出helloworld字符串
Napi::String hello(const Napi::CallbackInfo &info)
{
    Napi::Env napiEnv(info.Env());
    return Napi::String::New(napiEnv, "Hello World");
}
//

extern char **environ;

// dup指定文件描述符
Napi::Value sysDup(const Napi::CallbackInfo &info)
{
    Napi::Env napiEnv(info.Env());
    int fd = info[0].As<Napi::Number>().Int32Value();
    int newfd = dup(fd);
    return Napi::Number::New(napiEnv, newfd);
}

Napi::Value spawn(const Napi::CallbackInfo &info)
{
    Napi::Env napiEnv(info.Env());
    Napi::HandleScope scope(napiEnv);

    if (!info[0].IsArray())
    {
        std::cerr << "[ERROR] args should be an array" << std::endl;
        return Napi::Number::New(napiEnv, -1);
    }
    // args
    Napi::Array argv_ = info[0].As<Napi::Array>();
    int argc = argv_.Length();
    int argl = argc + 4;
    std::unique_ptr<char *, DelBuf> argv_unique_ptr(new char *[argl], DelBuf(argl));
    char **argv = argv_unique_ptr.get();
    argv[argl - 1] = NULL;
    for (int i = 0; i < argc; i++)
    {
        std::string arg = argv_.Get(i).As<Napi::String>();
        argv[i] = strdup(arg.c_str());
    }

    if (info.Length() != 2 || !info[1].IsArray())
    {
        std::cerr << "[ERROR] env should be an array" << std::endl;
        return Napi::Number::New(napiEnv, -1);
    }
    // env
    Napi::Array env_ = info[1].As<Napi::Array>();
    int envc = env_.Length();
    std::unique_ptr<char *, DelBuf> env_unique_ptr(new char *[envc + 1], DelBuf(envc + 1));
    char **env = env_unique_ptr.get();
    env[envc] = NULL;
    for (int i = 0; i < envc; i++)
    {
        std::string pair = env_.Get(i).As<Napi::String>();
        env[i] = strdup(pair.c_str());
    }

    // 调用 posix_openpt, 创建主设备
    int master = posix_openpt(O_RDWR | O_NOCTTY);
    if (master == -1)
    {
        perror("posix_openpt failed");
        return Napi::Number::New(napiEnv, -1);
    }
    // 获取主设备文件状态标志
    int master_flags = fcntl(master, F_GETFL, 0);
    if (master_flags == -1)
    {
        perror("fcntl failed");
        return Napi::Number::New(napiEnv, -1);
    }
    // 利用主设备文件状态标志,并设置主设备非阻塞
    fcntl(master, F_SETFL, master_flags | O_NONBLOCK);

    // 授权从设备
    if (grantpt(master) == -1)
    {
        perror("grantpt failed");
        return Napi::Number::New(napiEnv, -1);
    }
    // 解锁从设备
    if (unlockpt(master) == -1)
    {
        perror("unlockpt failed");
        return Napi::Number::New(napiEnv, -1);
    }
    // 获取从设备(路径名称)
    char salvePtyName[256];
    int res;
    res = ioctl(master, TIOCPTYGNAME, &salvePtyName);
    if (res == -1)
    {
        perror("ioctl failed");
        return Napi::Number::New(napiEnv, -1);
    }
    // 打开从设备
    int slave = open(salvePtyName, O_RDWR | O_NOCTTY);
    if (slave == -1)
    {
        perror("open slave device failed");
        return Napi::Number::New(napiEnv, -1);
    }
    // 将spawn子进程的标准输入、标准输出、标准错误重定向到从设备
    posix_spawn_file_actions_t acts = nullptr;
    posix_spawn_file_actions_init(&acts);
    posix_spawn_file_actions_adddup2(&acts, slave, STDIN_FILENO);
    posix_spawn_file_actions_adddup2(&acts, slave, STDOUT_FILENO);
    posix_spawn_file_actions_adddup2(&acts, slave, STDERR_FILENO);
    // 新进程创建时关闭slave文件描述符，确保新进程不会使用从设备，防止资源泄露
    posix_spawn_file_actions_addclose(&acts, slave);
    // 新进程创建时关闭master文件描述符，子进程不需要使用master文件描述符
    posix_spawn_file_actions_addclose(&acts, master);

    // 设置终端属性
    struct termios *term = configAndGetTermios();
    res = tcsetattr(slave, TCSANOW, term);
    if (res == -1)
    {
        perror("tcsetattr failed");
        return Napi::Number::New(napiEnv, -1);
    };

    // 设置窗口大小
    struct winsize win = {24, 80, 0, 0};
    if (ioctl(slave, TIOCSWINSZ, &win) == -1)
    {
        perror("ioctl TIOCSWINSZ");
        return Napi::Number::New(napiEnv, -1);
    }
    // 设置spawn子进程的属性
    posix_spawnattr_t attrs = nullptr;
    posix_spawnattr_init(&attrs);
    // POSIX_SPAWN_CLOEXEC_DEFAULT: 这个标志指示在新进程中，所有打开的文件描述符在执行新程序时都将被关闭。这有助于防止子进程意外地使用父进程打开的文件描述符，从而避免资源泄漏。
    // POSIX_SPAWN_SETSIGDEF: 这个标志指示在新进程中设置默认的信号处理程序。它会将所有信号的处理程序重置为默认行为，确保新进程不会继承父进程的信号处理设置。
    // POSIX_SPAWN_SETSIGMASK: 这个标志指示在新进程中设置信号屏蔽字。它允许您指定新进程在创建时应该屏蔽哪些信号，确保新进程在启动时不会受到某些信号的干扰。
    // POSIX_SPAWN_SETSID: 这个标志指示在新进程中创建一个新的会话。新进程将成为会话的领导者，并且它的进程组 ID 将与会话 ID 相同。这通常用于确保新进程与父进程的控制不再相互影响，特别是在处理终端时。
    int flags = POSIX_SPAWN_CLOEXEC_DEFAULT |
                POSIX_SPAWN_SETSIGDEF |
                POSIX_SPAWN_SETSIGMASK |
                POSIX_SPAWN_SETSID;
    posix_spawnattr_setflags(&attrs, flags);

    // 设置子进程信号集
    sigset_t signal_set;
    /* Reset all signal the child to their default behavior */
    sigfillset(&signal_set);
    posix_spawnattr_setsigdefault(&attrs, &signal_set);

    /* Reset the signal mask for all signals */
    sigemptyset(&signal_set);
    posix_spawnattr_setsigmask(&attrs, &signal_set);

    std::string cmd = "/bin/bash";
    pid_t pid;

    int ret;
    do
    {
        ret = posix_spawn(&pid, cmd.c_str(), &acts, &attrs, argv, env);
        if (ret != 0)
        {
            std::cerr << "posix_spawn failed: " << strerror(ret) << std::endl;
            return Napi::Number::New(napiEnv, -1);
        }
    } while (ret == EINTR);

    if (pid > 0)
    {
        return Napi::Number::New(napiEnv, master);
    }
}

struct termios *configAndGetTermios()
{
    struct termios t = termios();
    struct termios *term = &t;
    term->c_iflag = ICRNL | IXON | IXANY | IMAXBEL | BRKINT;
    term->c_oflag = OPOST | ONLCR;
    term->c_cflag = CREAD | CS8 | HUPCL;
    term->c_lflag = ICANON | ISIG | IEXTEN | ECHO | ECHOE | ECHOK | ECHOKE | ECHOCTL;

    term->c_cc[VEOF] = 4;
    term->c_cc[VEOL] = -1;
    term->c_cc[VEOL2] = -1;
    term->c_cc[VERASE] = 0x7f;
    term->c_cc[VWERASE] = 23;
    term->c_cc[VKILL] = 21;
    term->c_cc[VREPRINT] = 18;
    term->c_cc[VINTR] = 3;
    term->c_cc[VQUIT] = 0x1c;
    term->c_cc[VSUSP] = 26;
    term->c_cc[VSTART] = 17;
    term->c_cc[VSTOP] = 19;



    term->c_cc[VLNEXT] = 22;
    term->c_cc[VDISCARD] = 15;




    term->c_cc[VMIN] = 1;
    term->c_cc[VTIME] = 0;
#if (__APPLE__)
    // term->c_cc[VDSUSP] = 25;
    // term->c_cc[VSTATUS] = 20;
#endif
    // 设置波特率
    cfsetispeed(term, B38400);
    cfsetospeed(term, B38400);
    return term;
}

Napi::Value ExecuteLs(const Napi::CallbackInfo &info)
{
    Napi::Env env = info.Env();

    pid_t pid;
    int status;
    const char *argv[] = {"ls", "-l", nullptr};

    int ret = posix_spawn(&pid, "/bin/ls", nullptr, nullptr, const_cast<char *const *>(argv), environ);
    if (ret != 0)
    {
        return Napi::Number::New(env, ret);
    }

    waitpid(pid, &status, 0);

    return Napi::Number::New(env, WEXITSTATUS(status));
}

Napi::Object init(Napi::Env env, Napi::Object exports)
{
    exports.Set("hello", Napi::Function::New(env, hello, "hello"));
    exports.Set("spawn", Napi::Function::New(env, spawn, "spawn"));
    exports.Set("executeLs", Napi::Function::New(env, ExecuteLs, "executeLs"));
    exports.Set("sysDup", Napi::Function::New(env, sysDup, "sysDup"));
    return exports;
}

NODE_API_MODULE(pty, init);

// // 父进程
// int status;
// waitpid(pid, &status, 0);
// if (WIFEXITED(status))
// {
//     int exitCode = WEXITSTATUS(status);
//     std::cout << "Child exited with code: " << exitCode << std::endl;
//     return Napi::Number::New(napiEnv, master);
// }
// else
// {
//     std::cout << "Child did not exit normally." << std::endl;
//     return Napi::Number::New(napiEnv, -1);
// }