#ifndef GC_JSON_H
#define GC_JSON_H
#include <stdbool.h>
#include <stdint.h>

/**
 * @brief The JSON types available
 */
enum eGCJsonType
{
    GC_JSON_TYPE_NULL,
    GC_JSON_TYPE_BOOL,
    GC_JSON_TYPE_INT,
    GC_JSON_TYPE_DOUBLE,
    GC_JSON_TYPE_STRING,
    GC_JSON_TYPE_ARRAY,
    GC_JSON_TYPE_OBJECT
};
typedef enum eGCJsonType GCJsonType;

/**
 * @brief The result of a json callback
 */
enum eResultCallback
{
    GC_JSON_OK,
    GC_JSON_ERROR
};
typedef enum eResultCallback GCResultCallback;

/**
 * @brief The JSON object
 *
 * Every JSON object is represented by a pointer to a struct of this type.
 */
struct sGCJsonObject
{
    /// The type of the JSON object
    GCJsonType type;

    /// The pointer to the underlying JSON object
    const void* ptr;
};
typedef struct sGCJsonObject GCJsonObject;
typedef const char* GCString;

typedef bool (*tJsonObjectGetBool)(const GCJsonObject obj, GCResultCallback* result);
typedef int64_t (*tJsonObjectGetInt)(const GCJsonObject obj, GCResultCallback* result);
typedef double (*tJsonObjectGetDouble)(const GCJsonObject obj, GCResultCallback* result);
typedef char* (*tJsonObjectGetString)(const GCJsonObject obj, GCResultCallback* result);
typedef char* (*tJsonObjectSerialize)(const GCJsonObject obj, GCResultCallback* result);
typedef const GCJsonObject (*tJsonObjectGetArrayIdx)(const GCJsonObject obj, int64_t idx, GCResultCallback* result);
typedef const int64_t (*tJsonObjectGetArrayLength)(const GCJsonObject obj, GCResultCallback* result);
typedef const GCJsonObject (*tJsonObjectGetObjectField)(const GCJsonObject obj, const char* fieldName,
                                                        GCResultCallback* result);
typedef void (*tFreeSring)(GCString);

/**
 * @brief The JSON configuration data and parsing callbacks
 *
 * This struct holds the JSON configuration data and the parsing callbacks that the plugin can use to parse the
 * configuration.
 */
struct sGCJsonCallbacks
{
    /**
     * @brief Parse a boolean from a JSON object
     *
     * @param obj The JSON object
     * @param result The result of the operation
     */
    tJsonObjectGetBool getBool;

    /**
     * @brief Parse an integer from a JSON object
     *
     * @param obj The JSON object
     * @param result The result of the operation
     */
    tJsonObjectGetInt getInt;

    /**
     * @brief Parse a double from a JSON object
     *
     * @param obj The JSON object
     * @param result The result of the operation
     */
    tJsonObjectGetDouble getDouble;

    /**
     * @brief Parse a string from a JSON object
     *
     * @param obj The JSON object
     * @param result The result of the operation
     * @return The string parsed from the JSON object or NULL if an error occurred
     * @note The plugin **must** call @ref freeString on the returned string if it is not null. Not doing so will result
     * in a memory leak.
     */
    tJsonObjectGetString getString;

    /**
     * @brief Get an element from a JSON array
     *
     * @param obj The JSON object
     * @param idx The index of the element to get
     * @param result The result of the operation
     * @return The element at the specified index or NULL if an error occurred
     */
    tJsonObjectGetArrayIdx getArrayIdx;

    /**
     * @brief Get the length of a JSON array
     *
     * @param obj The JSON object
     * @param result The result of the operation
     * @return The length of the array or -1 if an error occurred
     */
    tJsonObjectGetArrayLength getArrayLength;

    /**
     * @brief Get a field from a JSON object
     *
     * @param obj The JSON object
     * @param fieldName The name of the field to get
     * @param result The result of the operation
     * @return The field or NULL if an error occurred
     */
    tJsonObjectGetObjectField getObjectField;

    /**
     * @brief Serialize a JSON object to a string
     *
     * @param obj The JSON object
     * @param result The result of the operation
     * @return The serialized JSON object or NULL if an error occurred
     * @note The plugin **must** call @ref freeString on the returned string if it is not null. Not doing so will result
     * in a memory leak.
     */
    tJsonObjectSerialize getObjectSerialized;

    /**
     * @brief Free a string
     *
     * Free a string returned by @ref getString or @ref getObjectSerialized.
     *
     * @param str The string to free
     */
    tFreeSring freeString;

    /**
     * @brief The plugin specific configuration
     *
     * Represents the top level JSON object that contains the plugin specific configuration set by the user.
     */
    const GCJsonObject data;
};
typedef const struct sGCJsonCallbacks* GCJsonCallbacks;
#endif // GC_JSON_H
