/****************************************************************************
 * <Novell-copyright>
 * Copyright (c) 2006 Novell, Inc. All Rights Reserved.
 * 
 * This program is free software; you can redistribute it and/or
 * modify it under the terms of version 2 of the GNU General Public License
 * as published by the Free Software Foundation.
 * 
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 * 
 * You should have received a copy of the GNU General Public License
 * along with this program; if not, contact Novell, Inc.
 * 
 * To contact Novell about this file by physical or electronic mail, you 
 * may find current contact information at www.novell.com.
 * </Novell-copyright>
 ****************************************************************************/

#ifndef BONGOJSON_H
#define BONGOJSON_H

#include <xpl.h>
#include <bongoutil.h>

XPL_BEGIN_C_LINKAGE

typedef struct _BongoJsonNode       BongoJsonNode;
typedef struct _BongoJsonObject     BongoJsonObject;
typedef struct _BongoJsonObjectIter BongoJsonObjectIter;

typedef struct _BongoJsonParser     BongoJsonParser;

typedef enum {
    BONGO_JSON_NULL,
    BONGO_JSON_OBJECT,
    BONGO_JSON_ARRAY,
    BONGO_JSON_BOOL,
    BONGO_JSON_DOUBLE,
    BONGO_JSON_INT,
    BONGO_JSON_STRING
} BongoJsonType;

typedef enum {
    BONGO_JSON_OK = 0,
    BONGO_JSON_NOT_FOUND = -1,
    BONGO_JSON_BAD_TYPE = -2,
    BONGO_JSON_UNEXPECTED_CHAR = -3,
    BONGO_JSON_BOUNDS_ERROR = -4,
    BONGO_JSON_UNCLOSED_COMMENT = -5,
    BONGO_JSON_UNCLOSED_STRING = -6,
    BONGO_JSON_UNCLOSED_OBJECT = -6,
    BONGO_JSON_UNCLOSED_ARRAY = -7,
    BONGO_JSON_MISSING_VALUE = -8,
    BONGO_JSON_BAD_KEY = -9,
    BONGO_JSON_UNEXPECTED_EOF = -10,
    BONGO_JSON_UNKNOWN_ERROR = -11,
} BongoJsonResult;

struct _BongoJsonNode {
    BongoJsonType type;
    
    union {
        BongoJsonObject *objectVal;
        BongoArray *arrayVal;
        BOOL boolVal;
        double doubleVal;
        int intVal;
        char *stringVal;
    } value;
};

typedef int (*BongoJsonParserReadFunc)(void *parserData, char *buffer, int bufLen);
typedef void (*BongoJsonParserFreeFunc)(void *parserData);

/*** BongoJsonNode ***/

BongoJsonNode *BongoJsonNodeNewNull(void);
BongoJsonNode *BongoJsonNodeNewObject(BongoJsonObject *val);
BongoJsonNode *BongoJsonNodeNewArray(BongoArray *val);
BongoJsonNode *BongoJsonNodeNewBool(BOOL val);
BongoJsonNode *BongoJsonNodeNewDouble(double val);
BongoJsonNode *BongoJsonNodeNewInt(int val);
BongoJsonNode *BongoJsonNodeNewString(const char *val);
BongoJsonNode *BongoJsonNodeNewStringGive(char *val);

BongoJsonNode *BongoJsonNodeDup(BongoJsonNode *node);

#define BongoJsonNodeAsObject(n) ((n)->value.objectVal)
#define BongoJsonNodeAsArray(n) ((n)->value.arrayVal)
#define BongoJsonNodeAsBool(n) ((n)->value.boolVal)
#define BongoJsonNodeAsDouble(n) ((n)->value.doubleVal)
#define BongoJsonNodeAsInt(n) ((n)->value.intVal)
#define BongoJsonNodeAsString(n) ((n)->value.stringVal)

char *BongoJsonNodeToString(BongoJsonNode *node);
void BongoJsonNodeToStringBuilder(BongoJsonNode *node, BongoStringBuilder *sb);

void BongoJsonNodeFree(BongoJsonNode *node);
void BongoJsonNodeFreeSteal(BongoJsonNode *node);

/*** BongoJsonArray **/

#define BongoJsonArrayNew(count) BongoArrayNew(sizeof(BongoJsonNode*), count);

void BongoJsonArrayAppend(BongoArray *array, BongoJsonNode *node);

#define BongoJsonArrayAppendObject(a, v) BongoJsonArrayAppend((a), BongoJsonNodeNewObject(v))
#define BongoJsonArrayAppendArray(a, v) BongoJsonArrayAppend((a), BongoJsonNodeNewArray(v))
#define BongoJsonArrayAppendBool(a, v) BongoJsonArrayAppend((a), BongoJsonNodeNewBool(v))
#define BongoJsonArrayAppendDouble(a, v) BongoJsonArrayAppend((a), BongoJsonNodeNewDouble(v))
#define BongoJsonArrayAppendInt(a, v) BongoJsonArrayAppend((a), BongoJsonNodeNewInt(v))
#define BongoJsonArrayAppendString(a, v) BongoJsonArrayAppend((a), BongoJsonNodeNewString(v))
#define BongoJsonArrayAppendStringGive(a, v) BongoJsonArrayAppend((a), BongoJsonNodeNewStringGive(v))

void BongoJsonArrayToStringBuilder(BongoArray *array, BongoStringBuilder *sb);
char *BongoJsonArrayToString(BongoArray *array);

void BongoJsonArrayRemove(BongoArray *array, int i);
void BongoJsonArrayRemoveSteal(BongoArray *array, int i);

#define BongoJsonArrayGet(a, i) BongoArrayIndex((a), BongoJsonNode*, (i))

#define DEF_ARRAY_GET(ctype, etype, name) \
    static __inline BongoJsonResult BongoJsonArrayGet##name(BongoArray *array, int i, ctype *val) \
    {                                                                   \
        BongoJsonNode *node = BongoJsonArrayGet(array, i);                \
        if (node->type == etype) {                                      \
            *val = BongoJsonNodeAs##name(node);                          \
            return BONGO_JSON_OK;                                        \
        } else {                                                        \
            return BONGO_JSON_BAD_TYPE;                                  \
        }                                                               \
    }

DEF_ARRAY_GET(BongoJsonObject *, BONGO_JSON_OBJECT, Object);
DEF_ARRAY_GET(BongoArray *, BONGO_JSON_ARRAY, Array);
DEF_ARRAY_GET(BOOL, BONGO_JSON_BOOL, Bool);
DEF_ARRAY_GET(double, BONGO_JSON_DOUBLE, Double);
DEF_ARRAY_GET(int, BONGO_JSON_INT, Int);
DEF_ARRAY_GET(const char *, BONGO_JSON_STRING, String);

#undef DEF_ARRAY_GET

BongoArray *BongoJsonArrayDup(BongoArray *array);

void BongoJsonArrayFree(BongoArray *array);

/*** BongoJsonObject ***/

struct _BongoJsonObjectIter {
    /* public */
    const char *key;
    BongoJsonNode *value;
    
    /* private */
    BongoHashtableIter iter;
};

BongoJsonObject *BongoJsonObjectNew(void);
void BongoJsonObjectFree(BongoJsonObject *obj);
BongoJsonObject *BongoJsonObjectDup(BongoJsonObject *obj);

void BongoJsonObjectToStringBuilder(BongoJsonObject *obj, BongoStringBuilder *sb);
char *BongoJsonObjectToString(BongoJsonObject *obj);

BOOL BongoJsonObjectIterFirst(BongoJsonObject *obj, BongoJsonObjectIter *iter);
BOOL BongoJsonObjectIterNext(BongoJsonObject *obj, BongoJsonObjectIter *iter);

BongoJsonResult BongoJsonObjectGet(BongoJsonObject *obj, const char *key, BongoJsonNode **node);

BongoJsonResult BongoJsonObjectGetObject(BongoJsonObject *obj, const char *key, BongoJsonObject **val);
BongoJsonResult BongoJsonObjectGetArray(BongoJsonObject *obj, const char *key, BongoArray **val);
BongoJsonResult BongoJsonObjectGetBool(BongoJsonObject *obj, const char *key, BOOL *val);
BongoJsonResult BongoJsonObjectGetDouble(BongoJsonObject *obj, const char *key, double *val);
BongoJsonResult BongoJsonObjectGetInt(BongoJsonObject *obj, const char *key, int *val);
BongoJsonResult BongoJsonObjectGetString(BongoJsonObject *obj, const char *key, const char **val);

BongoJsonResult BongoJsonObjectPut(BongoJsonObject *obj, const char *key, BongoJsonNode *node);
BongoJsonResult BongoJsonObjectPutNull(BongoJsonObject *obj, const char *key);
BongoJsonResult BongoJsonObjectPutObject(BongoJsonObject *obj, const char *key, BongoJsonObject *val);
BongoJsonResult BongoJsonObjectPutArray(BongoJsonObject *obj, const char *key, BongoArray *val);
BongoJsonResult BongoJsonObjectPutBool(BongoJsonObject *obj, const char *key, BOOL val);
BongoJsonResult BongoJsonObjectPutDouble(BongoJsonObject *obj, const char *key, double val);
BongoJsonResult BongoJsonObjectPutInt(BongoJsonObject *obj, const char *key, int val);
BongoJsonResult BongoJsonObjectPutString(BongoJsonObject *obj, const char *key, const char *val);
BongoJsonResult BongoJsonObjectPutStringGive(BongoJsonObject *obj, const char *key, const char *val);

BongoJsonResult BongoJsonObjectRemove(BongoJsonObject *obj, const char *key);
BongoJsonResult BongoJsonObjectRemoveSteal(BongoJsonObject *obj, const char *key);

/*** Parsing ***/

char *BongoJsonQuoteString(const char *str);
void BongoJsonQuoteStringToStringBuilder(const char *str, BongoStringBuilder *sb);

BongoJsonResult BongoJsonParseString(const char *str, BongoJsonNode **node);
BongoJsonResult BongoJsonParseFile(FILE *f, int toRead, BongoJsonNode **node);

BongoJsonResult BongoJsonValidateString(const char *str);

BongoJsonParser *BongoJsonParserNew(void *userData, 
                                  BongoJsonParserReadFunc read,
                                  BongoJsonParserFreeFunc freeFunc,
                                  int toRead);
BongoJsonParser *BongoJsonParserNewString(const char *str);
BongoJsonParser *BongoJsonParserNewFile(FILE *f, int toRead);

void BongoJsonParserFree(BongoJsonParser *parser);

BongoJsonResult BongoJsonParserReadNode(BongoJsonParser *parser, BongoJsonNode **node);

/*** JsonJPath : querying a json structure, sort of ***/

BongoJsonNode *BongoJsonJPath(BongoJsonNode *root, const char *path);

BongoJsonResult BongoJsonJPathGetObject(BongoJsonNode *n, const char *path, BongoJsonObject **val);
BongoJsonResult BongoJsonJPathGetArray(BongoJsonNode *n, const char *path, BongoArray **val);
BongoJsonResult BongoJsonJPathGetBool(BongoJsonNode *n, const char *path, BOOL *val);
BongoJsonResult BongoJsonJPathGetInt(BongoJsonNode *n, const char *path, int *val);
BongoJsonResult BongoJsonJPathGetString(BongoJsonNode *n, const char *path, const char **val);
BongoJsonResult BongoJsonJPathGetDouble(BongoJsonNode *n, const char *path, double *val);
BongoJsonResult BongoJsonJPathGetFloat(BongoJsonNode *n, const char *path, float *val);


/*** path resolvers ***/

BongoJsonResult BongoJsonNodeResolve(BongoJsonNode *node, const char *path, BongoJsonNode **out);
BongoJsonResult BongoJsonObjectResolve(BongoJsonObject *obj, const char *path, BongoJsonNode **out);

#define DEF_TYPED_RESOLVER(ctype, etype, name)                           \
    static __inline BongoJsonResult BongoJsonNodeResolve##name(BongoJsonNode *node, const char *path, ctype *out) \
    {                                                                   \
        BongoJsonResult res = BongoJsonNodeResolve(node, path, &node);    \
        if (res != BONGO_JSON_OK) return res;                            \
        if (node->type != etype) return BONGO_JSON_BAD_TYPE;             \
        *out = BongoJsonNodeAs##name(node);                              \
        return BONGO_JSON_OK;                                            \
    } \
    static __inline BongoJsonResult BongoJsonObjectResolve##name(BongoJsonObject *obj, const char *path, ctype *out) \
    {   BongoJsonNode *node; \
        BongoJsonResult res = BongoJsonObjectResolve(obj, path, &node);    \
        if (res != BONGO_JSON_OK) return res;                            \
        if (node->type != etype) return BONGO_JSON_BAD_TYPE;             \
        *out = BongoJsonNodeAs##name(node);                              \
        return BONGO_JSON_OK;                                            \
    }   

DEF_TYPED_RESOLVER(BongoJsonObject *, BONGO_JSON_OBJECT, Object);
DEF_TYPED_RESOLVER(BongoArray *, BONGO_JSON_ARRAY, Array);
DEF_TYPED_RESOLVER(BOOL, BONGO_JSON_BOOL, Bool);
DEF_TYPED_RESOLVER(double, BONGO_JSON_DOUBLE, Double);
DEF_TYPED_RESOLVER(int, BONGO_JSON_INT, Int);
DEF_TYPED_RESOLVER(const char *, BONGO_JSON_STRING, String);

#undef DEF_TYPED_RESOLVER

XPL_END_C_LINKAGE

#endif
