#ifndef JSON_H
#define JSON_H

#include <stddef.h>
#include <stdbool.h>

/*----------------------------------------------------------------------------
 * Gerenate JSON elements
 */

/* structure representing any JSON element */
struct json;

/* create a JSON null */
struct json *json_null(void);

/* create a JSON boolean */
struct json *json_bool(bool);

/* create a JSON number */
struct json *json_nbr(double);

/* create a JSON string */
struct json *json_str(const char *);

/* create an empty JSON array */
struct json *json_arr(void);

/*
 * add a single child to a JSON array:
 * - if first argument 'array' is NULL, an array is created beforehand.
 * - the JSON array is returned.
 */
struct json *json_arr_add(struct json *array, struct json *child);

/*
 * add multiple childs to a JSON array:
 * - if first argument 'array' is NULL, an array is created beforehand.
 * - variadic arguments must be a succession of (struct json *),
 *   NULL terminated.
 * - the JSON array is returned.
 */
struct json *json_arr_addv(struct json *array, ...);

/* array size (number of elements) */
size_t json_arr_size(struct json *array);

/*
 * create an empty JSON object
 */
struct json *json_obj(void);

/*
 * add a single (key, value) to a JSON object:
 * - if first argument 'object' is NULL, an object is created beforehand.
 * - 'key' must be a valid utf8 string
 * - the JSON object is returned.
 */
struct json *json_obj_add(struct json *object, const char *key, struct json* value);

/*
 * add multiple (key, value) pairs to a JSON object:
 * - if first argument 'object' is NULL, an object is created beforehand.
 * - variadic arguments must be a succession of (key, value) pairs of type
 *   (const char *, struct json *), NULL terminated.
 * - each 'key' must be a valid utf8 string
 * - the JSON object is returned.
 */
struct json *json_obj_addv(struct json *object, ...);

/* object size (number of entries) */
size_t json_obj_size(struct json *object);

/* free json element */
void json_free(struct json *);


/*----------------------------------------------------------------------------
 * JSON getters
 */

/* JSON predicates */
bool json_is_null(struct json *json);
bool json_is_bool(struct json *json);
bool json_is_nbr(struct json *json);
bool json_is_str(struct json *json);
bool json_is_arr(struct json *json);
bool json_is_obj(struct json *json);

/*
 * extract value from JSON element of type boolean:
 * - 'json' argument must be a JSON boolean value
 *   (crash otherwise).
 */
bool json_get_value_bool(struct json *json);

/*
 * extract value from JSON element of type boolean:
 * - return 'dflt' value if 'json' argument is NULL
 *   or isn't a JSON boolean value.
 */
bool json_get_value_bool_dflt(struct json *json, bool dflt);

/*
 * extract value from JSON element of type number:
 * - 'json' argument must be a JSON number value
 *   (crash otherwise).
 */
double json_get_value_nbr(struct json *json);

/*
 * extract value from JSON element of type number:
 * - return 'dflt' value if 'json' argument is NULL
 *   or isn't a JSON number value.
 */
double json_get_value_nbr_dflt(struct json *json, double dflt);

/*
 * extract value from JSON element of type string:
 * - 'json' argument must be a JSON string value
 *   (crash otherwise).
 */
char *json_get_value_str(struct json *json);

/*
 * extract value from JSON element of type string:
 * - return 'dflt' value if 'json' argument is NULL
 *   or isn't a JSON string value.
 */
char *json_get_value_str_dflt(struct json *json, char *dflt);

/*
 * extract JSON element from a JSON array:
 * - 'json' argument must be a JSON array value
 *   (crash otherwise).
 * - 'index' is the 0 based index value of the element to retrieve.
 * - return NULL if index is out of bound.
 */
struct json *json_arr_get(struct json *json, size_t index);

/*
 * extract JSON element from a JSON object:
 * - 'json' argument must be a JSON object value
 *   (crash otherwise).
 * - 'key' of the JSON element to retrieve.
 * - return NULL if no entry found for 'key'.
 */
struct json *json_obj_get(struct json *json, const char *key);

/*
 * extract nested JSON element from a JSON object:
 * - 'json' argument must be a JSON object value
 *   (crash otheriwse)
 * - follows a NULL terminated list of keys,
 *   each key being either:
 *   - a JSON object key
 *     (example "name")
 *   - a JSON array index in format "[<number>]"
 *     (example "[1]")
 * - return NULL if no entry found in the nested
 *   structure
 */
struct json *json_getv(struct json *json, ...);

/*
 * return JSON parent element
 */
struct json *json_parent(struct json *json);

/*----------------------------------------------------------------------------
 * JSON iterator
 */

/*
 * JSON iterator to iterate over JSON elements:
 * - an iterator on a JSON element that isn't a JSON array
 *   or a JSON object will return the element itself.
 * - and iterator on a JSON array or JSON object will
 *   return successively each child.
 */
struct json_iter {
    size_t count;
    struct json *parent;
    struct json *current_child;
};

/* create a JSON iterator for a JSON element */
struct json_iter json_iter_init(struct json *);

/* return next JSON element, or NULL when finished */
struct json *json_iter_next(struct json_iter *);

/* return count of elements already returned */
size_t json_iter_count(struct json_iter *iter);


/*----------------------------------------------------------------------------
 * Rendering of JSON elements
 */

/* render JSON element to a new malloc() string */
char *json_render(struct json *);

/* pretty rendering of JSON element to a new malloc() string */
char *json_render_pretty(struct json *);

/* print JSON element to stdout */
void json_print(struct json *);

/* pretty printing of JSON element to stdout */
void json_print_pretty(struct json *);


/*----------------------------------------------------------------------------
 * JSON parsing
 */

struct json_parser;

/* create a new JSON parser */
struct json_parser* json_parser_new(void);

/*
 * parse a JSON string:
 * - NULL is returned in case of parsing error
 */
struct json *json_parser_parse_str(struct json_parser *parser, const char *str);

/*
 * parse a JSON file:
 * - NULL is returned in case of parsing error
 */
struct json *json_parser_parse_file(struct json_parser *parser, const char *filename);

/* fetch error string in case of parsing error */
char *json_parser_error_msg(struct json_parser *parser);

/* free JSON parser structure */
void json_parser_free(struct json_parser *parser);

/*
 * Convenient functions to quickly parse a JSON string or file,
 * without the need to instantiate and free a JSON parser:
 * - return NULL in case of parsing error.
 * - no possiblity to get an error string in case of parsing error.
 */
struct json *json_parse_str(const char *str);
struct json *json_parse_file(const char *filename);

#endif
