/* JSON Printer
 * ZZJSON - Copyright (C) 2008 by Ivo van Poorten
 * License: GNU Lesser General Public License version 2.1
 */

#include "zzjson.h"

#define PRINT(fmt...) if (config->print(config->ohandle, ##fmt) < 0) return -1;
//#define PUTC(c)       if (config->putchar(c, config->ohandle) < 0) return -1;
#define PUTC(c)       PRINT("%c",c)
#define INC 4

static int print_string(ZZJSON_CONFIG *config, char *s) {
    int c, bs;
    if (!s) return 0;
    while ((c = *s++)) {
        bs = 1;
        switch (c) {
//            case '/':                 // useless escape of forward slash
            case '\\':
                if (*s == 'u') bs = 0;  // copy \uHHHH verbatim
                break;
            case '"':               break;
            case '\b':  c = 'b';    break;
            case '\f':  c = 'f';    break;
            case '\n':  c = 'n';    break;
            case '\r':  c = 'r';    break;
            case '\t':  c = 't';    break;
            default:    bs = 0;     break;
        }
        if (bs) PUTC('\\');
        PUTC(c);
    }
    return 0;
}

static int zzjson_print2(ZZJSON_CONFIG *config, ZZJSON *zzjson,
                 unsigned int indent, unsigned int objval) {
    char c = 0, d = 0;
    if (!zzjson) return -1;

    switch(zzjson->type) {
        case ZZJSON_OBJECT: c = '{'; d = '}'; break;
        case ZZJSON_ARRAY:  c = '['; d = ']'; break;
        default: break;
    }

    if (c) PRINT("%s%*s%c", indent ? "\n" : "", indent, "", c);

    while (zzjson) {
        switch(zzjson->type) {
        case ZZJSON_OBJECT:
            if (zzjson->value.object.val) {
                PRINT("\n%*s\"", indent+INC, "");
                if (print_string(config, zzjson->value.object.label) < 0)
                    return -1;
                PRINT("\" :");
                if (zzjson_print2(config, zzjson->value.object.val,
                                                indent+INC, 1) < 0) return -1;
            }
            break;
        case ZZJSON_ARRAY:
            if (zzjson->value.array.val)
                if (zzjson_print2(config, zzjson->value.array.val,
                                                indent+INC, 0) < 0) return -1;
            break;
        case ZZJSON_STRING:
            PRINT(objval ? " \"" : "\n%*s\"", indent, "");
            if (print_string(config, zzjson->value.string.string)<0) return -1;
            PUTC('"');
            break;
        case ZZJSON_FALSE:
            PRINT(objval ? " false" : "\n%*sfalse", indent, "");
            break;
        case ZZJSON_NULL:
            PRINT(objval ? " null" : "\n%*snull", indent, "");
            break;
        case ZZJSON_TRUE:
            PRINT(objval ? " true" : "\n%*strue", indent, "");
            break;
        case ZZJSON_NUMBER_NEGINT:
        case ZZJSON_NUMBER_POSINT:
        case ZZJSON_NUMBER_DOUBLE:
            PRINT(objval ? " " : "\n%*s", indent, "");
            if (zzjson->type == ZZJSON_NUMBER_DOUBLE) {
                PRINT("%16.16e", zzjson->value.number.val.dval);
            } else {
                if (zzjson->type == ZZJSON_NUMBER_NEGINT) PUTC('-');
                PRINT("%llu", zzjson->value.number.val.ival);
            }
        default:
            break;
        }
        zzjson = zzjson->next;
        if (zzjson) PUTC(',');
    }

    if (d) PRINT("\n%*s%c", indent, "", d);

    return 0;
}

int zzjson_print(ZZJSON_CONFIG *config, ZZJSON *zzjson) {
    int retval = zzjson_print2(config, zzjson, 0, 0);
//    if (retval >= 0) retval = config->putchar('\n', config->ohandle);
#ifndef CONFIG_NO_ERROR_MESSAGES
    if (retval <  0) config->error(config->ehandle, "print: unable to print");
#endif
    return retval;
}