/* 
 * Copyright 2008-2019 Aerospike, Inc.
 *
 * Portions may be licensed to Aerospike, Inc. under one or more contributor
 * license agreements.
 *
 * 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 <aerospike/as_map.h>
#include <aerospike/as_iterator.h>
#include <aerospike/as_map_iterator.h>
#include <aerospike/as_pair.h>
#include <citrusleaf/alloc.h>
#include <string.h>

/******************************************************************************
 *	FUNCTIONS
 *****************************************************************************/

as_map *as_map_cons(as_map *map, bool free, uint32_t flags,
                    const as_map_hooks *hooks)
{
    if (!map)
        return map;

    as_val_cons((as_val *)map, AS_MAP, free);
    map->flags = flags;
    map->hooks = hooks;
    return map;
}

as_map *as_map_init(as_map *map, const as_map_hooks *hooks)
{
    return as_map_cons(map, false, 0, hooks);
}

as_map *as_map_new(const as_map_hooks *hooks)
{
    as_map *map = (as_map *)cf_malloc(sizeof(as_map));
    return as_map_cons(map, true, 0, hooks);
}

/******************************************************************************
 * as_val FUNCTIONS
 ******************************************************************************/

void as_map_val_destroy(as_val *v)
{
    as_map *m = as_map_fromval(v);
    as_util_hook(destroy, false, m);
}

uint32_t as_map_val_hashcode(const as_val *v)
{
    as_map *m = as_map_fromval(v);
    return as_util_hook(hashcode, 0, m);
}

typedef struct as_map_val_tostring_data_s {
    char *buf;
    uint32_t blk;
    uint32_t cap;
    uint32_t pos;
    bool sep;
} as_map_val_tostring_data;

static bool as_map_val_tostring_foreach(const as_val *key, const as_val *val,
                                        void *udata)
{
    as_map_val_tostring_data *data = (as_map_val_tostring_data *)udata;

    char *keystr = as_val_tostring(key);
    if (!keystr) {
        return false;
    }
    int keylen = (int)strlen(keystr);

    char *valstr = as_val_tostring(val);
    if (!valstr) {
        cf_free(keystr);
        return false;
    }
    int vallen = (int)strlen(valstr);

    uint32_t entlen = keylen + 2 + vallen + 2;

    if (data->pos + entlen >= data->cap) {
        uint32_t adj = entlen > data->blk ? entlen : data->blk;
        data->buf = cf_realloc(data->buf, sizeof(char) * (data->cap + adj));
        memset(data->buf + data->cap, 0, sizeof(char) * adj);
        data->cap += adj;
    }

    if (data->sep) {
        data->buf[data->pos] = ',';
        data->buf[data->pos + 1] = ' ';
        data->pos += 2;
    }

    strcpy(data->buf + data->pos, keystr);
    data->pos += keylen;

    strcpy(data->buf + data->pos, ":");
    data->pos += 1;

    strcpy(data->buf + data->pos, valstr);
    data->pos += vallen;

    data->sep = true;

    cf_free(keystr);
    keystr = NULL;

    cf_free(valstr);
    valstr = NULL;

    return true;
}

char *as_map_val_tostring(const as_val *v)
{
    as_map_val_tostring_data data = {
        .buf = NULL, .blk = 1024, .cap = 1024, .pos = 0, .sep = false};

    data.buf = (char *)cf_calloc(data.cap, sizeof(char));

    strcpy(data.buf, "{");
    data.pos += 1;

    if (v) {
        as_map_foreach((as_map *)v, as_map_val_tostring_foreach, &data);
    }

    if (data.pos + 2 >= data.cap) {
        data.buf = cf_realloc(data.buf, sizeof(char) * (data.cap + 2));
        data.cap += 2;
    }

    data.buf[data.pos] = '}';
    data.buf[data.pos + 1] = 0;

    return data.buf;
}
