/* emul.c */

#include "windows.h"
#include "screen.h"


static int ScreenEmChars(
	SCREEN *pScr,
	char *c,
	int len)
{
	/*
	Function: Send a string of characters to the screen.  Placement
	continues as long as the stream of characters does not contain any
	control chracters or cause wrapping to another line.  When a control
	character is encountered or wrapping occurs, display stops and a
	count of the number of characters is returned.
	
	Parameters:
		pScr - the screen to place the characters on.

		c - the string of characters to place on the screen.

		len - the number of characters contained in the string

	Returns: The number of characters actually placed on the screen.
	*/

	int insert;
	int ocount;
	int attrib;
	int extra;
	int nchars;
	char *acurrent;         			/* place to put attributes */
	char *current;          			/* place to put characters */
	char *start;
	SCREENLINE *pScrLine;

	if (len <= 0)
		return(0);

	if (pScr->x != pScr->width - 1)
		pScr->bWrapPending = FALSE;
	else {
		if (pScr->bWrapPending) {
			pScr->x = 0;
			pScr->bWrapPending = FALSE;
			ScreenIndex(pScr);
		}
	}

	pScrLine = GetScreenLineFromY(pScr, pScr->y);
	if (pScrLine == NULL)
		return(0);
	
	current = &pScrLine->text[pScr->x];
	acurrent = &pScrLine->attrib[pScr->x];
	start = current;
	ocount = pScr->x;
	extra = 0;

	attrib = pScr->attrib;
	insert = pScr->IRM;

	for (nchars = 0; nchars < len && *c >= 32; nchars++) {
		if (insert)
			ScreenInsChar(pScr, 1);

		*current = *c;
		*acurrent = (char) attrib;
		c++;
		if (pScr->x < pScr->width - 1) {
			acurrent++;
			current++;
			pScr->x++;
		}
		else {
			extra = 1;
			if (pScr->DECAWM) {
				pScr->bWrapPending = TRUE;
				nchars++;
				break;
			}
		}
	}

	ScreenDraw(pScr, ocount, pScr->y, pScr->attrib, pScr->x - ocount + extra, start);
	
	return(nchars);

} /* ScreenEmChars */


void ScreenEm(
	LPSTR c,
	int len,
	SCREEN *pScr)
{
	int escflg;							/* vt100 escape level */
	RECT rc;
	unsigned int ic;
	char stat[20];
	int i;
	int nchars;
	
	if (pScr->screen_bottom != pScr->buffer_bottom) {
		ScreenUnscroll(pScr);
		InvalidateRect(pScr->hWnd, NULL, TRUE);
		SetScrollPos(pScr->hWnd, SB_VERT, pScr->numlines, TRUE);
	}

	ScreenCursorOff(pScr);
	escflg = pScr->escflg;

	#ifdef UM
		if (pScr->localprint && len > 0) {	/* see if printer needs anything */
			pcount = send_localprint(c, len);
			len -= pcount;
			c += pcount;
		}
	#endif

	while (len > 0) {
		while((*c < 32) && (escflg == 0) && (len > 0)) {  /* look at first character in the vt100 string, if it is a non-printable ascii code */
			switch(*c) {
			
			case 0x1b:					/* ESC found (begin vt100 control sequence) */
				escflg++;
				break;

			case -1:					/* IAC from telnet session */
				escflg = 6;
				break;

			#ifdef CISB
				case 0x05:				/* CTRL-E found (answerback) */
					bp_ENQ();
					break;
			#endif

			case 0x07:					/* CTRL-G found (bell) */
				ScreenBell(pScr);
				break;

			case 0x08:					/* CTRL-H found (backspace) */
				ScreenBackspace(pScr);
				break;

			case 0x09:					/* CTRL-I found (tab) */
				ScreenTab(pScr);		/* Later change for versatile tabbing */
				break;

			case 0x0a:					/* CTRL-J found (line feed) */
			case 0x0b:					/* CTRL-K found (treat as line feed) */
			case 0x0c:					/* CTRL-L found (treat as line feed) */
				ScreenIndex(pScr);
				break;

			case 0x0d:					/* CTRL-M found (carriage feed) */
				ScreenCarriageFeed(pScr);
				break;

			#if 0
				case 0x0e:      		/* CTRL-N found (invoke Graphics (G1) character set) */
					if (pScr->G1)
						pScr->attrib = VSgraph(pScr->attrib);
					else
						pScr->attrib = VSnotgraph(pScr->attrib);
					pScr->charset = 1;
					break;

				case 0x0f:      		/* CTRL-O found (invoke 'normal' (G0) character set) */
					if(pScr->G0)
						pScr->attrib = VSgraph(pScr->attrib);
					else
						pScr->attrib = VSnotgraph(pScr->attrib);
					pScr->charset = 0;
					break;
			#endif

			#ifdef CISB
				case 0x10:      		/* CTRL-P found (undocumented in vt100) */
					bp_DLE(c, len);
					len = 0;
					break;
			#endif

			#if 0
				case 0x11:      		/* CTRL-Q found (XON) (unused presently) */
				case 0x13:      		/* CTRL-S found (XOFF) (unused presently) */
				case 0x18:      		/* CTRL-X found (CAN) (unused presently) */
				case 0x1a:      		/* CTRL-Z found (SUB) (unused presently) */
					break;
			#endif

			} /* end switch */

			c++;        				/* advance to the next character in the string */
			len--;      				/* decrement the counter */

		} /* end while */

		if (escflg == 0) {				/* check for normal character to print */
			nchars = ScreenEmChars(pScr, c, len);
			c += nchars;
			len -= nchars;
		}

		while ((len > 0) && (escflg == 1)) {	/* ESC character was found */
			switch(*c) {
			
			case 0x08:      			/* CTRL-H found (backspace) */
				ScreenBackspace(pScr);
				break;

			case '[':               	/* mostly cursor movement options, and DEC private stuff following */
				ScreenApClear(pScr);
				escflg = 2;
				break;

			case '#':               	/* various screen adjustments */
				escflg = 3;
				break;

			case '(':               	/* G0 character set options */
				escflg = 4;
				break;

			case ')':               	/* G1 character set options */
				escflg = 5;
				break;

			case '>':               	/* keypad numeric mode (DECKPAM) */
				pScr->DECPAM = 0;
				escflg = 0;
				break;

			case '=':               	/* keypad application mode (DECKPAM) */
				pScr->DECPAM = 1;
				escflg = 0;
				break;

			case '7':               	/* save cursor (DECSC) */
				ScreenSaveCursor(pScr);
				escflg = 0;
				break;

			case '8':               	/* restore cursor (DECRC) */
				ScreenRestoreCursor(pScr);
				escflg = 0;
				break;

			#if 0
				case 'c':				/* reset to initial state (RIS) */
					ScreenReset(pScr);
					escflg = 0;
					break;
			#endif

			case 'D':					/* index (move down one line) (IND) */
				ScreenIndex(pScr);
				escflg = 0;
				break;

			case 'E':					/* next line (move down one line and to first column) (NEL) */
				pScr->x = 0;
				ScreenIndex(pScr);
				escflg = 0;
				break;

			case 'H':					/* horizontal tab set (HTS) */
				pScr->tabs[pScr->x] = 'x';
				escflg = 0;
				break;

			#ifdef CISB
				case 'I':				/* undoumented in vt100 */
					bp_ESC_I();
					break;
			#endif

			case 'M':					/* reverse index (move up one line) (RI) */
				ScreenRevIndex(pScr);
				escflg = 0;
				break;

			case 'Z':					/* identify terminal (DECID) */
				escflg = 0;
				break;

			default:
				ScreenEmChars(pScr, "\033", 1); /* put the ESC character into the Screen */
				ScreenEmChars(pScr, c, 1);      /* put the next character into the Screen */
				escflg = 0;
				break;

			} /* end switch */

			c++;
			len--;
		}

		while((escflg == 2) && (len > 0)) {     /* '[' handling */            
			switch(*c) {

			case 0x08:					/* backspace */
				ScreenBackspace(pScr);
				break;

			case '0':
			case '1':
			case '2':
			case '3':
			case '4':
			case '5':
			case '6':
			case '7':
			case '8':
			case '9':					/* numeric parameters */
				if (pScr->parms[pScr->parmptr] < 0)
					pScr->parms[pScr->parmptr] = 0;
				pScr->parms[pScr->parmptr] *= 10;
				pScr->parms[pScr->parmptr] += *c - '0';
				break;

			case '?':					/* vt100 mode change */
				pScr->parms[pScr->parmptr++] = -2;
				break;

			case ';':					/* parameter divider */
				pScr->parmptr++;
				break;

			case 'A':					/* cursor up (CUU) */
				pScr->bWrapPending = FALSE;
				rc.left = pScr->x * pScr->cxChar;
				rc.right = (pScr->x + 1) * pScr->cxChar;
				rc.top = pScr->cyChar * pScr->y;
				rc.bottom = pScr->cyChar * (pScr->y + 1);
				InvalidateRect(pScr->hWnd, &rc, TRUE);
				if (pScr->parms[0] < 1)
					pScr->y--;
				else
					pScr->y -= pScr->parms[0];
				if(pScr->y < pScr->top)
					pScr->y = pScr->top;
				ScreenRange(pScr);
				escflg = 0;
				SendMessage(pScr->hWnd, WM_PAINT, NULL, NULL);
				break;

			case 'B':					/* cursor down (CUD) */
				pScr->bWrapPending = FALSE;
				rc.left = pScr->x * pScr->cxChar;
				rc.right = (pScr->x + 1) * pScr->cxChar;
				rc.top = pScr->cyChar * pScr->y;
				rc.bottom = pScr->cyChar * (pScr->y + 1);
				InvalidateRect(pScr->hWnd, &rc, TRUE);
				if (pScr->parms[0] < 1)
					pScr->y++;
				else
					pScr->y += pScr->parms[0];
				if (pScr->y > pScr->bottom)
					pScr->y = pScr->bottom;
				ScreenRange(pScr);
				escflg = 0;
				SendMessage(pScr->hWnd, WM_PAINT, NULL, NULL);
				break;

			case 'C':					/* cursor forward (right) (CUF) */
				pScr->bWrapPending = FALSE;
				rc.left = pScr->x * pScr->cxChar;
				rc.right = (pScr->x + 1) * pScr->cxChar;
				rc.top = pScr->cyChar * pScr->y;
				rc.bottom = pScr->cyChar * (pScr->y +1);
				InvalidateRect(pScr->hWnd, &rc, TRUE);
				if(pScr->parms[0] < 1)
					pScr->x++;
				else
					pScr->x += pScr->parms[0];
				ScreenRange(pScr);
				if (pScr->x > pScr->width)
					pScr->x = pScr->width;
				escflg = 0;
				SendMessage(pScr->hWnd, WM_PAINT, NULL, NULL);
				break;

			case 'D':					/* cursor backward (left) (CUB) */
				pScr->bWrapPending = FALSE;
				rc.left = pScr->x * pScr->cxChar;
				rc.right = (pScr->x + 1) * pScr->cxChar;
				rc.top = pScr->cyChar * pScr->y;
				rc.bottom = pScr->cyChar * (pScr->y + 1);
				InvalidateRect(pScr->hWnd, &rc, TRUE);
				if(pScr->parms[0] < 1)
					pScr->x--;
				else
					pScr->x -= pScr->parms[0];
				ScreenRange(pScr);
				escflg = 0;
				SendMessage(pScr->hWnd, WM_PAINT, NULL, NULL);
				break;

			case 'f':					/* horizontal & vertical position (HVP) */
			case 'H':					/* cursor position (CUP) */
				pScr->bWrapPending = FALSE;
				rc.left = pScr->x * pScr->cxChar;
				rc.right = (pScr->x + 1) * pScr->cxChar;
				rc.top = pScr->cyChar * pScr->y;
				rc.bottom = pScr->cyChar * (pScr->y + 1);
				InvalidateRect(pScr->hWnd, &rc, TRUE);
				pScr->x = pScr->parms[1] - 1;
				pScr->y = pScr->parms[0] - 1;
				ScreenRange(pScr);		/* make certain the cursor position is valid */
				escflg = 0;
				SendMessage(pScr->hWnd, WM_PAINT, NULL, NULL);
				break;

			case 'J':					/* erase in display (ED) */
				switch(pScr->parms[0]) {

				case -1:
				case 0:					/* erase from active position to end of screen */
					ScreenEraseToEndOfScreen(pScr);
					break;
				case 1:					/* erase from start of screen to active position */
					#if 0
						ScreenEraseToPosition(pScr);
					#endif
					break;

				case 2:					/* erase whole screen */
					ScreenEraseScreen(pScr);
					break;

				default:
					break;
				}

				escflg = 0;
				break;

			case 'K':					/* erase in line (EL) */
				switch(pScr->parms[0]) {
				case -1:
				case 0:					/* erase to end of line */
					ScreenEraseToEOL(pScr);
					break;

				case 1:					/* erase to beginning of line */
					ScreenEraseToBOL(pScr);
					break;

				case 2:					/* erase whole line */
					ScreenEraseLine(pScr, -1);
					break;

				default:
					break;
				}

				escflg = 0;
				break;

			case 'L':					/* insert n lines preceding current line (IL) */
				if (pScr->parms[0] < 1)
					pScr->parms[0] = 1;
				ScreenInsLines(pScr, pScr->parms[0], -1);
				escflg = 0;
				break;

			case 'M':					/* delete n lines from current position downward (DL) */
				if (pScr->parms[0] < 1)
					pScr->parms[0] = 1;
				ScreenDelLines(pScr, pScr->parms[0], -1);
				escflg = 0;
				break;

			case 'P':					/* delete n chars from cursor to the left (DCH) */
				if (pScr->parms[0] < 1)
					pScr->parms[0] = 1;
				ScreenDelChars(pScr, pScr->parms[0]);
				escflg = 0;
				break;

			#if 0
				case 'R':				/* receive cursor position status from host */
					break;
			#endif

			#if 0
				case 'c':				/* device attributes (DA) */
					ScreenSendIdent();
					escflg = 0;
					break;
			#endif

			case 'g':               	/* tabulation clear (TBC) */
				if (pScr->parms[0] == 3)/* clear all tabs */
					ScreenTabClear(pScr);
				else
					if (pScr->parms[0] <= 0)	/* clear tab stop at active position */
						pScr->tabs[pScr->x] = ' ';
				escflg = 0;
				break;

			case 'h':               	/* set mode (SM) */
				ScreenSetOption(pScr,1);
				escflg = 0;
				break;

			case 'i':               	/* toggle printer */
				#if 0
					if(pScr->parms[pScr->parmptr] == 5)
    					pScr->localprint = 1;
					else if (pScr->parms[pScr->parmptr] == 4)
    					pScr->localprint = 0;
				#endif
				escflg = 0;
				break;

			case 'l':					/* reset mode (RM) */
				ScreenSetOption(pScr,0);
				escflg = 0;
				break;

			case 'm':					/* select graphics rendition (SGR) */
				{
				int temp = 0;

				while (temp <= pScr->parmptr) {
					if (pScr->parms[temp] < 1)
						pScr->attrib &= 128;
					else
						pScr->attrib |= 1 << (pScr->parms[temp] - 1);
					temp++;
					}
				}
				escflg = 0;
				break;

			case 'n':               	/* device status report (DSR) */
				switch (pScr->parms[0]) {
				#if 0
					case 0: 			/* response from vt100; ready, no malfunctions */
					case 3: 			/* response from vt100; malfunction, retry */
				#endif
				case 5: 				/* send status */
				case 6: 				/* send active position */
					wsprintf(stat, "\033[%d;%dR", pScr->y + 1, pScr->x + 1);
					for (i = 0; stat[i]; i++)
						SendMessage(pScr->hwndTel, WM_MYSCREENCHAR, stat[i], (LPARAM) pScr);
					break;
				} /* end switch */
				escflg = 0;
				break;

			case 'q':					/* load LEDs (unsupported) (DECLL) */
				escflg = 0;
				break;

			case 'r':					/* set top & bottom margins (DECSTBM) */
				if (pScr->parms[0] < 0)
					pScr->top = 0;
				else
					pScr->top = pScr->parms[0] - 1;
				if (pScr->parms[1] < 0)
					pScr->bottom = pScr->height - 1;
				else
					pScr->bottom = pScr->parms[1] - 1;
				if (pScr->top < 0)
					pScr->top = 0;
				if (pScr->top > pScr->height-1)
					pScr->top = pScr->height-1;
				if (pScr->bottom < 1)
					pScr->bottom = pScr->height;
				if (pScr->bottom >= pScr->height)
					pScr->bottom = pScr->height - 1;
				if (pScr->top >= pScr->bottom) {/* check for valid scrolling region */
					if (pScr->bottom >= 1)     	/* assume the bottom value has precedence, unless it is as the top of the screen */
						pScr->top = pScr->bottom - 1;
					else                /* totally psychotic case, bottom of screen set to the very top line, move the bottom to below the top */
						pScr->bottom = pScr->top + 1;
				}
				pScr->x = 0;
				pScr->y = 0;
				#if 0
					if (pScr->DECORG)
						pScr->y = pScr->top;	/* origin mode relative */
				#endif
				escflg = 0;
				break;

			#if 0 
				case 'x':				/* request/report terminal parameters (DECREQTPARM/DECREPTPARM) */
				case 'y':				/* invoke confidence test (DECTST) */
					break;
			#endif

			default:
				escflg = 0;
				break;

			}

			c++;
			len--;

			#if 0
				if (pScr->localprint && (len > 0)) {    /* see if printer needs anything */
					pcount = send_localprint(c, len);
					len -= pcount;
					c += pcount;
				}
			#endif
		}

		while ((escflg == 3) && (len > 0)) { /* #  Handling */
			switch (*c) {
			case 0x08:      			/* backspace */
				ScreenBackspace(pScr);
				break;

			#if 0
				case '3':				/* top half of double line (DECDHL) */
				case '4':				/* bottom half of double line (DECDHL) */
				case '5':				/* single width line (DECSWL) */
				case '6':				/* double width line (DECDWL) */
					break;
			#endif

			case '8':               	/* screen alignment display (DECALN) */
				ScreenAlign(pScr);
				escflg = 0;
				break;

			default:
				escflg = 0;
				break;

			}

			c++;
			len--;
		}

		while ((escflg == 4) && (len > 0)) { /* ( Handling (GO character set) */
			switch (*c) {

			case 0x08:      			/* backspace */
				ScreenBackspace(pScr);
				break;

			#if 0
				case 'A':               /* united kingdom character set (unsupported) */
				case 'B':               /* ASCII character set */
				case '1':               /* choose standard graphics (same as ASCII) */
					pScr->G0 = 0;
					if (!pScr->charset)
						pScr->attrib = ScreenNotGraph(pScr->attrib);
					escflg = 0;
					break;

				case '0':               /* choose special graphics set */
				case '2':               /* alternate character set (special graphics) */
					pScr->G0 = 1;
					if(!pScr->charset)
						pScr->attrib = ScreenGraph(pScr->attrib);
					escflg = 0;
					break;
			#endif

			default:
				escflg = 0;
				break;
			}

			c++;
			len--;

		} /* end while */

		while((escflg == 5) && (len > 0)) { /* ) Handling (G1 handling) */
			switch (*c) {

			case 0x08:					/* backspace */
				ScreenBackspace(pScr);
				break;

			#if 0
				case 'A':               /* united kingdom character set (unsupported) */
				case 'B':               /* ASCII character set */
				case '1':               /* choose standard graphics (same as ASCII) */
					pScr->G1 = 0;
					if (pScr->charset)
						pScr->attrib = ScreenNotGraph(pScr->attrib);
					escflg = 0;
					break;

				case '0':               /* choose special graphics set */
				case '2':               /* alternate character set (special graphics) */
					pScr->G1 = 1;
					if(pScr->charset)
						pScr->attrib = ScreenGraph(pScr->attrib);
					escflg = 0;
					break;
			#endif

			default:
				escflg = 0;
				break;
			} /* end switch */

			c++;
			len--;
		} /* end while */

		while ((escflg >= 6) && (escflg <= 10) && (len > 0)) { /* Handling IAC */
			ic = (unsigned char) *c;
			switch (escflg) {

			case 6:						/* Handling IAC xx */
				if (ic == 255) 			/* if IAC */
					escflg = 0;
				else if (ic == 250)		/* if SB */
					escflg = 7;
				else
					escflg = 9;
				break;

			case 7:						/* Handling IAC SB xx */
				if (ic == 255)			/* if IAC */
					escflg = 8;
				break;

			case 8:						/* Handling IAC SB IAC xx */
				if (ic == 255)			/* if IAC IAC */
				   escflg = 7;
				else if (ic == 240)		/* if IAC SE */
				   escflg = 0;
				break;

			case 9:						/* IAC xx xx */
				escflg = 0;
				break;
			}
			c++;						/* advance to the next character in the string */
			len--;						/* decrement the counter */
		}

		if (escflg > 2 && escflg < 6 && len > 0) {
			escflg = 0;
			c++;
			len--;
		}
	}
	pScr->escflg = escflg;
	ScreenCursorOn(pScr);

} /* ScreenEm */
