// ----------------------------------------------------------------------------
// <auto-generated>
// This is autogenerated code by CppSharp.
// Do not edit this file or all your changes will be lost after re-generation.
// </auto-generated>
// ----------------------------------------------------------------------------

extern "C"
{

#include <quickjs.h>
#include <cutils.h>

#if defined(__GNUC__) || defined(__clang__)
#define js_force_inline inline __attribute__((always_inline))
#else
#define js_force_inline inline
#endif

#define countof(x) (sizeof(x) / sizeof((x)[0]))

    enum
    {
        __JS_ATOM_NULL = JS_ATOM_NULL,
#define DEF(name, str) JS_ATOM_##name,
#include <quickjs-atom.h>
#undef DEF
        JS_ATOM_END,
    };

    struct JS_SignalContext
    {
        // TODO: List
        JSValue function;
        JSValue link;
        JSContext* ctx;
    };

    typedef int JS_EventId;
    typedef int JS_ClassId;
    typedef DynBuf JS_EventMap;

    struct JS_EventEntry
    {
        JS_EventId eventId;
        JSValue value;
    };

    static JS_EventMap* JS_Interop_InitEventMap(JS_EventMap* map)
    {
        dbuf_init(map);
        return map;
    }

    static void JS_Interop_FreeEventMap(JSContext* ctx, JS_EventMap* map)
    {
        JS_EventEntry* events = (JS_EventEntry*)map->buf;
        int numEvents = (map->size / sizeof(JS_EventEntry));
        for (int i = 0; i < numEvents; i++)
            if (!JS_IsUndefined(events[i].value))
                JS_FreeValue(ctx, events[i].value);

        dbuf_free(map);
    }

    static int JS_Interop_BinarySearch(JS_EventEntry* arr, int l, int r, JS_EventId id)
    {
        if (r >= l)
        {
            int mid = l + (r - l) / 2;

            // If the element is present at the middle itself
            if (arr[mid].eventId == id)
                return mid;

            // If element is smaller than mid, then it can only be present in left subarray
            if (arr[mid].eventId > id)
                return JS_Interop_BinarySearch(arr, l, mid - 1, id);

            // Else the element can only be present in right subarray
            return JS_Interop_BinarySearch(arr, mid + 1, r, id);
        }

        // We reach here when element is not present in array
        return -1;
    }

    static JSValue JS_Interop_FindEvent(JS_EventMap* map, JS_EventId eventId)
    {
        JS_EventEntry* events = (JS_EventEntry*)map->buf;
        int numEvents = (map->size / sizeof(JS_EventEntry));
        if (numEvents == 0)
            return JS_UNDEFINED;

        int index = JS_Interop_BinarySearch(events, 0, numEvents, eventId);
        return index >= 0 ? events[index].value : JS_UNDEFINED;
    }

    static int JS_Interop_InsertEvent(JS_EventMap* map, JS_EventId eventId, JSValue value)
    {
        JS_EventEntry* events = (JS_EventEntry*)map->buf;
        int numEvents = (map->size / sizeof(JS_EventEntry));

        int index = 0;
        for (int i = 0; i < numEvents; i++)
        {
            if (events[i].eventId == eventId)
            {
                index = -1;
                break;
            }
            if (events[i].eventId > eventId)
            {
                index = i;
                break;
            }
            index++;
        }

        if (index == -1)
            return -1;

        JS_EventEntry entry;
        entry.eventId = eventId;
        entry.value = value;

        dbuf_write(map, index * sizeof(JS_EventEntry), (const uint8_t*)&entry,
                   sizeof(JS_EventEntry));

        return 0;
    }

    static JSValue JS_Interop_InvokeConstructor(JSContext* ctx, JS_ClassId eventId, JSValue argv[], int argc)
    {
        // TODO: Error handling.
        JSValue proto = JS_GetClassProto(ctx, eventId);
        JSValue ctor = JS_GetProperty(ctx, proto, JS_ATOM_constructor);
        JSValue instance = JS_CallConstructor(ctx, ctor, argc, argv);
        JS_FreeValue(ctx, ctor);
        JS_FreeValue(ctx, proto);

        return instance;
    }

    enum JS_Interop_InstanceKind
    {
        JS_INTEROP_INSTANCE_RAW_POINTER = 1,
        JS_INTEROP_INSTANCE_SIGNAL_CONTEXT,
    };

    struct JS_Interop_ClassData
    {
        void* instance;
        JSContext* ctx;
        JS_EventMap events;
    };

    static JSValue JS_Interop_InitObject(JSContext* ctx, JSValue obj, JS_Interop_InstanceKind kind, void* instance)
    {
        switch (kind)
        {
            case JS_INTEROP_INSTANCE_RAW_POINTER:
                JS_SetOpaque(obj, instance);
                return obj;
            case JS_INTEROP_INSTANCE_SIGNAL_CONTEXT:
                JS_Interop_ClassData* data = (JS_Interop_ClassData*)js_mallocz(ctx,
                                                                               sizeof(JS_Interop_ClassData));
                data->ctx = ctx;
                data->instance = instance;
                JS_Interop_InitEventMap(&data->events);
                JS_SetOpaque(obj, data);
                return obj;
        }

        return JS_UNDEFINED;
    }

    static JSValue JS_Interop_CleanupObject(JSValue obj, JS_Interop_InstanceKind kind)
    {
        switch (kind)
        {
            case JS_INTEROP_INSTANCE_SIGNAL_CONTEXT:
            {
                JS_Interop_ClassData* data = (JS_Interop_ClassData*)JS_GetOpaque(obj, JS_GetClassID(obj));
                if (data)
                {
                    JS_Interop_FreeEventMap(data->ctx, &data->events);
                    js_free(data->ctx, data);
                }
                break;
            }
            case JS_INTEROP_INSTANCE_RAW_POINTER:
                break;
        }

        return JS_UNDEFINED;
    }

    static void* JS_Interop_GetInstance(JSValue obj, JS_ClassId classId, JS_Interop_InstanceKind kind)
    {
        switch (kind)
        {
            case JS_INTEROP_INSTANCE_RAW_POINTER:
                return JS_GetOpaque(obj, classId);
            case JS_INTEROP_INSTANCE_SIGNAL_CONTEXT:
                JS_Interop_ClassData* data = (JS_Interop_ClassData*)JS_GetOpaque(obj, classId);
                return data != nullptr ? data->instance : nullptr;
        }

        return nullptr;
    }

    static JSValue JS_Interop_CreateFromInstance(JSContext* ctx, JS_ClassId classId, JS_Interop_InstanceKind kind, void* instance)
    {
        if (instance == nullptr)
            return JS_NULL;

        // TODO: Error handling.
        JSValue obj = JS_NewObjectClass(ctx, classId);
        JS_Interop_InitObject(ctx, obj, kind, instance);

        return obj;
    }

    static inline JS_BOOL JS_IsInt(JSValueConst v)
    {
        return JS_IsNumber(v);
    }

    static inline JS_BOOL JS_IsInt8(JSValueConst v)
    {
        return JS_IsInt(v);
    }

    static inline JS_BOOL JS_IsUInt8(JSValueConst v)
    {
        return JS_IsInt(v);
    }

    static inline JS_BOOL JS_IsInt16(JSValueConst v)
    {
        return JS_IsInt(v);
    }

    static inline JS_BOOL JS_IsUInt16(JSValueConst v)
    {
        return JS_IsInt(v);
    }

    static inline JS_BOOL JS_IsInt32(JSValueConst v)
    {
        return JS_IsInt(v);
    }

    static inline JS_BOOL JS_IsUInt32(JSValueConst v)
    {
        return JS_IsInt(v);
    }

    static inline JS_BOOL JS_IsFloat(JSValueConst v)
    {
        int tag = JS_VALUE_GET_TAG(v);
        return JS_TAG_IS_FLOAT64(tag);
    }

    void register_CppSharp_QuickJS(JSContext* ctx, JSModuleDef* m, bool set, int phase);

#undef js_force_inline

} // extern "C"