/*

    Minimum Profit 5.x
    A Programmer's Text Editor

    Text User Interface.

    Copyright (C) 1991-2007 Angel Ortega <angel@triptico.com>

    This program is free software; you can redistribute it and/or
    modify it under the terms of the GNU General Public License
    as published by the Free Software Foundation; either version 2
    of the License, or (at your option) any later version.

    This program is distributed in the hope that it will be useful,
    but WITHOUT ANY WARRANTY; without even the implied warranty of
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    GNU General Public License for more details.

    You should have received a copy of the GNU General Public License
    along with this program; if not, write to the Free Software
    Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.

    http://www.triptico.com

*/

/* main TUI namspace */
mp.tui = {};

/* colors */

mp.colors.menu = { 'text' => [ 'white', 'blue' ], 'flags' => [ 'bright' ] };

/* code */

sub mp.tui.prompt(prompt)
/* draw a prompt on screen */
{
	/* delete all possible newlines */
	prompt = sregex("/\n/g", prompt, ' ');

	mp.tui.attr(mp.colors.menu.attr);
	mp.tui.move(0, mp.window.ty - 1, 1);
	mp.tui.addstr(prompt);
	mp.tui.attr(mp.colors.normal.attr);
	mp.tui.refresh();
}


sub mp.tui.readline(prompt, history, default, flags)
/* the readline function, with special functionality in 'flags' */
{
	local c, r, h, i, x;

	mp.tui.prompt(prompt ~ ' ');
	c = mp.tui.getxy();
	r = default || '';

	/* get the history stack */
	h = mp.get_history(history);

	i = 0;
	x = size(r);

	/* store in c[2] the usable size */
	push(c, mp.window.tx - c[0] - 1);

	while (1) {
		local s = r;
		local v = 0;

		/* is the string bigger than the usable size? */
		if (size(r) >= c[2]) {
			/* if x is beyond the usable size,
			   cut from the start */
			if (x >= c[2])
				v = x - c[2];

			s = splice(s, NULL, v, c[2]);
			s = s[1];
		}

		/* if it's a password, change everything to asterisks */
		if (flags.password)
			s = sregex('/./g', s, flags.password);

		/* draws the string */
		mp.tui.move(c[0], c[1], 1);
		mp.tui.addstr(s);
		mp.tui.move(c[0] + x - v, c[1]);

		local k = mp.tui.getkey();

		if (k eq 'space')
			k = ' ';

		if (k eq 'enter')
			break;
		else
		if (k eq 'escape') {
			r = NULL;
			break;
		}
		else
		if (k eq 'backspace' && x > 0) {
			x--;
			r = splice(r, NULL, x, 1);
			r = r[0];
		}
		else
		if(k eq 'delete') {
			r = splice(r, NULL, x, 1);
			r = r[0];
		}
		else
		if (k eq 'ctrl-u') {
			x = 0;
			r = '';
		}
		else
		if (k eq 'ctrl-k') {
			r = splice(r, NULL, x, -1);
			r = r[0];
		}
		else
		if (k eq 'cursor-up' && size(h)) {
			i--;
			r = h[i % size(h)];
			x = size(r);
		}
		else
		if (k eq 'cursor-down' && size(h)) {
			i++;
			r = h[i % size(h)];
			x = size(r);
		}
		else
		if (k eq 'cursor-left' && x > 0) {
			x--;
		}
		else
		if (k eq 'cursor-right' && x < size(r)) {
			x++;
		}
		else
		if (k eq 'home') {
			x = 0;
		}
		else
		if (k eq 'end') {
			x = size(r);
		}
		else
		if (k eq 'tab' && flags.file) {
			local l = glob(r ~ '*');

			if (size(l)) {
				local p = mp.tui.list(prompt, l, 0);

				if (p == NULL)
					r = NULL;
				else
					r = l[p];

				break;
			}
		}
		else
		if (size(k) == 1) {
			r = splice(r, k, x, 0);
			r = r[0];
			x++;
		}
	}

	/* if a string was accepted, store in the history */
	if (h != NULL && size(r) && h[-1] ne r)
		push(h, r);

	return r;
}


sub mp.tui.list(prompt, data, pos)
/* select from a list */
{
	local vy, ty, r;

	mp.tui.attr(mp.colors.menu.attr);
	mp.tui.move(0, 0, 1);
	mp.tui.addstr(prompt);
	mp.tui.attr(mp.colors.normal.attr);

	vy = 0;
	ty = mp.window.ty - 1;

	/* clipping regex */
	r = '/^.{1,' ~ (mp.window.tx) ~ '}/';

	if (pos == NULL)
		pos = 0;

	while (1) {
		local k, n;

		/* limits for pos */
		if (pos < 0)
			pos = 0;
		if (pos >= size(data))
			pos = size(data) - 1;

		/* limits for vy */
		if (pos < vy)
			vy = pos;
		if (vy + ty <= pos)
			vy = pos - ty + 1;

		/* draw all the lines */
		n = 0;
		while (n < ty) {
			local l = data[n + vy];

			/* no more data? */
			if (l == NULL)
				break;

			mp.tui.move(0, n + 1, 1);

			if (n + vy == pos)
				mp.tui.attr(mp.colors.cursor.attr);
			else
				mp.tui.attr(mp.colors.normal.attr);

			mp.tui.addstr(regex(r,
				sprintf("%-" ~ mp.window.tx ~ "s", l)));

			n++;
		}

		/* clean the rest of lines */
		mp.tui.attr(mp.colors.normal.attr);
		while (n < ty) {
			mp.tui.move(0, n + 1, 1);
			n++;
		}

		k = mp.tui.getkey();

		if (k eq 'cursor-up')
			pos--;
		else
		if (k eq 'cursor-down')
			pos++;
		else
		if (k eq 'page-up')
			pos -= ty;
		else
		if (k eq 'page-down')
			pos += ty;
		else
		if (k eq 'home')
			pos = 0;
		else
		if (k eq 'end')
			pos = size(data) - 1;
		else
		if (k eq 'enter')
			break;
		else
		if (k eq 'escape') {
			pos = NULL;
			break;
		}
	}

	return pos;
}


/* interface */

sub mp.drv.alert(msg)
{
	mp.tui.prompt(msg ~ L(" [ENTER]"));

	while (mp.tui.getkey() ne 'enter');
}


sub mp.drv.confirm(msg, def)
{
	local y, n;
	local ret = NULL;

	/* get the initials for localized 'Yes' and 'No' */
	y = regex('/^./', L("Yes"));
	n = regex('/^./', L("No"));

	/* add options */
	msg = msg ~ ' (' ~ y ~ '/' ~ n ~ ')';

	if (def != NULL) {
		/* a default option? add to prompt */
		msg = msg ~ ' [' ~ (def && y || n) ~ ']';
	}

	mp.tui.prompt(msg);

	while (ret == NULL) {
		local k = mp.tui.getkey();

		if (regex('/^' ~ y ~ '$/i', k))
			ret = 1;
		if (regex('/^' ~ n ~ '$/i', k))
			ret = 2;
		if (k eq 'escape')
			ret = 0;
		if (k eq 'enter')
			ret = (def && 1 || 2);
	}

	return ret;
}

sub mp.drv.openfile(prompt)
{
	mp.tui.readline(prompt, 'openfile', NULL, { 'file' => 1 } );
}


sub mp.drv.savefile(prompt)
{
	mp.tui.readline(prompt, 'savefile', NULL, { 'file' => 1 } );
}


sub mp.drv.form(widgets)
{
	local r = [];

	foreach (w, widgets) {
		local r1 = NULL;

		if (w.type eq 'text')
			r1 = mp.tui.readline(w.label, w.history, w.value);
		else
		if (w.type eq 'password')
			r1 = mp.tui.readline(w.label, NULL, NULL,
				{ 'password' => '*' });
		else
		if (w.type eq 'checkbox') {
			/* return value conversion */
			local c = [ NULL, 1, 0 ];

			r1 = c[mp.drv.confirm(w.label, w.value)];
		}
		else
		if (w.type eq 'list')
			r1 = mp.tui.list(w.label, w.list, w.value);

		/* cancellation? */
		if (r1 == NULL) {
			r = NULL;
			break;
		}

		/* store value */
		push(r, r1);
	}

	return r;
}


sub mp.drv.menu()
{
	local mx = 0;
	local action = NULL;
	local key = NULL;

	while (action == NULL && key ne 'escape') {
		local pos, mo, my;
		local n = 0;

		/* wrap */
		if (mx < 0)
			mx = size(mp.menu) - 1;
		if (mx >= size(mp.menu))
			mx = 0;

		/* draw the menu bar */
		mp.tui.attr(mp.colors.menu.attr);
		mp.tui.move(0, 0, 1);

		while (n < size(mp.menu)) {
			/* get the label */
			local l = L(mp.menu[n][0]);

			/* strip (by now) the & */
			l = sregex('/&/g', l, NULL);

			mp.tui.attr(mp.colors.menu.attr);
			mp.tui.addstr('   ');

			if (n == mx) {
				pos = mp.tui.getxy();
				mp.tui.attr(mp.colors.cursor.attr);
			}

			mp.tui.addstr(l);

			n++;
		}

		/* get the menu options */
		mo = mp.menu[mx][1];

		/* calculate panel optimal dimensions */
		pos[2] = 0;

		foreach (i, mo) {
			local l = mp.menu_label(i);

			if (size(l) > pos[2])
				pos[2] = size(l);
		}

		/* if the panel will surpass the right margin,
		   move to the left */
		if (pos[0] + pos[2] > mp.window.tx - 2)
			pos[0] = mp.window.tx - pos[2] - 2;

		mp.tui.refresh();
		mp.tui.attr(mp.colors.menu.attr);
		mp.tui.openpanel(pos[0], 1, pos[2] + 2, size(mo) + 2);

		my = 0;

		while (key ne 'escape') {
			/* draw the options */
			n = 0;
			while (n < size(mo)) {
				local l = mp.menu_label(mo[n]);

				/* set color */
				if (n == my)
					mp.tui.attr(mp.colors.cursor.attr);
				else
					mp.tui.attr(mp.colors.menu.attr);

				if (l != NULL) {
					mp.tui.move(1, 1 + n);
					mp.tui.addstr(sprintf("%-" ~ pos[2] ~ "s", l));
				}

				n++;
			}

			/* move the hw cursor to the selected option */
			mp.tui.move(1, 1 + my);

			mp.tui.refresh();

			key = mp.tui.getkey();

			if (key eq 'cursor-up') {
				/* move up avoiding separators */
				while (1) {
					if (--my < 0)
						my = size(mo) - 1;

					if (mo[my] ne '-')
						break;
				}
			}
			else
			if (key eq 'cursor-down') {
				/* move down avoiding separators */
				while (1) {
					if (++my >= size(mo))
						my = 0;

					if (mo[my] ne '-')
						break;
				}
			}
			else
			if (key eq 'cursor-right') {
				mx++;
				break;
			}
			else
			if (key eq 'cursor-left') {
				mx--;
				break;
			}
			else
			if (key eq 'enter') {
				action = mo[my];
				break;
			}
		}

		mp.tui.closepanel();
	}

	mp.tui.attr(mp.color.normal.attr);

	if (action != NULL)
		mp.process_action(action);

	return NULL;
}


sub mp.drv.busy(onoff)
{
	mp.tui.prompt(onoff && L("Please, wait...") || '');
}


/* returns the main namespace */
mp.tui;
