/*
 * Electric(tm) VLSI Design System
 *
 * File: dbterminal.c
 * General messages terminal output handler
 * Written by: Steven M. Rubin, Static Free Software
 *
 * Copyright (c) 2000 Static Free Software.
 *
 * Electric(tm) 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.
 *
 * Electric(tm) 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 Electric(tm); see the file COPYING.  If not, write to
 * the Free Software Foundation, Inc., 59 Temple Place, Suite 330,
 * Boston, Mass 02111-1307, USA.
 *
 * Static Free Software
 * 4119 Alpine Road
 * Portola Valley, California 94028
 * info@staticfreesoft.com
 */

#include "global.h"
#include "usr.h"
#include "edialogs.h"

INTSML   us_ttymute;					/* nonzero to supress unimportant messages */
INTBIG	 us_ttypamyItemHit;				/* item hit during parsing */
INTSML	 us_ttyttywriteseparator;		/* nonzero to write separator before next text */
INTSML   us_ttyttyinited;

/* ttygetline() Dialog */
DIALOGITEM us_ttyinputdialogitems[] =
{
 /*  1 */ {0, {96,200,120,264}, BUTTON, N_("OK")},
 /*  2 */ {0, {96,24,120,88}, BUTTON, N_("Cancel")},
 /*  3 */ {0, {58,38,74,246}, EDITTEXT, ""},
 /*  4 */ {0, {3,9,51,281}, MESSAGE, ""}
};
DIALOG us_ttyinputdialog = {{50,75,188,371}, "", 0, 4, us_ttyinputdialogitems};

/* Full Input Dialog */
DIALOGITEM us_ttyfulldialogitems[] =
{
 /*  1 */ {0, {160,328,184,392}, BUTTON, N_("OK")},
 /*  2 */ {0, {104,328,128,392}, BUTTON, N_("Cancel")},
 /*  3 */ {0, {24,8,56,408}, EDITTEXT, ""},
 /*  4 */ {0, {3,8,19,408}, MESSAGE, ""},
 /*  5 */ {0, {88,8,212,294}, SCROLL, ""},
 /*  6 */ {0, {72,56,88,270}, MESSAGE, N_("Type '?' for a list of options")}
};
DIALOG us_ttyfulldialog = {{50,75,271,493}, N_("Command Completion"), 0, 6, us_ttyfulldialogitems};

/* prototypes for local routines */
INTSML us_ttyinttychar(COMCOMP**, INTSML*);
void us_ttyoutttychar(char*);
void us_ttyerasettychar(void);
void us_ttykillttychar(void);

/************************* INITIALIZATION/TERMINATION *************************/

/*
 * initialize the text terminal and saves state information
 * so that we can restore its characteristics later.  This
 * routine is called once at the start of the program.
 */
void us_ttyinit(void)
{
	/* allocate character arrays that are as wide as the terminal */
	us_pattyline = us_paambiguous = 0;
	us_ttymute = 0;
	us_ttyttywriteseparator = 0;
	us_ttyttyinited = 1;
}

/*
 * close the terminal (called once at the end of the program)
 */
void us_ttyclose(void)
{
	if (us_pattyline != 0) efree(us_pattyline);
	if (us_paambiguous != 0) efree(us_paambiguous);
}

/***************************** TERMINAL OUTPUT *****************************/

/*
 * routine to mute nonerror messages if flag is nonzero
 * (returns previous state)
 */
INTSML ttyquiet(INTSML flag)
{
	REGISTER INTSML prev;

	prev = us_ttymute;
	us_ttymute = flag;
	return(prev);
}

void ttynewcommand(void)
{
	us_ttyttywriteseparator = 1;
}

/*
 * routine to output a normal message into the messages window
 * in the style of "printf"
 */
void ttyputmsg(char *msg, ...)
{
	va_list ap;

	/* don't print or save this message if muted */
	if (us_ttymute != 0) return;

	/* don't print this message if quit or aborted */
	if (el_pleasestop != 0) return;

	var_start(ap, msg);
	us_ttyprint(0, msg, ap);
	va_end(ap);
}

/*
 * routine to output a "verbose" message (those that provide unimportant
 * information that can be done without) into the messages window
 * in the style of "printf"
 */
void ttyputverbose(char *msg, ...)
{
	va_list ap;

	/* ignore this message if facts are turned off */
	if ((us_aid->aidstate&JUSTTHEFACTS) != 0) return;

	/* don't print or save this message if muted */
	if (us_ttymute != 0) return;

	/* don't print this message if quit or aborted */
	if (el_pleasestop != 0) return;

	var_start(ap, msg);
	us_ttyprint(0, msg, ap);
	va_end(ap);
}

/*
 * routine to output an error message into the messages window
 * in the style of "printf"
 */
void ttyputerr(char *msg, ...)
{
	va_list ap;

	var_start(ap, msg);
	ttybeep();
	us_ttyprint(1, msg, ap);
	va_end(ap);
}

/*
 * Routine to put out a message that is in 2 parts: a keystroke and a meaning.
 * The length of the "keystroke" field is "length".
 */
void ttyputinstruction(char *keystroke, INTBIG length, char *meaning)
{
	REGISTER INTBIG i;

	(void)initinfstr();
	for(i=0; i<length; i++)
	{
		if (*keystroke == 0) (void)addtoinfstr(' '); else
			(void)addtoinfstr(*keystroke++);
	}
	(void)addstringtoinfstr(meaning);
	ttyputmsg("%s", returninfstr());
}

void ttyputusage(char *usage)
{
	ttyputerr(_("Usage: %s"), usage);
}

void ttyputbadusage(char *command)
{
	ttyputerr(_("Bad '%s' command"), command);
}

void ttyputnomemory(void)
{
	ttyputerr(_("No memory"));
}

/*
 * routine to cause the current command to be aborted by removing it from
 * any macro and by printing the message on the terminal
 */
void us_abortcommand(char *msg, ...)
{
	va_list ap;

	var_start(ap, msg);
	us_ttyprint(1, msg, ap);
	va_end(ap);
	us_state |= COMMANDFAILED;
	if (getvalkey((INTBIG)us_aid, VAID, VSTRING, us_macrobuilding) != NOVARIABLE)
		ttyputmsg(_("Command ignored from macro definition"));
	us_unknowncommand();
}

/****************************** TERMINAL INPUT ******************************/

/*
 * ttygetchar gets the next character from the text keyboard.
 * The routine must call the graphics module to do input.
 */
INTSML ttygetchar(void)
{
	return(getnxtchar());
}

/*
 * routine to print "prompt" and then read a line of text from the terminal
 * The address of the text line is returned (0 if cancelled).
 */
char *ttygetline(char *prompt)
{
	INTBIG itemHit;
	char *line, *pt, *defaultval, localprompt[256];

	/* parse default value */
	strcpy(localprompt, prompt);
	defaultval = "";
	for(pt=localprompt; *pt != 0; pt++) if (*pt == '[') break;
	if (*pt != 0)
	{
		*pt++ = 0;
		defaultval = pt;
		for( ; *pt != 0; pt++) if (*pt == ']') break;
		if (*pt == ']') *pt = 0;
	}

	/* display the dialog box */
	if (DiaInitDialog(&us_ttyinputdialog) != 0) return(0);
	DiaSetText(4, localprompt);
	DiaSetText(-3, defaultval);

	/* loop until done */
	for(;;)
	{
		itemHit = DiaNextHit();
		if (itemHit == CANCEL) break;
		if (itemHit == OK) break;
	}

	if (itemHit != CANCEL)
		line = us_putintoinfstr(DiaGetText(3));

	/* terminate the dialog */
	DiaDoneDialog();
	if (itemHit == CANCEL) return(0);
	return(line);
}

/*
 * routine to display "prompt" and then accept a line of text from the
 * messages window.  The address of the text line is returned.  Returns
 * zero if end-of-file is typed (^D).
 */
char *ttygetlinemessages(char *prompt)
{
	return(getmessagesstring(prompt));
}

/*
 * routine to get a string with "parameter" information (does command completion)
 */
INTSML ttygetparam(char *prompt, COMCOMP *parameter, INTSML keycount, char *paramstart[])
{
	REGISTER INTBIG itemHit;
	extern DIALOG us_listdialog;

	/* is this necessary?  if not, us_pathiskey does not need to be global */
	us_pathiskey = parameter->ifmatch;

	/* use dialogs if requested */
	if ((us_aid->aidstate & USEDIALOGS) != 0)
	{
		return(us_specialparse(prompt, parameter, keycount, paramstart));
	}

	/* the general case: display the dialog box */
	if (DiaInitDialog(&us_listdialog) != 0) return(0);
	DiaInitTextDialog(3, parameter->toplist, parameter->nextcomcomp, DiaNullDlogDone,
		0, SCSELMOUSE|SCSELKEY|SCDOUBLEQUIT);
	DiaSetText(4, prompt);

	for(;;)
	{
		itemHit = DiaNextHit();
		if (itemHit == OK || itemHit == CANCEL) break;
	}
	paramstart[0] = us_putintoinfstr(DiaGetScrollLine(3, DiaGetCurLine(3)));
	DiaDoneDialog();
	if (itemHit == CANCEL) return(0);
	return(1);
}

/*
 * Routine to get a string with "parameter" information (does command completion).
 * Returns the number of strings parsed into "paramstart" (-1 if cancelled or
 * on error).
 */
INTSML ttygetfullparam(char *prompt, COMCOMP *parameter, INTSML keycount,
	char *paramstart[])
{
	INTSML i;

	/* initialize for command completion */
	us_expandttybuffers(80);
	us_pacurchar = 0;
	us_pattyline[0] = 0;
	us_palinestart = us_pattyline;
	us_paparamstart = paramstart;
	us_pakeycount = MAXPARS;
	us_paparamstart[us_paparamcount = 0] = us_palinestart;
	us_paparamtype[us_paparamcount] = parameter;
	us_paparamtype[us_paparamcount+1] = NOCOMCOMP;

	/* display the dialog box */
	if (DiaInitDialog(&us_ttyfulldialog) != 0) return(-1);
	DiaInitTextDialog(5, DiaNullDlogList, DiaNullDlogItem, DiaNullDlogDone, -1, 0);
 	DiaCharacterEdit(3);

	/* load header message */
	(void)initinfstr();
	if (*parameter->noise != 0)
	{
		(void)addstringtoinfstr(parameter->noise);
		if (parameter->def != 0)
			(void)formatinfstr(" (%s)", parameter->def);
	} else (void)addstringtoinfstr(prompt);
	DiaSetText(4, returninfstr());

	for(;;)
	{
		us_ttypamyItemHit = 0;
		if (us_pagetword(us_ttyinttychar, us_ttyoutttychar, us_ttyerasettychar,
			us_ttykillttychar, 0) != 0) break;
		if (us_ttypamyItemHit == CANCEL || us_ttypamyItemHit == OK) break;
	}

	/* parse the line if not cancelled */
	if (us_ttypamyItemHit == CANCEL) us_paparamcount = -1; else
	{
		us_paparamcount++;
		for(i=1; i<us_paparamcount; i++) (us_paparamstart[i])[-1] = 0;
		if (*us_paparamstart[us_paparamcount-1] == 0) us_paparamcount--;
	}
	DiaDoneDialog();
	return(us_paparamcount);
}

/***************************** INTERNAL SUPPORT *****************************/

/*
 * internal routine to print a message in the scrolling area with the
 * style of "printf".  Pops up the messages window if "important" is nonzero.
 */
void us_ttyprint(INTSML important, char *msg, va_list ap)
{
	char line[8192];
	REGISTER INTSML i, j, k;
	static INTBIG sepcount = 0;

	if (us_ttyttyinited == 0)
	{
		(void)vsprintf(line, msg, ap);
		error("%s", line);
		return;
	}

	if (us_ttyttywriteseparator != 0)
	{
		sepcount++;
		sprintf(line,
			"================================= %ld =================================",
				sepcount);
		putmessagesstring(line, important);
		if (us_termaudit != 0) xprintf(us_termaudit, "%s\n", line);
		us_ttyttywriteseparator = 0;
	}

	/* build the output line */
	(void)vsprintf(line, msg, ap);

	/* remove excessive trailing space */
	i = strlen(line);
	while (i > 1 && (line[i-1] == ' ' || line[i-1] == '\t') &&
		(line[i-2] == ' ' || line[i-2] == '\t')) line[--i] = 0;

	/* break the line at newline characters */
	for(k=j=0; j<i; j++)
	{
		if (line[j] == '\n')
		{
			line[j] = 0;
			putmessagesstring(&line[k], important);
			if (us_termaudit != 0) xprintf(us_termaudit, "%s\n", &line[k]);
			k = j + 1;
		}
	}

	if (k < i)
	{
		putmessagesstring(&line[k], important);
		if (us_termaudit != 0) xprintf(us_termaudit, "%s\n", &line[k]);
	}
}

INTSML us_ttyinttychar(COMCOMP **newcomcomp, INTSML *reset)
{
	INTSML chr;

	*reset = 0;
	for(;;)
	{
		chr = DiaGetNextCharacter(&us_ttypamyItemHit);
		if (chr == -1) continue;
		if (chr == -2)
		{
			if (us_ttypamyItemHit == 1 || us_ttypamyItemHit == 2) return(EOF);
			continue;
		}
		break;
	}
	if (chr == CTRLCKEY) chr = '\n';
	return(chr);
}

void us_ttyoutttychar(char *t)
{
	INTBIG len;
	char *newmsg, *line;

	len = strlen(t);
	if (len == 0) return;
	line = DiaGetText(3);
	newmsg = (char *)emalloc(strlen(line)+len+1, el_tempcluster);
	(void)strcpy(newmsg, line);
	(void)strcat(newmsg, t);
	DiaSetText(3, newmsg);
	efree(newmsg);
}

void us_ttyerasettychar(void)
{
	char *line;

	line = DiaGetText(3);
	line[strlen(line)-1] = 0;
	DiaSetText(3, line);
}

void us_ttykillttychar(void)
{
	DiaSetText(3, "");
}
