/* vi:ts=4:sw=4
 *
 * VIM - Vi IMproved		by Bram Moolenaar
 *
 * Read the file "credits.txt" for a list of people who contributed.
 * Read the file "uganda.txt" for copying and usage conditions.
 */

/*
 * message.c: functions for displaying messages on the command line
 */

#include "vim.h"
#include "globals.h"
#define MESSAGE			/* don't include prototype for smsg() */
#include "proto.h"
#include "param.h"
#ifdef KANJI
#include "kanji.h"
#endif

static int msg_check_screen __ARGS((void));

static int lines_left = -1;			/* lines left for listing */

/*
 * msg(s) - displays the string 's' on the status line
 * return TRUE if wait_return not called
 */
	int
msg(s)
	char_u		   *s;
{
	if (!screen_valid())			/* terminal not initialized */
	{
		fprintf(stderr, (char *)s);
		fflush(stderr);
		return TRUE;
	}

	msg_start();
	if (msg_highlight)			/* actually it is highlighting instead of invert */
		start_highlight();
	msg_outtrans(s, -1);
	if (msg_highlight)
	{
		stop_highlight();
		msg_highlight = FALSE;		/* clear for next call */
	}
	msg_ceol();
	return msg_end();
}

#ifndef PROTO		/* automatic prototype generation does not understand this */
/* VARARGS */
	void
smsg(s, a1, a2, a3, a4, a5, a6, a7, a8, a9, a10)
	char_u		*s;
	long		a1, a2, a3, a4, a5, a6, a7, a8, a9, a10;
{
	sprintf((char *)IObuff, (char *)s, a1, a2, a3, a4, a5, a6, a7, a8, a9, a10);
	msg(IObuff);
}
#endif

/*
 * emsg() - display an error message
 *
 * Rings the bell, if appropriate, and calls message() to do the real work
 *
 * return TRUE if wait_return not called
 */
	int
emsg(s)
	char_u		   *s;
{
	if (p_eb)
		beep();					/* also includes flush_buffers() */
	else
		flush_buffers(FALSE);	/* flush internal buffers */
	(void)set_highlight('e');	/* set highlight mode for error messages */
	msg_highlight = TRUE;
/*
 * Msg returns TRUE if wait_return() was not called.
 * In that case may call sleep() to give the user a chance to read the message.
 * Don't call sleep() if dont_sleep is set.
 */
	if (msg(s))
	{
		if (dont_sleep)
		{
			msg_outchar('\n');	/* one message per line, don't overwrite */
			cmdline_row = msg_row;
			need_wait_return = TRUE;
		}
		else
#ifdef notdef
			sleep(1);			/* give the user a chance to read the message */
#else
		{
			vim_delay();
			vim_delay();
		}
#endif
		return TRUE;
	}
	return FALSE;
}

	int
emsg2(s, a1)
	char_u *s, *a1;
{
	sprintf((char *)IObuff, (char *)s, (char *)a1);
	return emsg(IObuff);
}

/*
 * wait for the user to hit a key (normally a return)
 * if 'redraw' is TRUE, clear and redraw the screen
 * if 'redraw' is FALSE, just redraw the screen
 * if 'redraw' is -1, don't redraw at all
 */
	void
wait_return(redraw)
	int		redraw;
{
	int				c;
	int				oldState;
	int				tmpState;

/*
 * With the global command (and some others) we only need one return at the
 * end. Adjust cmdline_row to avoid the next message overwriting the last one.
 */
	if (no_wait_return)
	{
		need_wait_return = TRUE;
		cmdline_row = msg_row;
		if (!termcap_active)
			starttermcap();
		return;
	}
	need_wait_return = FALSE;
	lines_left = -1;
	oldState = State;
	State = HITRETURN;
	if (got_int)
		msg_outstr((char_u *)"Interrupt: ");

	(void)set_highlight('r');
	start_highlight();
#ifdef ORG_HITRETURN
	msg_outstr("Press RETURN to continue");
	stop_highlight();
	do {
		c = vgetc();
	} while (strchr("\r\n: ", c) == NULL);
	if (c == ':')			 		/* this can vi too (but not always!) */
		stuffcharReadbuff(c);
#else
	msg_outstr((char_u *)"Press RETURN or enter command to continue");
	stop_highlight();
	do
	{
		c = vgetc();
		got_int = FALSE;
	} while (c == Ctrl('C'));
	breakcheck();
# ifdef KANJI
	if (strchr("\r\n ", c) == NULL && c < 0x100)
# else
	if (strchr("\r\n ", c) == NULL)
# endif
		stuffcharReadbuff(c);
#endif

	/*
	 * If the user hits ':' we get a command line from the next line.
	 */
	if (c == ':')
		cmdline_row = msg_row;

	if (!termcap_active)			/* start termcap before redrawing */
		starttermcap();

/*
 * If the window size changed set_winsize() will redraw the screen.
 * Otherwise the screen is only redrawn if 'redraw' is set and no ':' typed.
 */
	tmpState = State;
	State = oldState;				/* restore State before set_winsize */
	msg_check();
	if (tmpState == SETWSIZE)		/* got resize event while in vgetc() */
		set_winsize(0, 0, FALSE);
	else if (redraw == TRUE)
	{
		if (c == ':')
			must_redraw = CLEAR;
		else
			updateScreen(CLEAR);
	}
	else if (msg_scrolled && c != ':' && redraw != -1)
		updateScreen(VALID);

	if (c == ':')
		skip_redraw = TRUE;			/* skip redraw once */
}

/*
 * Prepare for outputting characters in the command line.
 */
	void
msg_start()
{
	did_msg = TRUE;					/* for doglob() */
	keep_msg = NULL;				/* don't display old message now */
	msg_pos(cmdline_row, 0);
	cursor_off();
	lines_left = cmdline_row;
}

/*
 * Move message position. This should always be used after moving the cursor.
 * Use negative value if row or col does not have to be changed.
 */
	void
msg_pos(row, col)
	int		row, col;
{
	if (row >= 0)
		msg_row = row;
	if (col >= 0)
		msg_col = col;
	screen_start();
}

	void
msg_outchar(c)
	int		c;
{
	char_u		buf[2];

	buf[0] = c;
	buf[1] = NUL;
	msg_outstr(buf);
}

	void
msg_outnum(n)
	long		n;
{
	char_u		buf[20];

	sprintf((char *)buf, "%ld", n);
	msg_outstr(buf);
}

/*
 * output 'len' characters in 'str' (including NULs) with translation
 * if 'len' is -1, output upto a NUL character
 * return the number of characters it takes on the screen
 */
	int
msg_outtrans(str, len)
	register char_u *str;
	register int   len;
{
	int retval = 0;

	if (len == -1)
		len = STRLEN(str);
	while (--len >= 0)
	{
#ifdef KANJI
		char_u		buf[3];
		if (ISkanji(*str) && str[1])
		{
			buf[0] = *str++;
			buf[1] = *str++;
			buf[2] = NUL;
			msg_outstr(buf);
			retval += 2;
			len--;
			continue;
		}
#endif
		msg_outstr(transchar(*str));
		retval += charsize(*str);
		++str;
	}
	return retval;
}

/*
 * print line for :p command
 */
	void
msg_prt_line(s)
	char_u		   *s;
{
	register int	si = 0;
	register int	c;
	register int	col = 0;

	int 			n_extra = 0;
	int             n_spaces = 0;
	char_u			*p = NULL;			/* init to make SASC shut up */
	int 			n;

	for (;;)
	{
		if (n_extra)
		{
			--n_extra;
			c = *p++;
		}
		else if (n_spaces)
		{
		    --n_spaces;
			c = ' ';
		}
		else
		{
			c = s[si++];
#ifdef KANJI
			if (ISkanji(c) && s[si])
			{
				char_u		buf[3];
				buf[0] = c;
				buf[1] = s[si++];
				buf[2] = NUL;
				msg_outstr(buf);
				col += 2;
				continue;
			}
			else
#endif
			if (c == TAB && !curwin->w_p_list)
			{
				/* tab amount depends on current column */
				n_spaces = curbuf->b_p_ts - col % curbuf->b_p_ts - 1;
				c = ' ';
			}
			else if (c == NUL && curwin->w_p_list)
			{
				p = (char_u *)"";
				n_extra = 1;
				c = '$';
			}
			else if (c != NUL && (n = charsize(c)) > 1)
			{
				n_extra = n - 1;
				p = transchar(c);
				c = *p++;
			}
		}

		if (c == NUL)
			break;

		msg_outchar(c);
		col++;
	}
}

/*
 * output a string to the screen at position msg_row, msg_col
 * Update msg_row and msg_col for the next message.
 */
	void
msg_outstr(s)
	char_u		*s;
{
	int		c;

	/*
	 * if there is no valid screen, use fprintf so we can see error messages
	 */
	if (!msg_check_screen())
	{
		fprintf(stderr, (char *)s);
		return;
	}

	while (*s)
	{
		/*
		 * the screen is scrolled up when:
		 * - When outputting a newline in the last row
		 * - when outputting a character in the last column of the last row
		 *   (some terminals scroll automatically, some don't. To avoid problems
		 *   we scroll ourselves)
		 */
#ifdef KANJI
		if ((msg_row >= Rows - 1 && (*s == '\n' || msg_col >= Columns - 1))
			|| ((msg_row >= Rows - 1 && (msg_col == Columns - 2 && ISkanji(*s)))))
#else
		if (msg_row >= Rows - 1 && (*s == '\n' || msg_col >= Columns - 1))
#endif
		{
			screen_del_lines(0, 0, 1, (int)Rows);		/* always works */
			msg_row = Rows - 2;
			if (msg_col >= Columns)		/* can happen after screen resize */
				msg_col = Columns - 1;
			++msg_scrolled;
			if (cmdline_row > 0)
				--cmdline_row;
			/*
			 * if screen is completely filled wait for a character
			 */
			if (p_more && --lines_left == 0)
			{
#ifdef notdef
				windgoto((int)Rows - 1, 0);
				outstr((char_u *)"-- more --");
#else
				screen_msg("-- more --", Rows - 1, 0);
#endif
				c = vgetc();
				if (c == CR || c == NL)
					lines_left = 1;
				else if (c == 'q' || c == Ctrl('C'))
					got_int = TRUE;
				else
					lines_left = Rows - 1;
				outstr((char_u *)"\r          ");
			}
			screen_start();
		}
		if (*s == '\n')
		{
			msg_col = 0;
			++msg_row;
		}
		else
		{
#ifdef KANJI
			if (ISkanji(*s) && s[1])
			{
				if (msg_col == (Columns - 1))
				{
					screen_msg(" ", msg_row, msg_col++);
					msg_col = 0;
					msg_row++;
				}
				screen_msg(s++, msg_row, msg_col++);
			}
			else
#endif
			screen_outchar(*s, msg_row, msg_col);
			if (++msg_col >= Columns)
			{
				msg_col = 0;
				++msg_row;
			}
		}
		++s;
	}
}

/*
 * msg_check_screen - check if the screen is initialized.
 * Also check msg_row and msg_col, if they are too big it may cause a crash.
 */
	static int
msg_check_screen()
{
	if (!screen_valid())
		return FALSE;
	
	if (msg_row >= Rows)
		msg_row = Rows - 1;
	if (msg_col >= Columns)
		msg_col = Columns - 1;
	return TRUE;
}

/*
 * clear from current message position to end of screen
 * Note: msg_col is not updated, so we remember the end of the message
 * for msg_check().
 */
	void
msg_ceol()
{
	if (!msg_check_screen())
		return;
	screen_fill(msg_row, msg_row + 1, msg_col, (int)Columns, ' ', ' ');
	screen_fill(msg_row + 1, (int)Rows, 0, (int)Columns, ' ', ' ');
}

/*
 * end putting a message on the screen
 * call wait_return if the message does not fit in the available space
 * return TRUE if wait_return not called.
 */
	int
msg_end()
{
	lines_left = -1;
	/*
	 * if the string is larger than the window,
	 * or the ruler option is set and we run into it,
	 * we have to redraw the window.
	 * Do not do this if we are abandoning the file or editing the command line.
	 */
	if (!exiting && msg_check() && State != CMDLINE)
	{
		msg_outchar('\n');
		wait_return(FALSE);
		return FALSE;
	}
	flushbuf();
	return TRUE;
}

/*
 * If the written message has caused the screen to scroll up, or if we
 * run into the shown command or ruler, we have to redraw the window later.
 */
	int
msg_check()
{
	lines_left = -1;
	if (msg_scrolled || (msg_row == Rows - 1 && msg_col >= sc_col))
	{
		if (must_redraw < NOT_VALID)
			must_redraw = NOT_VALID;
		redraw_cmdline = TRUE;
		return TRUE;
	}
	return FALSE;
}
