/* -*- c-file-style:"stroustrup"; indent-tabs-mode: nil -*- */
#if !defined INC_PUBNUB_JSON_PARSE
#define INC_PUBNUB_JSON_PARSE

#include <stdbool.h>
#include <stdlib.h>


/** @file pubnub_json_parse.h

    A bunch of functions for parsing JSON. These are designed for
    internal use by the Pubnub client, but, since they are rather
    generic, the user can use them, too, though their availability and
    interface are not guaranteed to survive Pubnub client version
    changes.
 */

/** A representation of a JSON element. */
struct pbjson_elem {
    /** The start of the element - pointer to the first
        character. */
    char const* start;
    /** The end of the element - pointer to the character
        _after_ the last character */
    char const* end;
};


/** Results of parsing a JSON (object) definition */
enum pbjson_object_name_parse_result {
    /** No starting curly brace `{` */
    jonmpNoStartCurly,
    /** Key is missing from a JSON object definition */
    jonmpKeyMissing,
    /** Key is not a string (this forbidden by JSON) */
    jonmpKeyNotString,
    /** String not terminated (no end `"`) */
    jonmpStringNotTerminated,
    /** Colon (`:`) is missing in JSON object definition */
    jonmpMissingColon,
    /** Ending curly brace `}` is missing */
    jonmpObjectIncomplete,
    /** A comma `,`, delimiting key-value pairs in a JSON
        object, is missing.*/
    jonmpMissingValueSeparator,
    /** The key was not found in the JSON object definition */
    jonmpKeyNotFound,
    /** The name of the key is empty or otherwise not valid */
    jonmpInvalidKeyName,
    /** Value in an object is incomplete. Something like:

        {"zec": [1,

        There's no ending of the array in the value for the key "zec".
    */
    jonmpValueIncomplete,
    /** Parsed OK, JSON (object) is valid */
    jonmpOK
};


/** Skips whitespace starting from @p start, until @p end.
    Interprets whitespace as JSON does - that should be
    compatible with a lot of other specifications.

    @return Pointer to the first character that is not whitespace.
    It is == @p end if the whole input is skipped.
 */
char const* pbjson_skip_whitespace(char const* start, char const* end);


/** Finds the end of the string starting from @p start, until @p end.
    Interprets string as JSON does (starting and ending with
    double-quotation and allowing escape characters with backslash) -
    that should be compatible with a lot of other specifications.

    Assumes that @p start points to the first character of the string
    (that is, one past the opening double-quote).

    @return Pointer to the double-quotation character that is the end
    of string from input.  It is == @p end if the end of the string
    was not found in input.
*/

char const* pbjson_find_end_string(char const* start, char const* end);


/** Finds the end of the "primitive" value starting from @p start,
    until @p end.  Interprets "primitive value" a little more broadly
    than JSON does (basically, we allow anything that is not a JSON
    string, array or object) - that should be compatible with a lot of
    other specifications.

    Assumes that @p start points to the first character of the
    primitive.

    @return Pointer to the character that is the end of the primitive.
    It is == @p end if the end of the primitive was not found in
    input.
 */
char const* pbjson_find_end_primitive(char const* start, char const* end);


/** Finds the end of the "complex" value starting from @p start, until
    @p end.  Interprets "complex value" as a JSON object or array -
    that should be compatible with a lot of other specifications.

    Assumes that @p start points to the first character of the
    complex object (opening curly brace or square bracket).

    @return Pointer to the character that is the end of the complex.
    It is == @p end if the end of the complex was not found in
    input.
 */
char const* pbjson_find_end_complex(char const* start, char const* end);


/** Finds the end of the JSON element starting from @p start, until
    @p end.  Interprets element as JSON does (primitive, object or array) -
    that should be compatible with a lot of other specifications.

    Assumes that @p start points to the first character of the
    element.

    @return Pointer to the character that is the end of the element.
    It is == @p end if the end of the element was not found in
    input.
 */
char const* pbjson_find_end_element(char const* start, char const* end);


/** Gets the value from a JSON object from @p p, with the key @p name
    and puts it to @p parsed o success, returning jonmpOK. On failure,
    returns the error code and the effects on @p parsed are not
    defined.
*/
enum pbjson_object_name_parse_result
pbjson_get_object_value(struct pbjson_elem const* p,
                        char const*               name,
                        struct pbjson_elem*       parsed);


/** Helper function, returns whether string @p s is equal to the
    contents of the JSON element @p e.
*/
bool pbjson_elem_equals_string(struct pbjson_elem const* e, char const* s);

/** Helper function, returns a string describing an enum for
    the JSON (object) parse result.
 */
char const*
pbjson_object_name_parse_result_2_string(enum pbjson_object_name_parse_result e);


/** Copies the element @p p to the given string @p s, allocated by the
    caller as an array of @p n elements, which thus includes the
    terminating NUL character.

    If @p p can't fit in the given string, at most @p n - 1 elements
    will be copied to it. This is not considered an error.

    @return number of characters put into @p s, including the terminating NUL
*/
size_t pbjson_element_strcpy(struct pbjson_elem const* p, char* s, size_t n);

/** Copies the element @p p to the given string @p s, allocated by the
    caller as an array of @p n elements, which thus includes the
    terminating NUL character.

    If @p p can't fit in the given string, at most @p n - 1 elements
    will be copied to it. This is not considered an error.

    @return bool value whether field value matched or not.
*/
bool pbjson_value_for_field_found(struct pbjson_elem const* p, char const* name, char const* value);

char* pbjson_get_status_400_message_value(struct pbjson_elem const* el);

#endif /* !defined INC_PUBNUB_JSON_PARSE */
