/*
 * Licensed to the Apache Software Foundation (ASF) under one
 * or more contributor license agreements.  See the NOTICE file
 * distributed with this work for additional information
 * regarding copyright ownership.  The ASF licenses this file
 * to you under the Apache License, Version 2.0 (the
 * "License"); you may not use this file except in compliance
 * with the License.  You may obtain a copy of the License at
 *
 *  http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing,
 * software distributed under the License is distributed on an
 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
 * KIND, either express or implied.  See the License for the
 * specific language governing permissions and limitations
 * under the License.
 */

#ifndef _JSON_H_
#define _JSON_H_

#include <stdbool.h>
#include <stdint.h>
#include <ctype.h>
#include <stdio.h>
#include <sys/types.h>
#include <syscfg/syscfg.h>

#ifdef __cplusplus
extern "C" {
#endif

#define JSON_VALUE_TYPE_BOOL   (0)
#define JSON_VALUE_TYPE_UINT64 (1)
#define JSON_VALUE_TYPE_INT64  (2)
#define JSON_VALUE_TYPE_STRING (3)
#define JSON_VALUE_TYPE_ARRAY  (4)
#define JSON_VALUE_TYPE_OBJECT (5)

#if MYNEWT_VAL_JSON_FLOAT_SUPPORT
#define FLOAT_SUPPORT 1
#endif

#if MYNEWT_VAL_JSON_REAL_IS_FLOAT
typedef float json_real_t;
#define strtoreal strtof
#else
typedef double json_real_t;
#define strtoreal strtod
#endif

struct json_value {
    uint8_t jv_pad1;
    uint8_t jv_type;
    uint16_t jv_len;

    union {
        uint64_t u;
        float fl;
        const char *str;
        struct {
            char **keys;
            struct json_value **values;
        } composite;
    } jv_val;
};

#define JSON_VALUE_STRING(__jv, __str)        \
    (__jv)->jv_type = JSON_VALUE_TYPE_STRING; \
    (__jv)->jv_len = strlen(__str);           \
    (__jv)->jv_val.str = (__str);

#define JSON_VALUE_STRINGN(__jv, __str, __len) \
    (__jv)->jv_type = JSON_VALUE_TYPE_STRING;  \
    (__jv)->jv_len = (uint16_t) (__len);                    \
    (__jv)->jv_val.str = (__str);

#define JSON_VALUE_BOOL(__jv, __v)            \
    (__jv)->jv_type = JSON_VALUE_TYPE_BOOL;   \
    (__jv)->jv_val.u = (__v);

#define JSON_VALUE_INT(__jv, __v)             \
    (__jv)->jv_type = JSON_VALUE_TYPE_INT64;  \
    (__jv)->jv_val.u = (uint64_t) __v;

#define JSON_VALUE_UINT(__jv, __v)            \
    (__jv)->jv_type = JSON_VALUE_TYPE_UINT64; \
    (__jv)->jv_val.u = (uint64_t) __v;

/* Encoding functions */
typedef int (*json_write_func_t)(void *buf, char *data,
        int len);

struct json_encoder {
    json_write_func_t je_write;
    void *je_arg;
    unsigned int je_wr_commas : 1;
    char je_encode_buf[64];
};


#define JSON_NITEMS(x) (int)(sizeof(x)/sizeof(x[0]))

int json_encode_object_start(struct json_encoder *);
int json_encode_object_key(struct json_encoder *encoder, char *key);
int json_encode_object_entry(struct json_encoder *, char *,
        struct json_value *);
int json_encode_object_finish(struct json_encoder *);

int json_encode_array_name(struct json_encoder *encoder, char *name);
int json_encode_array_start(struct json_encoder *encoder);
int json_encode_array_value(struct json_encoder *encoder, struct json_value *val);
int json_encode_array_finish(struct json_encoder *encoder);

/* Json parser definitions */
typedef enum {
    t_integer,
    t_uinteger,
    t_real,
    t_string,
    t_boolean,
    t_character,
    t_object,
    t_structobject,
    t_array,
    t_check,
    t_ignore
} json_type;

struct json_enum_t {
    const char *name;
    long long int value;
};

struct json_array_t {
    json_type element_type;
    union {
        struct {
            const struct json_attr_t *subtype;
            char *base;
            size_t stride;
        } objects;
        struct {
            char **ptrs;
            char *store;
            int storelen;
        } strings;
        struct {
            long long int *store;
        } integers;
        struct {
            long long unsigned int *store;
        } uintegers;
        struct {
            json_real_t *store;
        } reals;
        struct {
            bool *store;
        } booleans;
    } arr;
    int *count;
    int maxlen;
};

struct json_attr_t {
    char *attribute;
    json_type type;
    union {
        long long int *integer;
        long long unsigned int *uinteger;
        json_real_t *real;
        char *string;
        bool *boolean;
        char *character;
        struct json_array_t array;
        size_t offset;
    } addr;
    union {
        long long int integer;
        long long unsigned int uinteger;
        json_real_t real;
        bool boolean;
        char character;
        char *check;
    } dflt;
    size_t len;
    const struct json_enum_t *map;
    bool nodefault;
};

struct json_buffer;

/* when you implement a json buffer, you must implement these functions */

/* returns the next character in the buffer or '\0'*/
typedef char (*json_buffer_read_next_byte_t)(struct json_buffer *);
/* returns the previous character in the buffer or '\0' */
typedef char (*json_buffer_read_prev_byte_t)(struct json_buffer *);
/* returns the number of characters read or zero */
typedef int (*json_buffer_readn_t)(struct json_buffer *, char *buf, int n);

struct json_buffer {
    json_buffer_readn_t jb_readn;
    json_buffer_read_next_byte_t jb_read_next;
    json_buffer_read_prev_byte_t jb_read_prev;
};

#define JSON_ATTR_MAX        31        /* max chars in JSON attribute name */
#define JSON_VAL_MAX        512        /* max chars in JSON value part */

int json_read_object(struct json_buffer *, const struct json_attr_t *);
int json_read_array(struct json_buffer *, const struct json_array_t *);

#define JSON_ERR_OBSTART     1   /* non-WS when expecting object start */
#define JSON_ERR_ATTRSTART   2   /* non-WS when expecting attrib start */
#define JSON_ERR_BADATTR     3   /* unknown attribute name */
#define JSON_ERR_ATTRLEN     4   /* attribute name too long */
#define JSON_ERR_NOARRAY     5   /* saw [ when not expecting array */
#define JSON_ERR_NOBRAK      6   /* array element specified, but no [ */
#define JSON_ERR_STRLONG     7   /* string value too long */
#define JSON_ERR_TOKLONG     8   /* token value too long */
#define JSON_ERR_BADTRAIL    9   /* garbage while expecting comma or } or ] */
#define JSON_ERR_ARRAYSTART  10  /* didn't find expected array start */
#define JSON_ERR_OBJARR      11  /* error while parsing object array */
#define JSON_ERR_SUBTOOLONG  12  /* too many array elements */
#define JSON_ERR_BADSUBTRAIL 13  /* garbage while expecting array comma */
#define JSON_ERR_SUBTYPE     14  /* unsupported array element type */
#define JSON_ERR_BADSTRING   15  /* error while string parsing */
#define JSON_ERR_CHECKFAIL   16  /* check attribute not matched */
#define JSON_ERR_NOPARSTR    17  /* can't support strings in parallel arrays */
#define JSON_ERR_BADENUM     18  /* invalid enumerated value */
#define JSON_ERR_QNONSTRING  19  /* saw quoted value when expecting nonstring */
#define JSON_ERR_NONQSTRING  19  /* didn't see quoted value when expecting string */
#define JSON_ERR_MISC        20  /* other data conversion error */
#define JSON_ERR_BADNUM      21  /* error while parsing a numerical argument */
#define JSON_ERR_NULLPTR     22  /* unexpected null value or attribute pointer */

/*
 * Use the following macros to declare template initializers for structobject
 * arrays.  Writing the equivalents out by hand is error-prone.
 *
 * JSON_STRUCT_OBJECT takes a structure name s, and a fieldname f in s.
 *
 * JSON_STRUCT_ARRAY takes the name of a structure array, a pointer to a an
 * initializer defining the subobject type, and the address of an integer to
 * store the length in.
 */
#define JSON_STRUCT_OBJECT(s, f)        .addr.offset = offsetof(s, f)
#define JSON_STRUCT_ARRAY(a, e, n) \
        .addr.array.element_type = t_structobject, \
        .addr.array.arr.objects.subtype = e, \
        .addr.array.arr.objects.base = (char*)a, \
        .addr.array.arr.objects.stride = sizeof(a[0]), \
        .addr.array.count = n, \
        .addr.array.maxlen = (int)(sizeof(a)/sizeof(a[0]))

#ifdef __cplusplus
}
#endif

#endif /* _JSON_H_ */
