/*
 * Created by jiaqiang.ye@lnxall.com
 *
 * External Application Invoker for Lua
 *
 * 2021/12/22
 */

#include <errno.h>
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <sys/types.h>
#include <unistd.h>
#include <signal.h>

#include "lua.h"
#include "lualib.h"
#include "lauxlib.h"

#include "lnxall_ubuslog.h"

static int lnxall_log(lua_State * L)
{
	char * logbuf;
	size_t loglen, curlen;
	int ntop, idx, llevel;

	logbuf = NULL;
	loglen = curlen = 0;
	/* check lua stack slot space */
	if (lua_checkstack(L, 2) == 0)
		return 0;

	/* get the number of arguments */
	ntop = lua_gettop(L);
	if (ntop <= 1) {
		lua_pushboolean(L, 0);
		lua_pushfstring(L, "Error, invalid number of argument: %d", ntop);
		return 2;
	}

	/* get the log level */
	llevel = lua_isinteger(L, 1) ? lua_tointeger(L, 1) : -1;
	if (llevel < 0) {
		lua_pushboolean(L, 0);
		lua_pushfstring(L, "Error, invalid log level: %d", llevel);
		return 2;
	}

	for (idx = 2; idx <= ntop; ++idx) {
		const char * arg;
		size_t arglen, left;
		if (lua_isstring(L, idx) == 0)
			break;

		arglen = 0;
		arg = lua_tolstring(L, idx, &arglen);
		if (arg == NULL)
			break;

		if (arglen == 0)
			continue;

		left = loglen - curlen;
		if (left <= (arglen + 0x8)) {
			char * newbuf;
			size_t more = (arglen + 0x10) << 0x1;
			newbuf = (char *) realloc(logbuf, loglen + more);
			if (newbuf == NULL) {
				fputs("Error, system out of memory for realloc!\n", stderr);
				fflush(stderr);
				break;
			}

			loglen += more;
			logbuf = newbuf;
		}

		if (curlen > 0)
			logbuf[curlen++] = ' ';
		memcpy(logbuf + curlen, arg, arglen);
		curlen += arglen;
	}

	if (curlen == 0) {
		if (logbuf != NULL)
			free(logbuf);
		lua_pushboolean(L, 0);
		lua_pushstring(L, "Error, no argument given for lnxall_log");
		return 2;
	}

	/* add terminating NULL byte */
	if (logbuf && curlen > 0)
		logbuf[curlen] = '\0';

	idx = lnxall_syslog(llevel, "%s", logbuf);
	lua_pushinteger(L, idx);
	free(logbuf);
	return 1;
}

static int loglevel_get(lua_State * L)
{
	lua_pushinteger(L, lnxall_loglevel_get());
	return 1;
}

static int loglevel_set(lua_State * L)
{
	int ntop, llevel;

	llevel = LNXALL_LOGERR;
	ntop = lua_gettop(L);
	if (ntop >= 1 && lua_isinteger(L, 1))
		llevel = lua_tointeger(L, 1);

	lua_pushboolean(L, lnxall_loglevel_set(llevel, 0) == 0);
	return 1;
}

static int loglevel_reinit(lua_State * L)
{
	int ntop;
	int sigint_dft;
	const char * appName;

	sigint_dft = 0;
	appName = NULL;

	ntop = lua_gettop(L);
	if (ntop >= 1 && lua_isstring(L, 1))
		appName = lua_tolstring(L, 1, NULL);
	if (appName && appName[0] == '\0')
		appName = NULL;

	if (ntop >= 2) {
		if (lua_isboolean(L, 2))
			sigint_dft = lua_toboolean(L, 2);
		else if (lua_isnumber(L, 2))
			sigint_dft = (int) lua_tonumber(L, 2);
	}

	lua_pushinteger(L, lnxall_log_reinit(
		appName, sigint_dft));
	return 1;
}

static const struct luaL_Reg ubuslog_regs[] = {
	{ "log",                   lnxall_log },
	{ "loglevel_get",          loglevel_get },
	{ "loglevel_set",          loglevel_set },
	{ "log_reinit",            loglevel_reinit },
	{ NULL,                    NULL },
};

int luaopen_ubuslog_core(lua_State * L)
{
	luaL_register(L, "ubuslog", ubuslog_regs);

	lua_pushinteger(L, LNXALL_LOGEMERG);
	lua_setfield(L, -2, "LOG_EMERG");

	lua_pushinteger(L, LNXALL_LOGALERT);
	lua_setfield(L, -2, "LOG_ALERT");

	lua_pushinteger(L, LNXALL_LOGCRIT);
	lua_setfield(L, -2, "LOG_CRIT");

	lua_pushinteger(L, LNXALL_LOGERR);
	lua_setfield(L, -2, "LOG_ERR");

	lua_pushinteger(L, LNXALL_LOGWARNING);
	lua_setfield(L, -2, "LOG_WARNING");

	lua_pushinteger(L, LNXALL_LOGNOTICE);
	lua_setfield(L, -2, "LOG_NOTICE");

	lua_pushinteger(L, LNXALL_LOGINFO);
	lua_setfield(L, -2, "LOG_INFO");

	lua_pushinteger(L, LNXALL_LOGDEBUG);
	lua_setfield(L, -2, "LOG_DEBUG");
	return 1;
}
