#include <jni.h>
#include <cstdarg>
#include <cstddef>
#include <fcntl.h>
#include <sys/syscall.h>
#include <unistd.h>
#include "dobby.h"
#include <android/log.h>
#define LOG_TAG "demo_native"
#define LOGI(...) __android_log_print(ANDROID_LOG_INFO, LOG_TAG, __VA_ARGS__)


static int (*orig_openat)(int dirfd, const char *pathname, int flags, ...) = nullptr;

int my_openat(int dirfd, const char *pathname, int flags, ...) {
    mode_t mode = 0;

    if (flags & O_CREAT) {
        va_list args;
        va_start(args, flags);
        mode = va_arg(args, mode_t);
        va_end(args);

        LOGI("[HOOK] openat called: dirfd=%d, path=%s, flags=0x%x, mode=0%o\n",
               dirfd, pathname ? pathname : "null", flags, mode);

        int fd = orig_openat(dirfd, pathname, flags, mode);

        LOGI("[HOOK] openat return: fd=%d\n", fd);
        return fd;
    } else {
        LOGI("[HOOK] openat called: dirfd=%d, path=%s, flags=0x%x\n",
               dirfd, pathname ? pathname : "null", flags);

        int fd = orig_openat(dirfd, pathname, flags);

        LOGI("[HOOK] openat return: fd=%d\n", fd);
        return fd;
    }
}

void hook_openat() {
    void *sym = DobbySymbolResolver(nullptr, "openat");
    if (!sym) {
        LOGI("[HOOK] failed to find symbol: openat\n");
        return;
    }

    if (DobbyHook(sym, (void *)my_openat, (void **)&orig_openat) == 0) {
        LOGI("[HOOK] openat hooked success, sym=%p, orig=%p\n", sym, orig_openat);
    } else {
        LOGI("[HOOK] openat hook failed\n");
    }
}

static void verify_openat_hook() {
    char buf[256] = {0};
    int fd = openat(AT_FDCWD, "/proc/self/maps", O_RDONLY, 0);
    if (fd < 0) {
        LOGI("[VERIFY] openat /proc/self/maps failed, fd=%d\n", fd);
        return;
    }

    ssize_t n = read(fd, buf, sizeof(buf) - 1);
    close(fd);

    if (n <= 0) {
        LOGI("[VERIFY] read /proc/self/maps failed, n=%zd\n", n);
        return;
    }

    buf[n] = '\0';
    LOGI("[VERIFY] /proc/self/maps first bytes:\n%s\n", buf);
}

jint JNICALL JNI_OnLoad(JavaVM *vm, void *reserved) {
    (void) vm;
    (void) reserved;
    hook_openat();
    verify_openat_hook();
    return JNI_VERSION_1_6;
}
