#include "lua_common.h"

#include <stdlib.h>
#include <string.h>

int libretrodb_lua_to_rmsgpack_value(
        lua_State * L,
        int index,
        struct rmsgpack_dom_value * out
) {

	int rv = -1;
	int i;
	const char * tmp_string = NULL;
	char * tmp_buff = NULL;
	struct rmsgpack_dom_value * tmp_value;
	const int key_idx = -2;
	const int value_idx = -1;
	const int MAX_FIELDS = 100;
	size_t tmp_len;
	lua_Number tmp_num;

	out->type = RDT_MAP;
	out->map.len = 0;
	out->map.items = calloc(MAX_FIELDS, sizeof(struct rmsgpack_dom_pair));
	lua_pushnil(L);
	while (lua_next(L, index - 1) != 0) {
		if (out->map.len > MAX_FIELDS) {
			printf("skipping due to too many keys\n");
		} else if (!lua_isstring(L, key_idx)) {
			printf("skipping non string key\n");
		} else if (lua_isnil(L, value_idx)) {
			// Skipping nil value fields to save disk space
		} else {
			i = out->map.len;
			tmp_buff = strdup(lua_tostring(L, key_idx));
			out->map.items[i].key.type = RDT_STRING;
			out->map.items[i].key.string.len = strlen(tmp_buff);
			out->map.items[i].key.string.buff = tmp_buff;

			tmp_value = &out->map.items[i].value;
			switch (lua_type(L, value_idx)) {
			case LUA_TNUMBER:
				tmp_num = lua_tonumber(L, value_idx);
				tmp_value->type = RDT_INT;
				tmp_value->int_ = tmp_num;
				break;
			case LUA_TBOOLEAN:
				tmp_value->type = RDT_BOOL;
				tmp_value->bool_ = lua_toboolean(L, value_idx);
				break;
			case LUA_TSTRING:
				tmp_buff = strdup(lua_tostring(L, value_idx));
				tmp_value->type = RDT_STRING;
				tmp_value->string.len = strlen(tmp_buff);
				tmp_value->string.buff = tmp_buff;
				break;
			case LUA_TTABLE:
				lua_getfield(L, value_idx, "binary");
				if (!lua_isstring(L, -1)) {
					lua_pop(L, 1);
					lua_getfield(L, value_idx, "uint");
					if (!lua_isnumber(L, -1)) {
						lua_pop(L, 1);
						goto set_nil;
					} else {
						tmp_num = lua_tonumber(L, -1);
						tmp_value->type = RDT_UINT;
						tmp_value->uint_ = tmp_num;
						lua_pop(L, 1);
					}
				} else {
					tmp_string = lua_tolstring(L, -1, &tmp_len);
					tmp_buff = malloc(tmp_len);
					memcpy(tmp_buff, tmp_string, tmp_len);
					tmp_value->type = RDT_BINARY;
					tmp_value->binary.len = tmp_len;
					tmp_value->binary.buff = tmp_buff;
					lua_pop(L, 1);
				}
				break;
			default:
set_nil:
				tmp_value->type = RDT_NULL;
			}
			out->map.len++;
		}
		lua_pop(L, 1);
	}
	rv = 0;
	return rv;
}

