/**
 * @file odict/entry.c  Ordered Dictionary -- entry
 *
 * Copyright (C) 2010 - 2015 Creytiv.com
 */

#include "re_types.h"
#include "re_fmt.h"
#include "re_mem.h"
#include "re_list.h"
#include "re_hash.h"
#include "re_odict.h"


static void destructor(void *arg)
{
	struct odict_entry *e = arg;

	switch (e->type) {

	case ODICT_OBJECT:
	case ODICT_ARRAY:
		mem_deref(e->u.odict);
		break;

	case ODICT_STRING:
		mem_deref(e->u.str);
		break;

	default:
		break;
	}

	hash_unlink(&e->he);
	list_unlink(&e->le);
	mem_deref(e->key);
}


int odict_entry_add(struct odict *o, const char *key,
		    int type, ...)
{
	struct odict_entry *e;
	va_list ap;
	int err;

	if (!o || !key)
		return EINVAL;

	e = mem_zalloc(sizeof(*e), destructor);
	if (!e)
		return ENOMEM;

	e->type = type;

	err = str_dup(&e->key, key);
	if (err)
		goto out;

	va_start(ap, type);

	switch (e->type) {

	case ODICT_OBJECT:
	case ODICT_ARRAY:
		e->u.odict = mem_ref(va_arg(ap, struct odict *));
		break;

	case ODICT_STRING:
		err = str_dup(&e->u.str, va_arg(ap, const char *));
		break;

	case ODICT_INT:
		e->u.integer = va_arg(ap, int64_t);
		break;

	case ODICT_DOUBLE:
		e->u.dbl = va_arg(ap, double);
		break;

	case ODICT_BOOL:
		e->u.boolean = va_arg(ap, int);
		break;

	case ODICT_NULL:
		break;

	default:
		err = EINVAL;
		break;
	}

	va_end(ap);

	if (err)
		goto out;

	list_append(&o->lst, &e->le, e);
	hash_append(o->ht, hash_fast_str(e->key), &e->he, e);

 out:
	if (err)
		mem_deref(e);

	return err;
}


void odict_entry_del(struct odict *o, const char *key)
{
	mem_deref((struct odict_entry *)odict_lookup(o, key));
}


int odict_entry_debug(struct re_printf *pf, const struct odict_entry *e)
{
	int err;

	if (!e)
		return 0;

	err = re_hprintf(pf, "%s", e->key);

	switch (e->type) {

	case ODICT_OBJECT:
	case ODICT_ARRAY:
		err |= re_hprintf(pf, ":%H", odict_debug, e->u.odict);
		break;

	case ODICT_STRING:
		err |= re_hprintf(pf, ":%s", e->u.str);
		break;

	case ODICT_INT:
		err |= re_hprintf(pf, ":%lli", e->u.integer);
		break;

	case ODICT_DOUBLE:
		err |= re_hprintf(pf, ":%f", e->u.dbl);
		break;

	case ODICT_BOOL:
		err |= re_hprintf(pf, ":%s", e->u.boolean ? "true" : "false");
		break;

	case ODICT_NULL:
		break;
	}

	return err;
}
