#ifndef FFIOBJECT_H
#define FFIOBJECT_H

#include <stdint.h>

typedef enum
{
    FFIVAL_NIL,
    FFIVAL_CHAR,
    FFIVAL_FLOAT,
    FFIVAL_INT,
    FFIVAL_STRING,
    FFIVAL_OBJECT,
    FFIVAL_ARRAY,
    FFIVAL_BOOL,
    FFIVAL_BLOCK,
    FFIVAL_FUNC,
} FfiObjectType;

typedef struct FfiObject FfiObject;
typedef FfiObject (*FFIFunc)(FfiObject *args, int argc);

typedef struct FfiKV
{
    char *key;
    struct FfiObject *value;
} FfiKV;

struct FfiObject
{
    FfiObjectType type;
    uint32_t flags;

    union
    {

        double fval;
        int64_t ival;
        FFIFunc fn;

        struct
        {
            char *data;
            uint32_t length;
        } string;

        struct
        {
            struct FfiKV *pairs;
            uint32_t length;
        } object;

        struct
        {
            struct FfiObject **elements;
            uint32_t length;
        } array;

        struct
        {
            void *memory;
            uint8_t block_size;
            uint32_t block_count;
        } block;
    };
};

// Helper-macros for FfiObject creation
#define IS_INT(v) ((v).type == FFIVAL_INT)
#define IS_FLOAT(v) ((v).type == FFIVAL_FLOAT)
#define IS_STRING(v) ((v).type == FFIVAL_STRING)
#define IS_BOOL(v) ((v).type == FFIVAL_BOOL)
#define IS_NIL(v) ((v).type == FFIVAL_NIL)
#define IS_ARRAY(v) ((v).type == FFIVAL_ARRAY)
#define IS_OBJECT(v) ((v).type == FFIVAL_OBJECT)
#define IS_BLOCK(v) ((v).type == FFIVAL_BLOCK)

#define AS_INT(v) ((v).ival)
#define AS_FLOAT(v) ((v).fval)
#define AS_STRING(v) ((v).string.data)
#define AS_BOOL(v) ((v).ival != 0)

#define REQUIRE_ARGC(n)        \
    do                         \
    {                          \
        if (argc < (n))        \
            return make_nil(); \
    } while (0)
#define REQUIRE_TYPE(i, t)       \
    do                           \
    {                            \
        if (args[i].type != (t)) \
            return make_nil();   \
    } while (0)

#endif