/*
 *------------------------------------------------------------------
 * json_format.c
 *
 * Copyright (c) 2015 Cisco and/or its affiliates.
 * Licensed 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.
 *------------------------------------------------------------------

*/
#include <inttypes.h>
#include <vat/json_format.h>
#include <vnet/ip/ip.h>
#include <vppinfra/vec.h>

#define VAT_TAB_WIDTH               2

typedef struct vat_print_ctx_s {
    FILE *ofp;
    u32 indent;
} vat_print_ctx_t;

/* Format an IP4 address. */
static u8 * vat_json_format_ip4_address (u8 * s, va_list * args)
{
  u8 * a = va_arg (*args, u8 *);
  return format (s, "%d.%d.%d.%d", a[0], a[1], a[2], a[3]);
}

/* Format an IP6 address. */
static u8 * vat_json_format_ip6_address (u8 * s, va_list * args)
{
  ip6_address_t * a = va_arg (*args, ip6_address_t *);
  u32 i, i_max_n_zero, max_n_zeros, i_first_zero, n_zeros, last_double_colon;

  i_max_n_zero = ARRAY_LEN (a->as_u16);
  max_n_zeros = 0;
  i_first_zero = i_max_n_zero;
  n_zeros = 0;
  for (i = 0; i < ARRAY_LEN (a->as_u16); i++)
    {
      u32 is_zero = a->as_u16[i] == 0;
      if (is_zero && i_first_zero >= ARRAY_LEN (a->as_u16))
        {
          i_first_zero = i;
          n_zeros = 0;
        }
      n_zeros += is_zero;
      if ((! is_zero && n_zeros > max_n_zeros)
          || (i + 1 >= ARRAY_LEN (a->as_u16) && n_zeros > max_n_zeros))
        {
          i_max_n_zero = i_first_zero;
          max_n_zeros = n_zeros;
          i_first_zero = ARRAY_LEN (a->as_u16);
          n_zeros = 0;
        }
    }

  last_double_colon = 0;
  for (i = 0; i < ARRAY_LEN (a->as_u16); i++)
    {
      if (i == i_max_n_zero && max_n_zeros > 1)
        {
          s = format (s, "::");
          i += max_n_zeros - 1;
          last_double_colon = 1;
        }
      else
        {
          s = format (s, "%s%x",
                      (last_double_colon || i == 0) ? "" : ":",
                      clib_net_to_host_u16 (a->as_u16[i]));
          last_double_colon = 0;
        }
    }

  return s;
}

static void
vat_json_indent_print (vat_print_ctx_t *ctx)
{
    int i;
    for (i=0; i<ctx->indent * VAT_TAB_WIDTH; i++) {
        fformat(ctx->ofp, " ");
    }
}

static void
vat_json_indent_line (vat_print_ctx_t * ctx, char * fmt, ...)
{
    va_list va;

    vat_json_indent_print(ctx);
    va_start(va, fmt);
    va_fformat(ctx->ofp, fmt, &va);
    va_end(va);
}

static u8
is_num_only (vat_json_node_t * p)
{
    vat_json_node_t * elem;
    vec_foreach(elem, p) {
        if (VAT_JSON_INT != elem->type &&
                VAT_JSON_UINT != elem->type) {
            return 0;
        }
    }
    return 1;
}

static void
vat_json_print_internal (vat_print_ctx_t *ctx, vat_json_node_t *node)
{
#define P(fmt,...) fformat(ctx->ofp, fmt, ##__VA_ARGS__)
#define PL(fmt,...) fformat(ctx->ofp, fmt"\n", ##__VA_ARGS__)
#define PPL(fmt,...) vat_json_indent_line(ctx, fmt"\n", ##__VA_ARGS__)
#define PP(fmt,...) vat_json_indent_line(ctx, fmt, ##__VA_ARGS__)
#define INCR (ctx->indent++)
#define DECR (ctx->indent--)

    vat_json_pair_t *pair;
    u32 i, count;
    vat_json_node_t *elem;
    u8 num_only = 0;

    if (!node) {
        return;
    }

    switch (node->type) {
    case VAT_JSON_OBJECT:
        count = vec_len(node->pairs);
        if (count >= 1) {
            PL("{");
            INCR;
            for (i=0; i<count; i++) {
                pair = &node->pairs[i];
                PP("\"%s\": ", pair->name);
                vat_json_print_internal(ctx, &pair->value);
                if (i < count - 1) {
                    P(",");
                }
                PL();
            }
            DECR;
            PP("}");
        } else { P("{}"); }
        break;
    case VAT_JSON_ARRAY:
        num_only = is_num_only(node->array);
        count = vec_len(node->array);
        if (count >= 1) {
            if (num_only)
                P("[");
            else
                PL("[ ");
            INCR;
            for (i=0; i<count; i++) {
                elem = &node->array[i];
                if (!num_only) {
                    vat_json_indent_print(ctx);
                }
                vat_json_print_internal(ctx, elem);
                if (i < count - 1) {
                    if (num_only) {
                        P(", ");
                    } else {
                        P(",");
                    }
                }
                if (!num_only) PL();
            }
            DECR;
            if (!num_only)
                PP("]");
            else
                P("]");
        } else { P("[]"); }
        break;
    case VAT_JSON_INT:
        P("%d", node->sint);
        break;
    case VAT_JSON_UINT:
        P("%"PRIu64, node->uint);
        break;
    case VAT_JSON_REAL:
        P("%f", node->real);
        break;
    case VAT_JSON_STRING:
        P("\"%s\"", node->string);
        break;
    case VAT_JSON_IPV4:
        P("\"%U\"", vat_json_format_ip4_address, &node->ip4);
        break;
    case VAT_JSON_IPV6:
        P("\"%U\"", vat_json_format_ip6_address, &node->ip6);
        break;
    default:
        break;
    }
#undef PPL
#undef PP
#undef PL
#undef P
}

void vat_json_print(FILE *ofp, vat_json_node_t *node)
{
    vat_print_ctx_t ctx;
    memset(&ctx, 0, sizeof ctx);
    ctx.indent = 0;
    ctx.ofp = ofp;
    fformat(ofp, "\n");
    vat_json_print_internal(&ctx, node);
    fformat(ofp, "\n");
}

void vat_json_free (vat_json_node_t *node)
{
    int i = 0;

    if (NULL == node) {
        return;
    }
    switch (node->type) {
    case VAT_JSON_OBJECT:
        for (i = 0; i < vec_len(node->pairs); i++) {
            vat_json_free(&node->pairs[i].value);
        }
        if (NULL != node->pairs) {
            vec_free(node->pairs);
        }
        break;
    case VAT_JSON_ARRAY:
        for (i = 0; i < vec_len(node->array); i++) {
            vat_json_free(&node->array[i]);
        }
        if (NULL != node->array) {
            vec_free(node->array);
        }
        break;
    case VAT_JSON_STRING:
        if (NULL != node->string) {
            vec_free(node->string);
        }
        break;
    default:
        break;
    }
}