/* Copyright (c) 2018 Dovecot authors, see the included COPYING file */

#include "lib.h"
#include "str.h"
#include "istream.h"
#include "array.h"
#include "dlua-script.h"
#include "dlua-script-private.h"
#include "mail-storage.h"
#include "mailbox-attribute.h"
#include "mail-storage-lua.h"
#include "mail-storage-lua-private.h"
#include "mail-user.h"

#define LUA_STORAGE_MAIL "struct mail"

void dlua_push_mail(lua_State *L, struct mail *mail)
{
	luaL_checkstack(L, 20, "out of memory");
	/* create a table for holding few things */
	lua_createtable(L, 0, 20);
	luaL_setmetatable(L, LUA_STORAGE_MAIL);

	lua_pushlightuserdata(L, mail);
	lua_setfield(L, -2, "item");

#undef LUA_TABLE_SET_NUMBER
#define LUA_TABLE_SET_NUMBER(field) \
	lua_pushnumber(L, mail->field); \
	lua_setfield(L, -2, #field);
#undef LUA_TABLE_SET_BOOL
#define LUA_TABLE_SET_BOOL(field) \
	lua_pushboolean(L, mail->field); \
	lua_setfield(L, -2, #field);

	LUA_TABLE_SET_NUMBER(seq);
	LUA_TABLE_SET_NUMBER(uid);
	LUA_TABLE_SET_BOOL(expunged);

	dlua_push_mailbox(L, mail->box);
	lua_setfield(L, -2, "mailbox");

}

static struct mail *
lua_check_storage_mail(lua_State *L, int arg)
{
	if (!lua_istable(L, arg)) {
		(void)luaL_error(L, "Bad argument #%d, expected %s got %s",
				 arg, LUA_STORAGE_MAIL,
				 lua_typename(L, lua_type(L, arg)));
	}
	lua_pushliteral(L, "item");
	lua_rawget(L, arg);
	void *bp = (void*)lua_touserdata(L, -1);
	lua_pop(L, 1);
	return (struct mail*)bp;
}

static int lua_storage_mail_tostring(lua_State *L)
{
	DLUA_REQUIRE_ARGS(L, 1);
	struct mail *mail = lua_check_storage_mail(L, 1);

	const char *str =
		t_strdup_printf("<%s:UID %u>", mailbox_get_vname(mail->box),
				mail->uid);
	lua_pushstring(L, str);
	return 1;
}

static int lua_storage_mail_eq(lua_State *L)
{
	DLUA_REQUIRE_ARGS(L, 2);
	struct mail *mail = lua_check_storage_mail(L, 1);
	struct mail *mail2 = lua_check_storage_mail(L, 2);

	if (!DLUA_MAILBOX_EQUALS(mail->box, mail2->box))
		lua_pushboolean(L, FALSE);
	else
		lua_pushboolean(L, mail->uid != mail2->uid);
	return 1;
}

static int lua_storage_mail_lt(lua_State *L)
{
	DLUA_REQUIRE_ARGS(L, 2);
	struct mail *mail = lua_check_storage_mail(L, 1);
	struct mail *mail2 = lua_check_storage_mail(L, 2);

	if (!DLUA_MAILBOX_EQUALS(mail->box, mail2->box))
		return luaL_error(L,
				  "For lt, Mail can only be compared within same mailbox");
	else
		lua_pushboolean(L, mail->uid < mail2->uid);
	return 1;
}

static int lua_storage_mail_le(lua_State *L)
{
	DLUA_REQUIRE_ARGS(L, 2);
	struct mail *mail = lua_check_storage_mail(L, 1);
	struct mail *mail2 = lua_check_storage_mail(L, 2);

	if (!DLUA_MAILBOX_EQUALS(mail->box, mail2->box))
		return luaL_error(L,
				 "For le, mails can only be within same mailbox");
	else
		lua_pushboolean(L, mail->uid <= mail2->uid);

	return 1;
}

static int lua_storage_mail_gc(lua_State *L)
{
	(void)lua_check_storage_mail(L, 1);

	/* reset value to NULL */
	lua_pushliteral(L, "item");
	lua_pushnil(L);
	lua_rawset(L, 1);

	return 0;
}

static luaL_Reg lua_storage_mail_methods[] = {
	{ "__tostring", lua_storage_mail_tostring },
	{ "__eq", lua_storage_mail_eq },
	{ "__lt", lua_storage_mail_lt },
	{ "__le", lua_storage_mail_le },
	{ "__gc", lua_storage_mail_gc },
	{ NULL, NULL }
};

void lua_storage_mail_register(struct dlua_script *script)
{
	luaL_newmetatable(script->L, LUA_STORAGE_MAIL);
	lua_pushvalue(script->L, -1);
	lua_setfield(script->L, -2, "__index");
	luaL_setfuncs(script->L, lua_storage_mail_methods, 0);
	lua_pop(script->L, 1);
}
