/* guts.c - the guts which do the actual work
 *
 * This file contains all of the internal functions which perform the actions
 * required of a terminal, such as scrolling and cursor movement. By the time
 * functions in this file have been reached, all callbacks have been called
 * that needed to be called.
 */

#include <stdio.h>
#include <string.h>
#include "Internal.h"

#define VT100ID	"\033[?1;0c"
#define VT102ID	"\033[?6c"		    /* from Linux code */


/* Linefeed, scrolling the screen up (with callbacks) if necessary.
 */
int vt100__lf(vt_info vt)
{
	vt->scroll_by = 1;
	vt->y++;
	if (vt->y > vt->rows)
		return (vt100_action(vt, VT_ACTION_SCROLL_UP));
	return (0);
}


/* Move up, scrolling the screen down (with callbacks) if necessary.
 */
int vt100__ri(vt_info vt)
{
	vt->scroll_by = 1;
	vt->y--;
	if (vt->y < 1)
		return (vt100_action(vt, VT_ACTION_SCROLL_DOWN));
	return (0);
}


/* Return VT100 identification string using the callout function, if given.
 */
int vt100__id(vt_info vt)
{
	if (!vt->output_function)
		return (0);
	(vt->output_function) (VT100ID);
	return (0);
}


/* Save cursor and attributes.
 */
int vt100__save_cur(vt_info vt)
{
	vt->saved_x = vt->x;
	vt->saved_y = vt->y;
	vt->saved_attr = vt->current_attr;
	return (0);
}


/* Restore cursor and attributes.
 */
int vt100__restore_cur(vt_info vt)
{
	vt->x = vt->saved_x;
	vt->y = vt->saved_y;
	if (vt->x < 1)
		vt->x = 1;
	if (vt->y < 1)
		vt->y = 1;
	if (vt->y > vt->rows)
		vt->y = vt->rows;
	vt->current_attr = vt->saved_attr;
	return (0);
}


/* Status report, using the callout function if one was given.
 */
int vt100__status(vt_info vt)
{
	if (!vt->output_function)
		return (0);
	(vt->output_function) ("\033[0n");  /* terminal OK */
	return (0);
}


/* Send cursor position report using the callout function, if one was given.
 */
int vt100__cursor(vt_info vt)
{
	char buf[40];

	if (!vt->output_function)
		return (0);

	sprintf(buf, "\033[%d;%dR", vt->y, vt->x);
	(vt->output_function) (buf);

	return (0);
}


/* Move cursor to new position.
 */
int vt100__goto(vt_info vt)
{
	vt->x = vt->new_x;
	vt->y = vt->new_y;
	if (vt->x < 1)
		vt->x = 1;
	if (vt->x > vt->cols)
		vt->x = vt->cols;
	if (vt->y < 1)
		vt->y = 1;
	if (vt->y > vt->rows)
		vt->y = vt->rows;
	return (0);
}


/* Fill screen with 'E' (DEC alignment test)
 */
int vt100__decalign(vt_info vt)
{
	int i;

	memset(vt->text, 'E', vt->rows * vt->cols);
	for (i = 0; i < (vt->rows * vt->cols); i++) {
		vt->attrs[i] = VT_COLOUR_WHITE << VT_FOREGROUND_SHIFT;
	}

	return (0);
}


/* Clear all/part of screen/line.
 *
 *   par[0] == 0-9: ESC K
 *   par[0] == 10+: ESC J
 *
 * All cleared characters have attributes reset and text set to ' '.
 */
int vt100__clear(vt_info vt)
{
	int l_start, l_end, d_start, d_end, curs;
	int start, end;
	int i;

	l_start = (vt->y - 1) * (vt->cols); /* line start index */
	l_end = l_start + vt->cols;	    /* line end index */
	d_start = 0;			    /* display start index */
	d_end = vt->cols * vt->rows;	    /* display end index */
	curs = l_start + vt->x - 1;	    /* cursor index */

	switch (vt->par[0]) {
	case 0:
		start = curs;
		end = l_end;
		break;			    /* cursor to end of line */
	case 1:
		start = l_start;
		end = curs;
		break;			    /* start of line to cursor */
	case 2:
		start = l_start;
		end = l_end;
		break;			    /* entire line */
	case 10:
		start = curs;
		end = d_end;
		break;			    /* cursor to end of screen */
	case 11:
		start = d_start;
		end = curs;
		break;			    /* start of screen to cursor */
	case 12:
		start = d_start;
		end = d_end;
		break;			    /* entire screen */
	default:
		return (0);		    /* unknown code - ignore */
	}

	for (i = start; i < end; i++) {
		vt->text[i] = ' ';
		vt->attrs[i] = VT_COLOUR_WHITE << VT_FOREGROUND_SHIFT;
	}

	return (0);
}


/* Scroll the screen up by "vt->scroll_by" lines, moving the cursor with it.
 * By "scroll up", we mean move the text - and the cursor - upwards.
 *
 * Newly uncovered lines are blanked with black background, white foreground.
 */
int vt100__scroll_up(vt_info vt)
{
	int amount, size;
	int i;

	amount = vt->cols * vt->scroll_by;
	size = vt->cols * vt->rows;

	if ((amount <= 0) || (amount > size))
		return (-1);

	for (i = amount; i < size; i++) {   /* move text and attributes */
		vt->text[i - amount] = vt->text[i];
		vt->attrs[i - amount] = vt->attrs[i];
	}

	for (i = size - amount; i < size; i++) {	/* blank new lines */
		vt->text[i] = ' ';
		vt->attrs[i] = VT_COLOUR_WHITE << VT_FOREGROUND_SHIFT;
	}

	vt->y -= vt->scroll_by;		    /* move cursor */
	if (vt->y < 1)
		vt->y = 1;

	return (0);
}


/* Scroll the screen down by "vt->scroll_by" lines, moving the cursor with it.
 * By "scroll down", we mean move the text - and the cursor - downwards.
 *
 * Newly uncovered lines are blanked with black background, white foreground.
 */
int vt100__scroll_down(vt_info vt)
{
	int amount, size;
	int i;

	amount = vt->cols * vt->scroll_by;
	size = vt->cols * vt->rows;

	if ((amount <= 0) || (amount > size))
		return (-1);

	for (i = (size - amount) - 1; i >= 0; i--) {	/* move text and attributes */
		vt->text[i + amount] = vt->text[i];
		vt->attrs[i + amount] = vt->attrs[i];
	}

	for (i = 0; i < amount; i++) {	    /* blank new lines */
		vt->text[i] = ' ';
		vt->attrs[i] = VT_COLOUR_WHITE << VT_FOREGROUND_SHIFT;
	}

	vt->y += vt->scroll_by;		    /* move cursor */
	if (vt->y > vt->rows)
		vt->y = vt->rows;

	return (0);
}


/* Set graphics rendition by reading each of vt->par[] and processing.
 */
int vt100__rendition(vt_info vt)
{
	int i;

	for (i = 0; i <= vt->npar; i++) {
		switch (vt->par[i]) {
		case 0:		    /* reset attributes */
/*
        vt->current_attr &= (VT_FOREGROUND_MASK | VT_BACKGROUND_MASK);
*/
			vt->current_attr =
			    VT_COLOUR_WHITE << VT_FOREGROUND_SHIFT;

			/* The commented out code above is to only switch off attributes; it seems
			 * more common for ESC [ m to be supposed to reset colours too.
			 */

			break;
		case 1:
			vt->current_attr |= VT_ATTR_BOLD;
			break;		    /* bold */
		case 2:
			vt->current_attr |= VT_ATTR_FAINT;
			break;		    /* faint */
		case 3:
			vt->current_attr |= VT_ATTR_STANDOUT;
			break;		    /* standout */
		case 4:
			vt->current_attr |= VT_ATTR_UNDERLINE;
			break;		    /* underline */
		case 5:
			vt->current_attr |= VT_ATTR_BLINK;
			break;		    /* flashing */
		case 7:
			vt->current_attr |= VT_ATTR_REVERSE;
			break;		    /* negative */
		case 21:
		case 22:		    /* normal intensity */
			vt->current_attr ^=
			    vt->current_attr & VT_ATTR_BOLD;
			vt->current_attr ^=
			    vt->current_attr & VT_ATTR_FAINT;
			vt->current_attr ^=
			    vt->current_attr & VT_ATTR_STANDOUT;
			break;
		case 23:
			vt->current_attr ^=
			    vt->current_attr & VT_ATTR_STANDOUT;
			break;
		case 24:
			vt->current_attr ^=
			    vt->current_attr & VT_ATTR_UNDERLINE;
			break;
		case 25:
			vt->current_attr ^=
			    vt->current_attr & VT_ATTR_BLINK;
			break;
		case 27:
			vt->current_attr ^=
			    vt->current_attr & VT_ATTR_REVERSE;
			break;
		case 38:
		case 39:		    /* default foreground (7) */
			vt->current_attr ^=
			    vt->current_attr & VT_FOREGROUND_MASK;
			vt->current_attr |=
			    (VT_COLOUR_WHITE << VT_FOREGROUND_SHIFT);
			break;
		case 49:		    /* default background (0) */
			vt->current_attr ^=
			    vt->current_attr & VT_BACKGROUND_MASK;
			vt->current_attr |=
			    (VT_COLOUR_BLACK << VT_BACKGROUND_SHIFT);
			break;
		default:
			if (vt->par[i] >= 30 && vt->par[i] <= 37) {
				vt->current_attr ^=
				    vt->current_attr & VT_FOREGROUND_MASK;
				vt->current_attr |=
				    (vt->par[i] -
				     30) << VT_FOREGROUND_SHIFT;
			} else if (vt->par[i] >= 40 && vt->par[i] <= 47) {
				vt->current_attr ^=
				    vt->current_attr & VT_BACKGROUND_MASK;
				vt->current_attr |=
				    (vt->par[i] -
				     40) << VT_BACKGROUND_SHIFT;
			}
			break;
		}
	}

	return (0);
}


/* Set terminal mode - IGNORED
 *
 *    Ps = 4            (A)     Insert Mode
 *         20           (A)     `Automatic Linefeed' Mode.
 *         34                   Normal Cursor Visibility
 *         ?1           (V)     Application Cursor Keys
 *         ?3           (V)     Change Terminal Width to 132 columns
 *         ?5           (V)     Reverse Video
 *         ?6           (V)     `Origin' Mode
 *         ?7           (V)     `Wrap' Mode
 *         ?25          (V)     Visible Cursor
 *
 * (A) = ANSI, (V) = VT100
 */
int vt100__set_mode(vt_info vt)
{
	return (0);
}


/* reset terminal mode - IGNORED
 */
int vt100__reset_mode(vt_info vt)
{
	return (0);
}


/* delete character(s)
 */
int vt100__del_char(vt_info vt)
{
	int num, l_start, l_end, curs, i;

	num = vt->par[0];
	if (num < 1)
		num = 1;

	l_start = (vt->y - 1) * (vt->cols); /* line start index */
	l_end = l_start + vt->cols;	    /* line end index */
	curs = l_start + vt->x - 1;	    /* cursor index */

	if ((curs + num) > l_end)
		num = l_end - curs;

	for (i = curs; i < (l_end - num); i++) {
		vt->text[i] = vt->text[i + num];
		vt->attrs[i] = vt->attrs[i + num];
	}

	for (; i < l_end; i++) {
		vt->text[i] = ' ';
		vt->attrs[i] = VT_COLOUR_WHITE << VT_FOREGROUND_SHIFT;
	}

	return (0);
}


/* delete line(s)
 */
int vt100__del_line(vt_info vt)
{
	int num, l_start, l_end, size, i;

	num = vt->par[0];
	if (num < 1)
		num = 1;

	l_start = (vt->y - 1) * (vt->cols); /* start line index */
	l_end = l_start + num * vt->cols;   /* end of end line index */
	size = vt->cols * vt->rows;	    /* display size */

	if (l_end > size)
		l_end = size;

	for (i = l_start; i < (l_end - num * vt->cols); i++) {
		vt->text[i] = vt->text[i + num * vt->cols];
		vt->attrs[i] = vt->attrs[i + num * vt->cols];
	}

	for (; i < l_end; i++) {
		vt->text[i] = ' ';
		vt->attrs[i] = VT_COLOUR_WHITE << VT_FOREGROUND_SHIFT;
	}

	return (0);
}


/* insert character(s)
 */
int vt100__ins_char(vt_info vt)
{
	int num, l_start, l_end, curs, i;

	num = vt->par[0];
	if (num < 1)
		num = 1;

	l_start = (vt->y - 1) * (vt->cols); /* line start index */
	l_end = l_start + vt->cols;	    /* line end index */
	curs = l_start + vt->x - 1;	    /* cursor index */

	if ((curs + num) > l_end)
		num = l_end - curs;

	for (i = l_end - 1; i >= (curs + num); i--) {
		vt->text[i] = vt->text[i - num];
		vt->attrs[i] = vt->attrs[i - num];
	}

	for (; i >= curs; i--) {
		vt->text[i] = ' ';
		vt->attrs[i] = VT_COLOUR_WHITE << VT_FOREGROUND_SHIFT;
	}

	return (0);
}


/* insert line(s)
 */
int vt100__ins_line(vt_info vt)
{
	int num, l_start, l_end, size, i;

	num = vt->par[0];
	if (num < 1)
		num = 1;

	l_start = (vt->y - 1) * (vt->cols); /* start line index */
	l_end = l_start + num * vt->cols;   /* end of end line index */
	size = vt->cols * vt->rows;	    /* display size */

	if (l_end > size)
		l_end = size;
	if ((l_end - num * vt->cols) < l_start)
		num = (l_end - l_start) / vt->cols;

	for (i = l_end - 1; i >= (l_start + num * vt->cols); i++) {
		vt->text[i] = vt->text[i - num * vt->cols];
		vt->attrs[i] = vt->attrs[i - num * vt->cols];
	}

	for (; i >= l_start; i--) {
		vt->text[i] = ' ';
		vt->attrs[i] = VT_COLOUR_WHITE << VT_FOREGROUND_SHIFT;
	}

	return (0);
}


/* EOF */
