#pragma once
#include "frida-gum.h"
#include <fcntl.h>
#include <unistd.h>
#include "squirrel.h"
#include "sqstdaux.h"
#include "sqstdblob.h"
#include "sqstdio.h"
#include "sqstdmath.h"
#include "sqstdstring.h"
#include "sqstdsystem.h"
#include <mutex>
#include <iostream>

extern HSQUIRRELVM v;
extern std::recursive_mutex SqMtx;

#define CONTAINS_STRING(str, substr) (str == substr)

typedef struct {
    const SQChar* name;
    size_t offset;
} CpuContextMember;

// HookFunc
struct _ExampleListener
{
    GObject parent;
    guint num_calls;
    std::vector<std::string> ArgumentCount;
    std::string RetType;
    HSQOBJECT CallBackOnEnter;
    HSQOBJECT CallBackOnLeave;
    SQUserPointer FuncAddress;
};
enum _ExampleHookId
{
    EXAMPLE_HOOK_OPEN,
    EXAMPLE_HOOK_CLOSE
};
typedef struct _ExampleListener ExampleListener;
typedef enum _ExampleHookId ExampleHookId;

static void example_listener_iface_init(gpointer g_iface, gpointer iface_data);

static GumInterceptor* interceptor;

static std::thread::id Zhuxianc = std::this_thread::get_id();

#define EXAMPLE_TYPE_LISTENER (example_listener_get_type())
G_DECLARE_FINAL_TYPE(ExampleListener, example_listener, EXAMPLE, LISTENER, GObject)
G_DEFINE_TYPE_EXTENDED(ExampleListener, example_listener, G_TYPE_OBJECT, 0, G_IMPLEMENT_INTERFACE(GUM_TYPE_INVOCATION_LISTENER, example_listener_iface_init))

// 自定义替换函数
static void replacement_function(GumInvocationContext* context)
{
    // 在这里执行你的逻辑
    // gum_print("自定义逻辑已执行!\n");
    // 跳过原函数，直接返回
    // 如果需要修改返回值，可以通过 context->cpu_context 修改寄存器
}

static SQInteger L_HookFunc(HSQUIRRELVM v)
{
    static bool InitFlag = false;
    if (!InitFlag)
    {
        InitFlag = true;

        gum_init_embedded();

        interceptor = gum_interceptor_obtain();
    }

    GumInvocationListener* listener = (GumInvocationListener*)g_object_new(EXAMPLE_TYPE_LISTENER, NULL);
    // 得到函数地址
    SQUserPointer FuncAddress;
    sq_getuserpointer(v, 2, &FuncAddress);

    // 遍历参数类型数组
    std::vector<std::string> ParameterType;
    sq_pushnull(v); // null iterator
    while (SQ_SUCCEEDED(sq_next(v, 3)))
    {
        const SQChar* path;
        sq_getstring(v, -1, &path);
        ParameterType.push_back(path);
        sq_pop(v, 2);
    }
    sq_pop(v, 1);

    // 得到入口回调函数
    HSQOBJECT CallBackOnEnter;
    sq_getstackobj(v, 4, &CallBackOnEnter);
    // 必须增加一次引用一会记得删除 不然这个函数会被释放
    sq_addref(v, &CallBackOnEnter);

    // 得到出口回调函数
    HSQOBJECT CallBackOnLeave;
    sq_getstackobj(v, 5, &CallBackOnLeave);
    // 必须增加一次引用一会记得删除 不然这个函数会被释放
    sq_addref(v, &CallBackOnLeave);

    EXAMPLE_LISTENER(listener)->ArgumentCount = ParameterType;
    // EXAMPLE_LISTENER(listener)->RetType = std::string(RetBuf);
    EXAMPLE_LISTENER(listener)->CallBackOnEnter = CallBackOnEnter;
    EXAMPLE_LISTENER(listener)->CallBackOnLeave = CallBackOnLeave;
    EXAMPLE_LISTENER(listener)->FuncAddress = FuncAddress;

    gum_interceptor_begin_transaction(interceptor);
    gum_interceptor_attach(interceptor, GSIZE_TO_POINTER(FuncAddress), listener, GSIZE_TO_POINTER(FuncAddress));
    gum_interceptor_end_transaction(interceptor);

    sq_pushuserpointer(v, listener);
    return 1;
}

static SQInteger L_DeHookFunc(HSQUIRRELVM v)
{
    // 得到函数地址
    SQUserPointer FuncAddress;
    sq_getuserpointer(v, 2, &FuncAddress);

    GumInvocationListener* listener = (GumInvocationListener*)FuncAddress;
    gum_interceptor_detach(interceptor, listener);

    return 0;
}


static void example_listener_on_enter(GumInvocationListener* listener, GumInvocationContext* ic)
{

    gpointer ReturnAddress = gum_invocation_context_get_return_address(ic);
    ExampleListener* self = EXAMPLE_LISTENER(listener);

    // std::cout << "调用点:  " << self->FuncAddress << std::endl;

    std::lock_guard<std::recursive_mutex> lock(SqMtx);
    // 执行虚拟机Main函数
    SQInteger top = sq_gettop(v); // saves the stack size before the call

    //传入返回地址
    sq_pushroottable(v);
    sq_pushstring(v, _SC("_Haker_SetNextReturnAddress"), -1);
    if (SQ_SUCCEEDED(sq_get(v, -2)))
    {
        sq_pushroottable(v);
        sq_pushuserpointer(v, ReturnAddress);
        sq_call(v, 2, SQFalse, SQTrue);
    }
    sq_settop(v, top);

    //传入CPU上下文
    sq_pushroottable(v);
    sq_pushstring(v, _SC("_Haker_SetCpuContext"), -1);
    if (SQ_SUCCEEDED(sq_get(v, -2)))
    {
        sq_pushroottable(v);
        sq_newtable(v);
        struct TableEntry
        {
            const SQChar* key;
            SQUserPointer value;
        } entries[] = {
            {_SC("eip"), (SQUserPointer)ic->cpu_context->eip},
            {_SC("edi"), (SQUserPointer)ic->cpu_context->edi},
            {_SC("esi"), (SQUserPointer)ic->cpu_context->esi},
            {_SC("ebp"), (SQUserPointer)ic->cpu_context->ebp},
            {_SC("esp"), (SQUserPointer)ic->cpu_context->esp},
            {_SC("ebx"), (SQUserPointer)ic->cpu_context->ebx},
            {_SC("edx"), (SQUserPointer)ic->cpu_context->edx},
            {_SC("ecx"), (SQUserPointer)ic->cpu_context->ecx},
            {_SC("eax"), (SQUserPointer)ic->cpu_context->eax},
             };

        for (const auto& entry : entries)
        {
            sq_pushstring(v, entry.key, -1);
            sq_pushuserpointer(v, entry.value);
            sq_newslot(v, -3, SQFalse);
        }
        sq_call(v, 2, SQFalse, SQTrue);
    }
    sq_settop(v, top);



    sq_pushobject(v, self->CallBackOnEnter);
    sq_pushroottable(v);
    sq_newarray(v, 0);
    for (size_t i = 0; i < self->ArgumentCount.size() - 1; i++)
    {
        void* Abuf = gum_invocation_context_get_nth_argument(ic, i);

        if (CONTAINS_STRING(self->ArgumentCount[i], "int"))
            sq_pushinteger(v, (int)Abuf);
        else if (CONTAINS_STRING(self->ArgumentCount[i], "float"))
        {
            SQFloat* a = reinterpret_cast<float*>(&Abuf);
            sq_pushfloat(v, *(SQFloat*)a);
        }
        else if (CONTAINS_STRING(self->ArgumentCount[i], "bool"))
            sq_pushbool(v, (bool)Abuf);
        else if (CONTAINS_STRING(self->ArgumentCount[i], "string"))
            sq_pushstring(v, (char*)Abuf, -1);
        else if (CONTAINS_STRING(self->ArgumentCount[i], "pointer"))
            sq_pushuserpointer(v, (void*)Abuf);
        else if (CONTAINS_STRING(self->ArgumentCount[i], "short"))
            sq_pushinteger(v, (int)Abuf);
        else if (CONTAINS_STRING(self->ArgumentCount[i], "char"))
            sq_pushinteger(v, (int)Abuf);
        else
            sq_pushuserpointer(v, (void*)Abuf);
        sq_arrayappend(v, -2);
    }
    sq_call(v, 2, SQTrue, SQTrue); // calls the function
    if (sq_gettype(v, -1) == OT_ARRAY)
    {
        sq_pushnull(v); // null iterator
        int Idx = 0;
        while (SQ_SUCCEEDED(sq_next(v, -2)))
        {
            if (CONTAINS_STRING(self->ArgumentCount[Idx], "int"))
            {
                SQInteger Abuf;
                sq_getinteger(v, -1, &Abuf);
                gum_invocation_context_replace_nth_argument(ic, Idx, (void*)Abuf);
            }
            else if (CONTAINS_STRING(self->ArgumentCount[Idx], "float"))
            {
                SQFloat Abuf;
                sq_getfloat(v, -1, &Abuf);
                gum_invocation_context_replace_nth_argument(ic, Idx, &Abuf);
            }
            else if (CONTAINS_STRING(self->ArgumentCount[Idx], "bool"))
            {
                SQBool Abuf;
                sq_getbool(v, -1, &Abuf);
                gum_invocation_context_replace_nth_argument(ic, Idx, (void*)Abuf);
            }
            else if (CONTAINS_STRING(self->ArgumentCount[Idx], "string"))
            {
                const SQChar* Abuf;
                sq_getstring(v, -1, &Abuf);
                gum_invocation_context_replace_nth_argument(ic, Idx, (void*)Abuf);
            }
            else if (CONTAINS_STRING(self->ArgumentCount[Idx], "pointer"))
            {
                SQUserPointer Abuf;
                sq_getuserpointer(v, -1, &Abuf);
                gum_invocation_context_replace_nth_argument(ic, Idx, (void*)Abuf);
            }
            else if (CONTAINS_STRING(self->ArgumentCount[Idx], "short"))
            {
                SQInteger Abuf;
                sq_getinteger(v, -1, &Abuf);
                gum_invocation_context_replace_nth_argument(ic, Idx, (void*)Abuf);
            }
            else if (CONTAINS_STRING(self->ArgumentCount[Idx], "char"))
            {
                SQInteger Abuf;
                sq_getinteger(v, -1, &Abuf);
                char CBUF = Abuf;
                gum_invocation_context_replace_nth_argument(ic, Idx, (void*)CBUF);
            }
            Idx++;
            sq_pop(v, 2);
        }
        sq_pop(v, 1);
        sq_settop(v, top); // restores the original stack size
        return;
    }
    sq_settop(v, top); // restores the original stack size
    return;
}
static void example_listener_on_leave(GumInvocationListener* listener, GumInvocationContext* ic)
{
    ExampleListener* self = EXAMPLE_LISTENER(listener);

    std::lock_guard<std::recursive_mutex> lock(SqMtx);
    // 执行虚拟机Main函数
    SQInteger top = sq_gettop(v); // saves the stack size before the call
    sq_pushobject(v, self->CallBackOnLeave);
    sq_pushroottable(v);
    sq_newarray(v, 0);
    for (size_t i = 0; i < self->ArgumentCount.size(); i++)
    {
        void* Abuf;

        if (i == (self->ArgumentCount.size() - 1))
            Abuf = gum_invocation_context_get_return_value(ic);
        else
            Abuf = gum_invocation_context_get_nth_argument(ic, i);

        if (CONTAINS_STRING(self->ArgumentCount[i], "int"))
            sq_pushinteger(v, (int)Abuf);
        else if (CONTAINS_STRING(self->ArgumentCount[i], "float"))
        {
            SQFloat* a = reinterpret_cast<float*>(&Abuf);
            sq_pushfloat(v, *(SQFloat*)a);
        }
        else if (CONTAINS_STRING(self->ArgumentCount[i], "bool"))
            sq_pushbool(v, (bool)Abuf);
        else if (CONTAINS_STRING(self->ArgumentCount[i], "string"))
            sq_pushstring(v, (char*)Abuf, -1);
        else if (CONTAINS_STRING(self->ArgumentCount[i], "pointer"))
            sq_pushuserpointer(v, (void*)Abuf);
        else if (CONTAINS_STRING(self->ArgumentCount[i], "short"))
            sq_pushinteger(v, (int)Abuf);
        else if (CONTAINS_STRING(self->ArgumentCount[i], "char"))
            sq_pushinteger(v, (int)Abuf);
        else
            sq_pushuserpointer(v, (void*)Abuf);
        sq_arrayappend(v, -2);
    }
    sq_call(v, 2, SQTrue, SQTrue); // calls the function
    if (sq_gettype(v, -1) != OT_NULL)
    {
        if (CONTAINS_STRING(self->ArgumentCount.back(), "int"))
        {
            SQInteger Abuf;
            sq_getinteger(v, -1, &Abuf);
            gum_invocation_context_replace_return_value(ic, (void*)Abuf);
        }
        else if (CONTAINS_STRING(self->ArgumentCount.back(), "float"))
        {
            SQFloat Abuf;
            sq_getfloat(v, -1, &Abuf);
            gum_invocation_context_replace_return_value(ic, &Abuf);
        }
        else if (CONTAINS_STRING(self->ArgumentCount.back(), "bool"))
        {
            SQBool Abuf;
            sq_getbool(v, -1, &Abuf);
            gum_invocation_context_replace_return_value(ic, (void*)Abuf);
        }
        else if (CONTAINS_STRING(self->ArgumentCount.back(), "string"))
        {
            const SQChar* Abuf;
            sq_getstring(v, -1, &Abuf);
            gum_invocation_context_replace_return_value(ic, (void*)Abuf);
        }
        else if (CONTAINS_STRING(self->ArgumentCount.back(), "pointer"))
        {
            SQUserPointer Abuf;
            sq_getuserpointer(v, -1, &Abuf);
            gum_invocation_context_replace_return_value(ic, (void*)Abuf);
        }
        else if (CONTAINS_STRING(self->ArgumentCount.back(), "short"))
        {
            SQInteger Abuf;
            sq_getinteger(v, -1, &Abuf);
            gum_invocation_context_replace_return_value(ic, (void*)Abuf);
        }
        else if (CONTAINS_STRING(self->ArgumentCount.back(), "char"))
        {
            SQInteger Abuf;
            sq_getinteger(v, -1, &Abuf);
            char CBUF = Abuf;
            gum_invocation_context_replace_return_value(ic, (void*)Abuf);
        }
    }
    sq_settop(v, top);
}
static void example_listener_class_init(ExampleListenerClass* klass)
{
    (void)EXAMPLE_IS_LISTENER;
    (void)glib_autoptr_cleanup_ExampleListener;
}
static void example_listener_iface_init(gpointer g_iface, gpointer iface_data)
{
    GumInvocationListenerInterface* iface = (GumInvocationListenerInterface*)g_iface;
    iface->on_enter = example_listener_on_enter;
    iface->on_leave = example_listener_on_leave;
}
static void example_listener_init(ExampleListener* self)
{
}

static void RegisterActiveHook(HSQUIRRELVM v)
{
    register_World_func(v, L_HookFunc, _SC("Sq_HookFunc"));
    register_World_func(v, L_DeHookFunc, _SC("Sq_DeHookFunc"));
}