/****************************************************
 * vt100 emulator - remote character interpretation
 *
 *	v2.4 861214 DBW - lots of fixes/additions (see readme file)
 *	v2.3 861101 DBW - minor bug fixes
 *	v2.2 861012 DBW - more of the same
 *	v2.1 860915 DBW - new features (see README)
 *	     860823 DBW - Integrated and rewrote lots of code
 *	v2.0 860803 DRB - Rewrote the control sequence parser
 *	v1.1 860720 DBW	- Switches, 80 cols, colors, bug fixes
 *	v1.0 860712 DBW	- First version released
 *
 ****************************************************/

#define MODULE_REMOTE 1
#include "vt100.h"

static int    p[10];
static int    numpar;
static char   escseq[40];

void doctrl();
void doesc();				/* force correct denomination */
void doerase();
void doindex();

/************************************************
*  function to handle remote characters
*************************************************/
void doremote(c)
char c;
    {
    if (c == 24) { inesc = 0; inctrl = 0; return; }
    if (c == 27 || (inesc >= 0 && c >= ' ')) { doesc(c); return; }
    if (inctrl >= 0 && c >= ' ') { doctrl(c); return; }
    if (c == 10 || c == 11 || c == 12) {
	if (nlmode) doindex('E'); else doindex('D');
	return;
	}
    if (c == 13) {
	if (!nlmode) emit(c);
	return;
	}
    if (c == 15) { alt = 0; return; }
    if (c == 14) { alt = 1; return; }
    if (a[alt] && c > 94 && c < 127) { doalt(c); return; }
    emit(c);
    }

void doesc(c)
char c;
{
    if (inesc < 0) { inesc = 0; return; }
    if (c == 27 || c == 24) { inesc = -1; return; }
    if (c < ' ' || c == 127) return;	  /* Ignore control chars */

    /* Collect intermediates */
    if (c < '0') {escseq[inesc++] = c; return; }

    /* by process of elimination, we have a final character.
       Put it in the buffer, and dispatch on the first character
       in the buffer */

    escseq[inesc] = c;
    inesc = -1;				/* No longer collecting a sequence */
    switch (escseq[0])			/* Dispatch on the first received */
    {
      case '[':				/* Control sequence introducer */
	numpar = 0;			/* No parameters yet */
	private = 0;			/* Not a private sequence (yet?) */
	badseq = 0;			/* Good until proven bad */
	p[0] = p[1] = 0;		/* But default the first parameter */
	inctrl = 0;			/* We are in a control sequence */
	return;				/* All done for now ... */

      case 'D': case 'E': case 'M':	/* Some kind of index */
	doindex (c);			/* Do the index */
	return;				/* Return */

      case '7':				/* Save cursor position */
	savx = x; savy = y; savmode = curmode; savalt = alt;
	sa[0] = a[0]; sa[1] = a[1];
	return;

      case '8':				/* Restore cursor position */
	x = savx; y = savy; alt = savalt; curmode = savmode;
	a[0] = sa[0]; a[1] = sa[1];
	return;

      case 'c':				/* Reset */
	top = MINY; bot = MAXY; savx = MINX; savy = MINY;
	curmode = FS_NORMAL; keyapp = FALSE; curapp = FALSE;
	inesc = -1;
	a[0] = 0; a[1] = 0; sa[0] = 0; sa[1] = 0;
	emit(12);
	return;

      case '(':				/* Change character set */
	if (c == '0' || c == '2') a[0] = 1; else a[0] = 0;
	return;

      case ')':				/* Change the other character set */
	if (c == '0' || c == '2') a[1] = 1; else a[1] = 0;
	return;

      case '=':				/* set keypad application mode */
        keyapp = TRUE;
        return;
        
      case '>':				/* reset application mode */
        keyapp = FALSE;
        return;
        
      case 'Z':
	sendchar(27); sendstring("[?1;7c"); return;

      /* If we didn't match anything, we can just return, happy in the
	 knowledge that we've at least eaten the whole sequence */

    }					/* End of switch */
    return;
}

void doctrl(c)
char c;
{
    int	    i;

    if (c == 27 || c == 24) { inctrl = -1; return; }
    if (c < ' ' || c == 127) return;		  /* Ignore control chars */

    /* First, look for some parameter characters.  If the very first
	parameter character isn't a digit, then we have a 
	private sequence */

    if (c >= '0' && c < '@')
    {
	/* can't have parameters after intermediates */
	if (inctrl > 0) {badseq++ ; return; }
	switch (c)
	{
	  case '0': case '1': case '2': case '3': case '4':
	  case '5': case '6': case '7': case '8': case '9':
	    p[numpar] = p[numpar] * 10 + (c - '0');
	    return;

	  case ';':
	    p[++numpar] = 0;		/* Start a new parameter */
	    return;

	  case '<': case '=': case '>': case '?': /* Can only mean private */

	    /* Only allowed BEFORE parameters */
    	    if (inctrl == 0) private = c;
	    return;

	/* if we come here, it's a bad sequence */
	}
	badseq++;			/* Flag the bad sequence */
    }

    if (c < '0')			/* Intermediate character */
    {
	escseq[inctrl++] = c;		/* Save the intermediate character */
	return;
    }

    /* if we get here, we have the final character.  Put it in the 
       escape sequence buffer, then dispatch the control sequence */

    numpar++;				/* Reflect the real number of parameters */
    escseq[inctrl++] = c;		/* Store the final character */
    escseq[inctrl] = '\000';		/* Tie off the buffer */
    inctrl = -1;			/* End of the control sequence scan */

    /* Don't know how to do most private sequences right now,
	so just punt them */

    if ((private != 0 && private != '?') || badseq != 0) return;
    if (private == '?' && escseq[0] != 'h' &&
	escseq[0] != 'l') return;

    switch (escseq[0])			/* Dispatch on first intermediate or final */
    {
      case 'A': if (p[0]<=0) p[0] = 1;
		y -= 8*p[0]; if (y<top)  y = top;  return;
      case 'B': if (p[0]<=0) p[0] = 1;
		y += 8*p[0]; if (y>bot)  y = bot;  return;
      case 'C': if (p[0]<=0) p[0] = 1;
		x += 8*p[0]; if (x>MAXX) x = MAXX; return;
      case 'D': if (p[0]<=0) p[0] = 1;  
		x -= 8*p[0]; if (x<MINX) x = MINX; return;

      case 'H': case 'f':		/* Cursor position */
	if (p[0] <= 0) p[0] = 1;
	if (p[1] <= 0) p[1] = 1;
	y = (--p[0]*8)+MINY; x = (--p[1]*8)+MINX;
	if (y > MAXY) y = MAXY;
	if (x > MAXX) x = MAXX;
	if (y < MINY) y = MINY;
	if (x < MINX) x = MINX;
	return;

      case 'L':				/* ANSI insert line */
      case 'M':				/* ANSI delete line */
	if (p[0] <= 0) p[0] = 1;
	ScrollRaster(mywindow->RPort,0L,
	    (long)((escseq[0] == 'M' ? 8L : -8L) * p[0]),
	    (long)MINX,(long)y-6,(long)(MAXX+7),(long)bot+1);
	return;

      case 'r':				/* Set scroll region */
	if (p[0] <= 0) p[0] = 1;
	if (p[1] <= 0) p[1] = p_lines;
	top = (--p[0]*8)+MINY; bot = (--p[1]*8)+MINY;
	if (top < MINY) top = MINY;
	if (bot > MAXY) bot = MAXY;
	if (top > bot) { top = MINY; bot = MAXY; }
	x = MINX; y = MINY;
	return;

      case 'm':				/* Set graphic rendition */
	for (i=0;i<numpar;i++) {
	    if (p[i] < 0) p[i] = 0;
	    switch (p[i]) {
		case 0:
		curmode  = FS_NORMAL;
		break;

		case 1:
		curmode |= FSF_BOLD;
		break;

		case 4:
		curmode |= FSF_UNDERLINED;
		break;

		case 5:
		curmode |= FSF_ITALIC;
		break;

		default:
		curmode |= FSF_REVERSE;
		break;
		}
	    }
	return;

      case 'K':				/* Erase in line */
	doerase();
	return;

      case 'J':				/* Erase in display */
	if (p[0] < 0) p[0] = 0;
	SetAPen(mywindow->RPort,0L);
	if (p[0] == 0) {
	    if (y < MAXY) RectFill(mywindow->RPort,
		(long)MINX,(long)(y+2),(long)(MAXX+7),(long)(MAXY+1));
	    }
	else if (p[0] == 1) {
	    if (y > MINY) RectFill(mywindow->RPort,
		(long)MINX,(long)(MINY-6),(long)(MAXX+7),(long)(y-7));
	    }
	else RectFill(mywindow->RPort,
	    (long)MINX,(long)(MINY-6),(long)(MAXX+7),(long)(MAXY+1));
	SetAPen(mywindow->RPort,1L);
	doerase(); return;

      case 'h':				/* Set parameter */
	if (private == 0 && p[0] == 20)		nlmode = 1;
	else if (private == '?') {
	    if      (p[0] == 7)	p_wrap = 1;
	    else if (p[0] == 1)	curapp = 1;
	    }
	return;

      case 'l':				/* Reset parameter */
	if (private == 0 && p[0] == 20)		nlmode = 0;
	else if (private == '?') {
	    if      (p[0] == 7)	p_wrap = 0;
	    else if (p[0] == 1) curapp = 0;
	    }
	return;

      case 'x':
	sendchar(27); sendstring("[3;1;8;64;64;1;0x"); return;

      case 'n':
	if (p[0] == 6) {
	    sendchar(27);
	    sprintf(escseq,"[%d;%dR",((y-MINY)/8)+1,((x-MINX)/8)+1);
	    sendstring(escseq); return;
	    }
	sendchar(27); sendstring("[0n"); return;

      case 'c':
	sendchar(27); sendstring("[?1;7c"); return;
    }

    /* Don't know how to do this one, so punt it */
}

void doindex(c)
char c;
    {
    if (c != 'M') {
	if (c == 'E') x = MINX;
	if (y > bot) if (y < MAXY) y += 8;
	if (y == bot)
	    ScrollRaster(mywindow->RPort,0L,8L,(long)MINX,(long)(top-6),
		(long)(MAXX+7),(long)(bot+1));
	if (y < bot) y += 8;
	}
    else {
	if (y < top) if (y > MINY) y -= 8;
	if (y == top)
	    ScrollRaster(mywindow->RPort,0L,-8L,(long)MINX,(long)(top-6),
		(long)(MAXX+7),(long)(bot+1));
	if (y > top) y -= 8;
	}
    return;
    }

doalt(c)
char c;
    {
    int oldx,newx;
    inesc = -1;
    oldx = x; emit(' '); newx = x;
    x = oldx;
    SetAPen(mywindow->RPort,1L);
    switch (c) {
	case 'a':
	doline(0,-6,8,1);
	break;

	case 'j':
	case 'm':
	case 'v':   doline(4,-6,4,-2);
	if      (c=='j')  doline(0,-2,4,-2);
	else if (c=='m')  doline(4,-2,8,-2);
	else              doline(0,-2,8,-2);
	break;

	case 'k':
	case 'l':
	case 'w': doline(4,-2,4,1);
	if      (c=='k')  doline(0,-2,4,-2);
	else if (c=='l')  doline(4,-2,8,-2);
	else              doline(0,-2,8,-2);
	break;

	case 'n':
	case 'q': doline(0,-2,8,-2);
	if      (c=='n')  doline(4,-6,4,2);
	break;

	case 't':
	case 'u':
	case 'x':   doline(4,-6,4,1);
	if      (c=='t')  doline(4,-2,8,-2);
	else if (c=='u')  doline(0,-2,4,-2);
	break;
	}
    x = newx;
    }

doline(x1,y1,x2,y2) {
    RectFill(mywindow->RPort,(long)(x+x1),(long)(y+y1),
	(long)(x+x2),(long)(y+y2));
    }

void doerase()
    {
    if (p[0] < 0) p[0] = 0;
    SetAPen(mywindow->RPort,0L);
    if (p[0] == 0) RectFill(mywindow->RPort,(long)x,(long)(y-6),
	(long)(MAXX+7),(long)(y+1));
    else if (p[0] == 1) RectFill(mywindow->RPort,
	(long)MINX,(long)(y-6),(long)(x+7),(long)(y+1));
    else RectFill(mywindow->RPort,
	(long)MINX,(long)(y-6),(long)(MAXX+7),(long)(y+1));
    SetAPen(mywindow->RPort,1L);
    return;
    }

