/*	-*- mode: c; mode: fold -*-	*/
# include	"config.h"
# ifdef		SCRIPT_LUA
# include	<stdio.h>
# include	<stdlib.h>
# include	<unistd.h>
# include	<string.h>
# include	<lua.h>
# include	<lualib.h>
# include	"pager.h"
# include	"script.h"

# define	STARTUP		"Startup.lua"

/*{{{	statics & callable functions */
static Bool	isinit = False;
static double	lua_no_err = (double) NO_ERR,
		lua_err_fail = (double) ERR_FAIL,
		lua_err_fatal = (double) ERR_FATAL,
		lua_err_abort = (double) ERR_ABORT;
static script	*ls = NULL;
static char	*lline = NULL;
static int	lsiz = 0;
static char	*lcb = NULL;

static void
lua_logger (void)
{
	lua_Object	obj;
	char		*sav, *str;
	char		typ;
	
	if (((obj = lua_getparam (1)) != LUA_NOOBJECT) &&
	    (str = lua_getstring (obj)) &&
	    (sav = strdup (str))) {
		typ = *sav;
		if (((obj = lua_getparam (2)) == LUA_NOOBJECT) ||
		    (! (str = lua_getstring (obj)))) {
			str = sav;
			typ = LG_INF;
		}
		if (ls && ls -> logger)
			(*ls -> logger) (typ, "%s\n", str);
		free (sav);
	}
}

static void
lua_callback (void *sp, string_t *s, char_t sep, void *data)
{
	int		len;
	char		*str;
	lua_Object	obj;

	if (str = sextract (s)) {
		len = strlen (str);
		if (len + 2 >= lsiz) {
			lsiz = len + 64;
			if (! (lline = Realloc (lline, lsiz + 4)))
				lsiz = 0;
		}
		if (lline) {
			sprintf (lline, "%s%c", str, (char) sep);
			if (lcb && (obj = lua_getglobal (lcb)) && lua_isfunction (obj)) {
				lua_pushstring (lline);
				lua_callfunction (obj);
			}
		}
		free (str);
	}
}

static void
lua_setcb (void)
{
	lua_Object	obj1, obj2;
	char		*sep, *func;
	
	if ((obj1 = lua_getparam (1)) != LUA_NOOBJECT)
		obj2 = lua_getparam (2);
	else
		obj2 = LUA_NOOBJECT;
	if (lcb) {
		free (lcb);
		lcb = NULL;
	}
	if ((obj1 == LUA_NOOBJECT) || (! lua_isstring (obj1)) || (! (sep = lua_getstring (obj1)))) {
		if (ls && ls -> sp)
			tty_set_line_callback (ls -> sp, NULL, NULL, NULL);
	} else {
		if (ls && ls -> sp)
			tty_set_line_callback (ls -> sp, lua_callback, sep, NULL);
		if ((obj2 != LUA_NOOBJECT) && lua_isstring (obj2) && (func = lua_getstring (obj2)))
			lcb = strdup (func);
	}
}

static void
lua_get_line (void)
{
	if (lline)
		lua_pushstring (lline);
	else
		lua_pushnil ();
}

static void
lua_hangup (void)
{
	lua_Object	obj;
	double		sec;
	int		msec;
	
	if (((obj = lua_getparam (1)) != LUA_NOOBJECT) &&
	    lua_isnumber (obj)) {
		sec = lua_getnumber (obj);
		msec = (int) (sec * 1000.0);
	} else
		msec = 500;
	if (ls && ls -> sp)
		tty_hangup (ls -> sp, msec);
}

static void
do_send (Bool dcv)
{
	int		ret;
	lua_Object	obj;
	char		*str;
	
	ret = 0;
	if (((obj = lua_getparam (1)) != LUA_NOOBJECT) &&
	    lua_isstring (obj) && (str = lua_getstring (obj)))
		if (ls && ls -> sp) {
			if (dcv)
				str = scr_convert (ls, str);
			if (str) {
				if (tty_send_string (ls -> sp, str) != -1)
					ret = 1;
				if (dcv)
					free (str);
			}
		}
	if (ret)
		lua_pushnumber (1.0);
	else
		lua_pushnil ();
}

static void
lua_send (void)
{
	do_send (False);
}

static void
lua_csend (void)
{
	do_send (True);
}

static void
lua_expect (void)
{
	int		ret;
	lua_Object	obj;
	int		tout;
	char		*str;
	int		cnt, siz;
	char		**ex;
	int		*len;
	int		start;
	int		n;
	
	if (((obj = lua_getparam (1)) != LUA_NOOBJECT) && lua_isnumber (obj)) {
		tout = (int) lua_getnumber (obj);
		start = 2;
	} else {
		tout = 5;
		start = 1;
	}
	ex = NULL;
	cnt = 0;
	siz = 0;
	while (((obj = lua_getparam (start)) != LUA_NOOBJECT) &&
	       lua_isstring (obj) && (str = lua_getstring (obj))) {
		if (cnt >= siz) {
			siz += 4;
			if (! (ex = (char **) Realloc (ex, (siz + 2) * sizeof (char *))))
				break;
		}
		if (ex[cnt] = strdup (str))
			++cnt;
	}
	ret = -1;
	if (ex) {
		ex[cnt] = NULL;
		if ((cnt > 0) && (len = (int *) malloc ((cnt + 1) * sizeof (int)))) {
			for (n = 0; n < cnt; ++n)
				len[n] = strlen (ex[n]);
			len[cnt] = 0;
			if (ls && ls -> sp)
				ret = tty_expect_list (ls -> sp, tout, ex, len);
			free (len);
			for (n = 0; n < cnt; ++n)
				free (ex[n]);
		}
		free (ex);
	}
	lua_pushnumber ((double) ret);
}

static void
lua_send_expect (void)
{
	int		ret;
	lua_Object	obj;
	int		tout;
	char		*str;

	ret = 0;
	if ((obj = lua_getparam (1)) != LUA_NOOBJECT) {
		if (lua_isnumber (obj)) {
			tout = (int) lua_getnumber (obj);
			obj = lua_getparam (2);
		}
		if ((obj != LUA_NOOBJECT) && lua_isstring (obj) && (str = lua_getstring (obj)))
			if (ls && ls -> sp && (tty_send_expect (ls -> sp, tout, str, NULL) != -1))
				ret = 1;
	}
	if (ret)
		lua_pushnumber (1.0);
	else
		lua_pushnil ();
}

static void
lua_drain (void)
{
	lua_Object	obj;
	int		sec;
	
	if (((obj = lua_getparam (1)) != LUA_NOOBJECT) && lua_isnumber (obj))
		sec = (int) lua_getnumber (obj);
	else
		sec = 1;
	if (ls && ls -> sp)
		tty_drain (ls -> sp, sec);
}

static void
lua_cvdef (void)
{
	lua_Object	obj1, obj2;
	char		*src, *dst;
	int		n;
	
	if (ls) {
		if (! ls -> ctab)
			ls -> ctab = cv_new ();
		if (ls -> ctab)
			if (((obj1 = lua_getparam (1)) != LUA_NOOBJECT) && lua_isstring (obj1) && (src = lua_getstring (obj1))) {
				if ((obj2 = lua_getparam (2)) == LUA_NOOBJECT)
					cv_undefine (ls -> ctab, (char_t) *src);
				else if (lua_isstring (obj2) && (dst = lua_getstring (obj2)))
					cv_define (ls -> ctab, (char_t) *src, (char_t) *dst);
				else if (lua_isnumber (obj2)) {
					n = (int) lua_getnumber (obj2);
					if (n < 0)
						cv_invalid (ls -> ctab, (char_t) *src);
					else
						cv_define (ls -> ctab, (char_t) *src, (char_t) n);
				}
			}
	}
}

static void
lua_conv (void)
{
	char		*ret;
	lua_Object	obj;
	char		*str;
	
	ret = NULL;
	if (((obj = lua_getparam (1)) != LUA_NOOBJECT) && lua_isstring (obj) && (str = lua_getstring (obj)))
		if (ls)
			ret = scr_convert (ls, str);
	if (ret) {
		lua_pushstring (ret);
		free (ret);
	} else
		lua_pushnil ();
}
/*}}}*/
/*{{{	init/deinit */
static int
lua_init (script *s, char *libdir)
{
	char	*fname;

	if (! isinit) {
		iolib_open ();
		strlib_open ();
		mathlib_open ();
		lua_pushnumber (lua_no_err);	lua_storeglobal ("NO_ERR");
		lua_pushnumber (lua_err_fail);	lua_storeglobal ("ERR_FAIL");
		lua_pushnumber (lua_err_fatal);	lua_storeglobal ("ERR_FATAL");
		lua_pushnumber (lua_err_abort);	lua_storeglobal ("ERR_ABORT");
		/* void		logger (string str);				*/
		lua_register ("logger", lua_logger);
		/* void		setcb ([string sep[, string|function func]]);	*/
		lua_register ("setcb", lua_setcb);
		/* string|nil	get_line (void);				*/
		lua_register ("get_line", lua_get_line);
		/* void		hangup ([num sec]);				*/
		lua_register ("hangup", lua_hangup);
		/* num|nil	send (string line);				*/
		lua_register ("send", lua_send);
		/* num|nil	csend (string line);				*/
		lua_register ("csend", lua_csend);
		/* num		expect (num tout, string, s1, ..., string sn);	*/
		lua_register ("expect", lua_expect);
		/* num|nil	send_expect (num tout, string str);		*/
		lua_register ("send_expect", lua_send_expect);
		/* void		drain ([num sec]);				*/
		lua_register ("drain", lua_drain);
		/* void		cvdef (string src[, string|num dst]);		*/
		lua_register ("cvdef", lua_cvdef);
		/* string|nil	conv (string str);				*/
		lua_register ("conv", lua_conv);
		if (libdir && (fname = malloc (strlen (libdir) + sizeof (STARTUP) + 4))) {
			sprintf (fname, "%s/%s", libdir, STARTUP);
			if (access (fname, R_OK) != -1)
				lua_dofile (fname);
			free (fname);
		}
		lline = NULL;
		lsiz = 0;
		isinit = True;
	}
	return NO_ERR;
}

static void
lua_deinit (script *s)
{
	if (lline) {
		free (lline);
		lline = NULL;
		lsiz = 0;
	}
}
/*}}}*/
/*{{{	execution */
static int
lua_execute (script *s, char *func, char *parm)
{
	int		err;
	lua_Object	obj;
	double		ret;
	
	err = NO_ERR;
	if ((obj = lua_getglobal (func)) && lua_isfunction (obj)) {
		ls = s;
		lua_beginblock ();
		lua_pushnumber ((double) s -> delay.day);	lua_storeglobal ("delay_day");
		lua_pushnumber ((double) s -> delay.mon);	lua_storeglobal ("delay_mon");
		lua_pushnumber ((double) s -> delay.year);	lua_storeglobal ("delay_year");
		lua_pushnumber ((double) s -> delay.hour);	lua_storeglobal ("delay_hour");
		lua_pushnumber ((double) s -> delay.min);	lua_storeglobal ("delay_min");
		lua_pushnumber ((double) s -> delay.sec);	lua_storeglobal ("delay_sec");
		lua_pushnumber ((double) s -> expire.day);	lua_storeglobal ("expire_day");
		lua_pushnumber ((double) s -> expire.mon);	lua_storeglobal ("expire_mon");
		lua_pushnumber ((double) s -> expire.year);	lua_storeglobal ("expire_year");
		lua_pushnumber ((double) s -> expire.hour);	lua_storeglobal ("expire_hour");
		lua_pushnumber ((double) s -> expire.min);	lua_storeglobal ("expire_min");
		lua_pushnumber ((double) s -> expire.sec);	lua_storeglobal ("expire_sec");
		lua_pushnumber ((double) s -> rds);		lua_storeglobal ("rds");
		if (parm)
			lua_pushstring (parm);
		else
			lua_pushnil ();
		if (lua_callfunction (obj))
			err = ERR_FATAL;
		else if ((obj = lua_getresult (1)) && (obj != LUA_NOOBJECT)) {
			ret = lua_getnumber (obj);
			if (ret == lua_no_err)
				err = NO_ERR;
			else if (ret == lua_err_fail)
				err = ERR_FAIL;
			else if (ret == lua_err_fatal)
				err = ERR_FATAL;
			else if (ret == lua_err_abort)
				err = ERR_ABORT;
		}
		lua_endblock ();
		if (ls -> sp)
			tty_set_line_callback (ls -> sp, NULL, NULL, NULL);
		ls = NULL;
	}
	return err;
}
/*}}}*/
/*{{{	loading */
static int
lua_load_string (script *s, char *scr)
{
	return lua_dostring (scr) ? ERR_FATAL : NO_ERR;
}

static int
lua_load_file (script *s, char *fname)
{
	return lua_dofile (fname) ? ERR_FATAL : NO_ERR;
}
/*}}}*/
/*{{{	preinit/postdeinit/scriptentry */
static int
lua_preinit (char *libdir)
{
	return lua_init (NULL, libdir);
}

static void
lua_postdeinit (void)
{
	lua_deinit (NULL);
}

funcs	flua = {
	"Lua",
	lua_init,
	lua_deinit,
	lua_execute,
	lua_load_string,
	lua_load_file,
	lua_preinit,
	lua_postdeinit
};
/*}}}*/
# endif		/* SCRIPT_LUA */

