/************************************************************************\
 * File Version Information
 * $Header: /Altair32v2/wincon_io.c 29    11/08/06 9:21p Racini $
 ************************************************************************/
/************************************************************************\
  MITS Altair Emulator
  Windows Console support
 
  Copyright (c) 2002-2006 Richard A. Cini
  Copyright (c) 2003 Joseph Forgione (VT100 Support)
  Copyright (c) 2002 Anthony J. Wood (base WinCon code; awsoftware.org)

  The console I/O interface is intended to enable the emulator to use
  the built-in "console window" facilities of Windows as the console
  terminal. This is accomplished by this set of routines along with
  the port emulation routines in sio.c. These routines do	polled I/O
  to a Windows console window. The WinCon code supports ANSI color and
  the VT100 terminal control set. Admittedly, a color VT100 terminal is
  less than prototypical based on the time frames each were available
  in the market. VT100 support was initiatiated after coming across
  a few CP/M games that used cursor control codes to place a status line
  on the screen.


Change Log:
  2002/08/26  AJW -- Initial coding
  2002/09/14  RAC -- Modifications to co-exist with tcp_io
  2002/10/04  RAC -- Final conversion to swappable function pointers
  2002/10/31  RAC -- Added handler to trap console exit messages to prevent
  						uncontrolled exit from emulator.
  2002/11/15  RAC -- RELEASE MARKER -- v2.40.2100
  2003/02/26  JJF -- Began adding VT100 Support.
  2003/03/09  JJF -- Modified deocode framework for ANSI color
  2003/04/26  RAC -- RELEASE MARKER -- v2.50.2045
  2004/01/20  RAC -- Diff'ed changes from FJS (Fred J. Scipione)
  2004/03/09  RAC -- Diff'ed more changes from FJS (Fred J. Scipione)
  2004/07/30  RAC -- RELEASE MARKER -- v3.00.0135
  2006/05/12  RAC -- RELEASE MARKER -- v3.10.0200
  2006/11/15  RAC -- RELEASE MARKER -- v3.20.0400
\**************************************************************************/
#include <windows.h>			// includes wincon.h
#include <commctrl.h> 
#include <stdio.h>
#include "altair32.h"		
#include "comcthlp.h"


/**  Defines  *************************************************/
#define ESC			27
#define LF			10
#define CR			13
#define Console_W	80
#define Console_H	25 // Minimum 25 on Windows 2000
// #define Text_Color	2;
// #define Back_Color	0;

//VT 100 Single Character Functions
//---------------------------------
#define STS		'S'
#define	SSA		'F'
#define	VTS		'J'
#define	CCH		'T'
#define	DCS		'P'
#define	EMI		'b'
#define	EPA		'W'
#define	ESA		'G'
#define	HTJ		'I'
#define	HTS		'H'
#define	IND		'D'
#ifdef INT
#undef INT
#endif
#define	INT		'a'
#define	MW		'U'
#define	NEL		'E'
#define	OSC		']'
#define	PLD		'K'
#define	PLU		'L'
#define	PM		'^'
#define	PU1		'Q' //Private Use 1 (Time)
#define	PU2		'R' //Private Use 2 (Date)
#define	RI		'M'
#define	RIS		'c'
#define	SPA		'V'
#define	SS2		'N'
#define	SS3		'O'
#define ST		'\\'
#define EDA		'J'

//VT 100 Single Parameter Functions
//---------------------------------
#define CBT		'Z'
#define CHA		'G'
#define CHT		'I'
#define CNL		'E'
#define CPL		'F'
#define CTC		'W'
#define CUB		'D'
#define CUD		'B'
#define CUF		'C'
#define CUU		'A'
#define CVT		'Y'
#define DA		'c'
#define DAQ		'o'
#define DCH		'P'
#define DL		'M'
#define DSR		'n'
#define EA		'O'
#define ECH		'X'
#define ED		'J'
#define EF		'N'
#define EL		'K'
#define HPA		'`'
#define HPR		'a'
#define ICH		'@'
#define IL		'L'
#define MC		'i'
#define NP		'U'
#define PP		'V'
#define REP		'b'
#define RM		'l'
#define SD		'T'
#define SEM		'Q'
#define SGR		'm'
#define SM		'h'
#define SU		'S'
#define TBC		'g'
#define VPA		'd'
#define VPR		'e'

//VT 100 Double Parameter Functions
//---------------------------------
#define HVP		'f'
#define CPR		'R'
#define CUP		'H'


/**  Declarations  ********************************************/
static BOOL Invert = 0, Underline = 0, /* Blink=0, */ Bold=0, Dim = 0;
static char vt_command[256], report_string[128];
static int VT_index = 0, VTparam[10];
static short unsigned int text_attrib = 0, next_ready = 0, did_read = 0;
static short is_open = FALSE;
static short int vt_flag = 0;
static unsigned short int  Foreground = 2; // FJS Green
static unsigned short int  Background = 0; // FJS Black
static SMALL_RECT source_block;
static COORD destination_block;
static CONSOLE_SCREEN_BUFFER_INFO csbi; 
static short int G0_pointer = 4, G1_pointer = 0;
static BOOL G1_Shift=FALSE;

/**  Forward Prototypes  **************************************/
static BOOL WINAPI blpConCloseHandler( DWORD );
static int  IoWinConEnsureOpen(void);
static char IoWinConInkey(void);
static void IoWinConPutc(char c);
static char IoWinConPeek(void);
static void IoWinConClose(void);
static void In_vtDecode(char,HANDLE);
static void cls_block( HANDLE ,int ,int ,int ,int ,BOOL );
static void Move_Cursor( HANDLE ,int ,int,BOOL,BOOL );
static int GetCursor_Pos(HANDLE, BOOL);
static void Move_Text_Block(HANDLE ,SMALL_RECT,COORD );
#if 0
static int BitInvert(int);
#else
#define BitInvert(x) Rev3Bits[(x)&7]
static int Rev3Bits[8] = { // FJS
	// 000 001 010 011 100 101 110 111
	// 000 100 010 110 001 101 011 111
	    0,  4,  2,  6,  1,  5,  3,  7
};
#endif
static unsigned Set_attrib(void);	// FJS
static void Do_ANSI(HANDLE,int);
static void unimplemented (int);
static unsigned char Alt_set(char);

/**************************************************************\
****  Exported Functions  *************************************|
\**************************************************************/

/**************************************************************\
*
*  FUNCTION:    WINCON_InStatus
*
*  INPUTS:      None
*
*  RETURNS:     0 if no data is ready, 1 if data is available
*
*  COMMENTS:	
*
\**************************************************************/
int WINCON_InStatus(void)
{
	if (next_ready != 0){		//This mess is to keep the data
		if(did_read !=0 ){		//from comming in too fast.
			did_read=did_read-1;
			return(0);
		}
		return(next_ready);
	}
	
	if (!IoWinConEnsureOpen())
		return 0;

	return (int)(IoWinConPeek()!=0);
}


/**************************************************************\
*
*  FUNCTION:    WINCON_Read
*
*  INPUTS:      None
*
*  RETURNS:     Character (>= 0) or 0 if no data has been read
*
*  COMMENTS:	In order to simulate the way most serial ports
*				work on the Altair, if no data is ready, this
*				routine will return the last character in the
*				buffer, or zero if no characters have ever been
*				put in the buffer.
*
\**************************************************************/
int WINCON_Read(void)
{
	char c;
	static int last_read = 0; 
	short unsigned int index=0;
	short int lenth=0;

	
	lenth=strlen(report_string);

	if (lenth != 0){
		last_read=report_string[0]; 
		for (index=0;index<=lenth;index++)
			report_string[index]=report_string[index+1];
		if (report_string[0]==0){
			did_read=0;
			next_ready=0;
			return(last_read);
		}
		did_read=4;			//Virtual Delay for VT100 report functions.
		return(last_read);	//Value determined experimentally may need changing.
	}
	
	next_ready=0;

	if (!IoWinConEnsureOpen())
		return 0;

	c = IoWinConInkey();

	if (c!=0)
		last_read = c;
		
	return (int)last_read;
}


/**************************************************************\
*
*  FUNCTION:    WINCON_OutStatus
*
*  INPUTS:      None
*
*  RETURNS:     1 if data can be written (i.e., buffer isn't full), else 0
*
*  COMMENTS:	
*
\**************************************************************/
int WINCON_OutStatus(void)
{
	return IoWinConEnsureOpen();
}


/**************************************************************\
*
*  FUNCTION:    WINCON_Write
*
*  INPUTS:      Character to write
*
*  RETURNS:     Nothing
*
*  COMMENTS:	Errors are effectively ignored
*
\**************************************************************/
void WINCON_Write(UCHAR cData)
{
	if (!IoWinConEnsureOpen())
		return;
	IoWinConPutc((char)(cData&0x7f));	// Strip parity

}


/**************************************************************\
*
*  FUNCTION:    WINCON_Shutdown - Shutdown connection
*
*  INPUTS:      None
*
*  RETURNS:     Continue flag - TRUE if caller should continue in state machine
*
*  COMMENTS:	There is no error reporting to help troubleshoot problems
*
\**************************************************************/
BOOL WINCON_Shutdown(void)
{
	IoWinConClose();
	is_open = FALSE;
	return TRUE;
}


/**************************************************************\
****  Internal Subroutines  ***********************************|
\**************************************************************/
static void IoWinConClose(void)
{
	// TRUE=add; FALSE=delete
	SetConsoleCtrlHandler((PHANDLER_ROUTINE) blpConCloseHandler, FALSE);
	FreeConsole();
}


static char IoWinConPeek(void)
{
	DWORD numevents;
	HANDLE hin;
	INPUT_RECORD record;
	unsigned long dummy;
	hin=GetStdHandle(STD_INPUT_HANDLE);
	if (INVALID_HANDLE_VALUE!=hin)
		if(GetNumberOfConsoleInputEvents(hin, &numevents))
			while (numevents>0)
			{
				if (PeekConsoleInput(hin, &record, 1, &dummy))
					if (record.EventType==KEY_EVENT)
						if (record.Event.KeyEvent.bKeyDown)
							if (record.Event.KeyEvent.uChar.AsciiChar!=0)
								return record.Event.KeyEvent.uChar.AsciiChar;
				ReadConsoleInput(hin, &record, 1, &dummy);	// Discard non-key events from buffer
				GetNumberOfConsoleInputEvents(hin, &numevents);
			}
	return 0;
}


static char IoWinConInkey(void)
{

	DWORD numevents;
	HANDLE hin;
	INPUT_RECORD record;
	unsigned long dummy;

	hin=GetStdHandle(STD_INPUT_HANDLE);
	if (INVALID_HANDLE_VALUE!=hin)
		if(GetNumberOfConsoleInputEvents(hin, &numevents))
			while (numevents>0)
			{
				if (ReadConsoleInput(hin, &record, 1, &dummy))
					if (record.EventType==KEY_EVENT)
						if (record.Event.KeyEvent.bKeyDown)
							return record.Event.KeyEvent.uChar.AsciiChar;
				GetNumberOfConsoleInputEvents(hin, &numevents);
			}
	return 0;
}


#if 0	//FJS
char IoWinConGetC(void)
{
	HANDLE hin;
	INPUT_RECORD record;
	unsigned long dummy;

	hin=GetStdHandle(STD_INPUT_HANDLE);
	if (INVALID_HANDLE_VALUE!=hin)
		while (TRUE)
		{
			if (ReadConsoleInput(hin, &record, 1, &dummy))
				if (record.EventType==KEY_EVENT)
					if (record.Event.KeyEvent.bKeyDown)
						return record.Event.KeyEvent.uChar.AsciiChar;
		}

	return 0x1b;	// ESC
}
#endif


static void IoWinConPutc(char c)
{
	BOOL Set_Back=FALSE;
	HANDLE hout;
	int Temp_Y = 0, Temp_X= 0; // change
	unsigned long dummy;


	hout = GetStdHandle(STD_OUTPUT_HANDLE);
	if (INVALID_HANDLE_VALUE != hout) {
		if (c == ESC) {			// Don't Print, it's an Escape Seq.
			VTparam[0] = 0;		// [0] holds parameter count.
			vt_flag = 1;
			return;
		}

		if (c == 0) return; // FJS <nul> char always ignored

		if (c < 32) vt_flag = 0; // FJS ctl chars kill any escapes

		if (c == 5) {	// report terminal type
			sprintf(report_string, "Altair32");
			next_ready=1;
			did_read=4;
			return;
		}

		if (c == 14) {	// Select G1 Character set
			G1_Shift = 1 /* YES */;
			return;
		}

		if (c == 15) {	// Select G0 Character set
			G1_Shift = 0 /* NO */;
			return;
		}

		
		if (vt_flag) {	// Don't Print, it's an Escape Seq.
			In_vtDecode(c, hout);
			return;
		}

		if ((c == 7) && (!Bell)) return;	// Drop unwanted BEL

		Temp_X=GetCursor_Pos(hout,TRUE); 
		Temp_Y=GetCursor_Pos(hout,FALSE);
		
		// We have to do this mess cause windows 95/98/Me won't shut off Word Wrap.
		Set_Back = (WordWrap == 0) && (Temp_X == Console_W-1) && (c != CR); // FJS

		if ((c == LF) && (Temp_Y == Console_H-1)) // FJS
		// if  (c==LF)
		//   if (GetCursor_Pos(hout,FALSE) == Console_H-1) // sb Temp_Y ??
				SetConsoleTextAttribute(hout,(unsigned short)(Background*16));

		if (c > 32 /* ASCII!!! */) c = Alt_set(c); // FJS U.K. Pound symbol too

		WriteConsole(hout, &c, 1, &dummy, NULL);
		if (Set_Back) Move_Cursor(hout, Console_W-1, Temp_Y, TRUE, TRUE);
		if (c == LF) SetConsoleTextAttribute(hout, text_attrib);
	}
}


static int IoWinConEnsureOpen(void)
{
	if (!is_open){
		
		HANDLE hin, hout;
		COORD buffer;
		SMALL_RECT screen;
		
		AllocConsole();
		// TRUE=add; FALSE=delete
		SetConsoleCtrlHandler((PHANDLER_ROUTINE) blpConCloseHandler, TRUE);
		buffer.X=Console_W+1; 
		buffer.Y=Console_H;
		screen.Bottom=Console_H-1;
		screen.Right=Console_W-1; 	// CHANGE FJS hide extra column
		screen.Top=0;
		screen.Left=0;
		hin=GetStdHandle(STD_INPUT_HANDLE);
		if (INVALID_HANDLE_VALUE!=hin){
			next_ready=0;
			did_read=0;
			strcpy(report_string,"");
			SetConsoleTitle("Altair VT100 Console"); 
			SetConsoleMode(hin, 0);	// disable system ctrl+c processing:  pass to reading app
			hout=GetStdHandle(STD_OUTPUT_HANDLE);
			if (INVALID_HANDLE_VALUE!=hout){
			CONSOLE_CURSOR_INFO ci;

				//				SetConsoleMode(hout,1); // Turn off WordWrap; Doesn't work on Win98
//				SetConsoleMode(hout,3); // Turn ON Wordwrap
				ci.dwSize = 100; /* 100 percent cursor size */
				ci.bVisible = TRUE; 
				SetConsoleWindowInfo(hout,TRUE,&screen); // change
				SetConsoleScreenBufferSize(hout,buffer); // JJF
				SetConsoleCursorInfo(hout, &ci);
				G1_Shift=0;
				G0_pointer=4;
				G1_pointer=0;
				Foreground=Text_Color;
				Background=Back_Color;
				Bold=FALSE;
				Dim=FALSE;
				Invert=FALSE;
				Underline=FALSE;
				text_attrib = Set_attrib(); // FJS
				// Set_attrib();
				SetConsoleTextAttribute(hout, text_attrib);
				cls_block(hout,0,0,Console_W-1,Console_H,FALSE);
			}
			is_open = TRUE;
		}
	}
	return is_open;
}


/**************************************************************\
*
*  FUNCTION:    blpConCloseHandler - handler for CloseConsole
*
*  INPUTS:      None
*
*  RETURNS:     BOOL flag - TRUE if message handled and OK to exit
*				or FALSE to terminate immediately (default handler)
*
*  COMMENTS:	
*
\**************************************************************/
static BOOL WINAPI blpConCloseHandler(DWORD dwCtrlType)
{
	switch (dwCtrlType){
		case CTRL_CLOSE_EVENT:
		case CTRL_SHUTDOWN_EVENT:
			// Do orderly emulator cleanup here. Otherwise, app is
			// terminated using ExitProcess()
			// Returning TRUE causes the system to post a query to
			// the user asking if he wishes to terminate the process.
			// Hopefully selecting NO will prevent the emulator from closing.
		return TRUE;	// msg processed

		// default: break;
	}
	return FALSE;		// default: terminate process immediately
}


/*******************************************************************\
* vt_flag VTparamA=0,VTparamB=0 vt_command[256] VT_index
* vt_flag =0 Nothing 
* vt_flag =1 Previous Char was an ESC get ready for code
* vt_flag =2 Previous Seq was ESC[ start collecting 1st number
* vt_flag =3 Previous Seq was ESC[##; collect 2nd number
\********************************************************************/
static void In_vtDecode(char c, HANDLE hout)
{
	static short int G_Select = 0;
	int Temp_X = 0, Temp_Y = 0, index = 0;
	SYSTEMTIME st;

	if (G_Select == 1 /* YES */) {
		switch (c) {
			case '0':	// line char set as G0
				G0_pointer = 0;
			break;
			case '1':	// std char set as G0
				G0_pointer = 1;
			break;
			case '2':	// alt line char set as G0
				G0_pointer = 2;
			break;
			case 'A':	// UK char set as G0
				G0_pointer = 3;
			break;
			case 'B':	// US char set as G0
				G0_pointer=4;
			// break;
			// default: break; // don't change char set
		} // close switch
		G_Select = 0;
		vt_flag = 0;	
		VT_index = 0;
		return;
	} //fi

	if (G_Select == 2) {
		switch (c) {
			case '0':	//line char set as G1
				G1_pointer=0;
			break;
			case '1':
				G1_pointer=1;
			break;
			case '2':
				G1_pointer=2;
			break;
			case 'A':	//UK char set as G1
				G1_pointer=3;
			break;
			case 'B':	//US char set as G1
				G1_pointer=4;
			// break;
			// default: break; // don't change char set
		} // close switch
		G_Select=0;
		vt_flag=0;	
		VT_index=0;
		return;
	} //fi

	if ((vt_flag>1) && (c>='0') && (c<='9')) {
		vt_command[VT_index] = c;
		VT_index += 1;
		return;
	}

	if (vt_flag == 1) {
		switch(c) {	// for codes of the format ESC [Character].
		case '[':
			vt_flag = 2;	// Parameter list is comming next.
		return;
		case '(':
			G_Select = 1;	// Group 0 Set
		return;
		case ')':
			G_Select = 2;	// Group 1 Set
		return;
		case STS:
			unimplemented(1);
		break;
		case SSA:
			unimplemented(2);
		break;
		case VTS:
			unimplemented(3);
		break;
		case CCH:
			unimplemented(4);
		break;
		case DCS:
			unimplemented(5);
		break;
		case EMI:
			unimplemented(6);
		break;
		case EPA:
			unimplemented(7);
		break;
		case ESA:
			unimplemented(8);
		break;
		case HTJ:
			unimplemented(9);
		break;
		case HTS:
			unimplemented(10);
		break;
		case IND:
			unimplemented(11);
		break;
		case INT:
			unimplemented(12);
		break;
		case MW:
			unimplemented(13);
		break;
		case NEL:
			unimplemented(14);
		break;
		case OSC:
			unimplemented(15);
		break;
		case PLD:
			unimplemented(16);
		break;
		case PLU:
			unimplemented(17);
		break;
		case PM:
			unimplemented(18);
		break;
		case PU1: 
			GetLocalTime(&st);
			sprintf(report_string,"%02u:%02u:%02u",st.wHour,st.wMinute,st.wSecond);
			next_ready=1;
			did_read=4;		
		break;
		case PU2:
			GetLocalTime(&st);
			sprintf(report_string,"%02u/%02u/%4u",st.wMonth,st.wDay,st.wYear);
			next_ready=1;
			did_read=4;
		break;
		case RI:
			unimplemented(21);
		break;
		case RIS:
			G1_Shift=0;
			G0_pointer=4;
			G1_pointer=0;
			Foreground=Text_Color;
			Background=Back_Color;
			Bold=FALSE;
			Dim=FALSE;
			Invert=FALSE;
			Underline=FALSE;
			text_attrib = Set_attrib(); // FJS
			//Set_attrib();
			SetConsoleTextAttribute(hout, text_attrib);
			cls_block( hout ,0 ,0 ,Console_W-1 ,Console_H ,TRUE );
		break;
		case SPA:
			unimplemented(22);
		break;
		case SS2:
			unimplemented(23);
		break;
		case SS3:
			unimplemented(24);
		break;
		case ST:
			unimplemented(25);
		break;
		} // end of switch(c)
		vt_flag = // 0;	// Code found and decoded or Invalid,DONE.
		VT_index = 0;
		return;
	} // fi

	if (vt_flag > 1) {
		vt_command[VT_index] = 0 /* EOS */; // NUL terminate the string
		VTparam[vt_flag-1]=atoi(vt_command);
		if (strlen(vt_command) == 0) {
			VTparam[0]=0; // Flag to use default parameter.
			VTparam[1]=1; // Most controls default to 1
			VTparam[2]=1; 
		}	
		else
			VTparam[0] = vt_flag-1;

		VT_index=0;
		switch(c) {
		case ';': // indicates next parameter is comming through.
			vt_flag += 1;
			return;
		case CBT:
			unimplemented(26);
			break;
		case CHA:
			Move_Cursor(hout,VTparam[1]-1,0,TRUE,FALSE);
			break;
		case CHT:
			unimplemented(27);
			break;
		case CNL:
			Move_Cursor(hout,0,VTparam[1],TRUE,FALSE);
			break;
		case CPL:
			Move_Cursor(hout,0,-(VTparam[1]),TRUE,FALSE);
			break;
		case CTC:
			unimplemented(28);
			break;
		case CUB:
			Move_Cursor(hout,-VTparam[1],0,FALSE,FALSE);
			break;
		case CUD:
			Move_Cursor(hout,0,VTparam[1],FALSE,FALSE);
			break;
		case CUF:
			Move_Cursor(hout,VTparam[1],0,FALSE,FALSE);
			break;
		case CUU:
			Move_Cursor(hout,0,-VTparam[1],FALSE,FALSE);
			break;
		case CVT:
			unimplemented(29);
			break;
		case DA:
			if (VTparam[1]==0){
				sprintf(report_string,"\033[?1;2c"); // VT100 with AVO
				next_ready=1;
				did_read=4;
			}
			break;
		case DAQ:
			unimplemented(31);
			break;
		case DCH:
			Temp_X=GetCursor_Pos(hout,TRUE);
			Temp_Y=GetCursor_Pos(hout,FALSE);
			source_block.Top=Temp_Y;
			source_block.Left=Temp_X+VTparam[1];
			source_block.Right=Console_W;
			source_block.Bottom=Temp_Y;
			destination_block.X=Temp_X;
			destination_block.Y=Temp_Y;	
			Move_Text_Block(hout,source_block,destination_block);
			cls_block(hout,Console_W-VTparam[1],Temp_Y,Console_W-1,Temp_Y,FALSE);			
			break;
		case DL:
			Temp_X=GetCursor_Pos(hout,TRUE);
			Temp_Y=GetCursor_Pos(hout,FALSE);
			source_block.Top=Temp_Y+VTparam[1];
			source_block.Left=0;
			source_block.Bottom=Console_H-1;
			source_block.Right=Console_W-1;
			destination_block.X=0;
			destination_block.Y=Temp_Y;
			Move_Text_Block(hout,source_block,destination_block);
			cls_block(hout,0,Console_H-VTparam[1],Console_W-1,Console_H,FALSE);
			break;
		case DSR:
			if (VTparam[1]==5){
				sprintf(report_string,"\033[0n");
				next_ready=1;
				did_read=4;
			}
			break;
		case EA:
			unimplemented(34);
			break;
		case ECH:
			unimplemented(35);
			break;
		case ED:
			if (VTparam[0]==0) VTparam[1]=0;
			Temp_X=GetCursor_Pos(hout,TRUE);
			Temp_Y=GetCursor_Pos(hout,FALSE);
			if ((VTparam[1]) ==0 )	// Cursor to End of screen
				cls_block(hout,Temp_X,Temp_Y,Console_W-1,Console_H,FALSE);
			else if ((VTparam[1]) ==1 )	// Beginning of screen to Cursor
				cls_block(hout,0,0,Temp_X,Temp_Y,FALSE);
			else if ((VTparam[1]) ==2)		// Whole screen
				cls_block(hout,0,0,Console_W-1,Console_H,FALSE);
			break;
		case EF:
			unimplemented(36);
			break;
		case EL:
			if (VTparam[0]==0) VTparam[1]=0;
			Temp_X=GetCursor_Pos(hout,TRUE);
			Temp_Y=GetCursor_Pos(hout,FALSE);
			if ((VTparam[1]) ==0 ) //Cursor to End of line
				cls_block(hout,Temp_X,Temp_Y,Console_W-1,Temp_Y,FALSE);
			if ((VTparam[1]) ==1 ) //Beginning of line to Cursor
				cls_block(hout,0,Temp_Y,Temp_X,Temp_Y,FALSE);
			if ((VTparam[1]) ==2)  //Whole line
				cls_block(hout,0,Temp_Y,Console_W-1,Temp_Y,FALSE);
			break;
		case HPA:
			unimplemented(37);
			break;
		case HPR:
			unimplemented(38);
			break;
		case ICH:
			Temp_X=GetCursor_Pos(hout,TRUE);
			Temp_Y=GetCursor_Pos(hout,FALSE);
			source_block.Top=Temp_Y;
			source_block.Left=Temp_X;
			source_block.Right=Console_W-VTparam[1]-1;
			source_block.Bottom=Temp_Y;
			destination_block.X=Temp_X+VTparam[1];
			destination_block.Y=Temp_Y;	
			Move_Text_Block(hout,source_block,destination_block);
			cls_block(hout,Temp_X,Temp_Y,Temp_X+VTparam[1]-1,Temp_Y,FALSE);			
			break;
		case IL:
			Temp_X=GetCursor_Pos(hout,TRUE);
			Temp_Y=GetCursor_Pos(hout,FALSE);			
			source_block.Top=Temp_Y;
			source_block.Left=0;
			source_block.Right=Console_W-1;
			source_block.Bottom=Console_H-VTparam[1]-2;
			destination_block.X=0;
			destination_block.Y=Temp_Y+VTparam[1];
			Move_Text_Block(hout,source_block,destination_block);
			cls_block(hout,0,Temp_Y,Console_W-1,Temp_Y+VTparam[1]-1,FALSE);
			break;
		case MC:
			unimplemented(39);
			break;
		case NP:
			unimplemented(40);
			break;
		case PP:
			unimplemented(41);
			break;
		case REP:
			unimplemented(42);
			break;
		case RM:
			unimplemented(43);
			break;
		case SD:
			unimplemented(44);
			break;
		case SEM:
			unimplemented(45);
			break;
		case SGR:
			if(VTparam[0]==0) {
				VTparam[1]=0;
				VTparam[0]=1;
			}
			for (index=1;index<=VTparam[0];index++) // Odd case, this can take
				Do_ANSI(hout,VTparam[index]);		// many Parameters.
			break;
		case SM:
			unimplemented(46);
			break;
		case SU:
			unimplemented(47);
			break;
		case TBC:
			unimplemented(48);
			break;
		case VPA:
			unimplemented(49);
			break;
		case VPR:
			unimplemented(50);
			break;

			// Multi parmeter codes
		case HVP: // Set cursor X,Y position
			if(VTparam[0]==0){
				VTparam[2]=1;
				VTparam[1]=1;
			}
			if (VTparam[1]==0)
				VTparam[1]=1;
			if (VTparam[2]==0)
				VTparam[2]=1;
			Move_Cursor(hout,VTparam[2]-1,VTparam[1]-1,TRUE,TRUE);
			break;
		case CPR: //T his might work 
			Temp_X=GetCursor_Pos(hout,TRUE)+1;
			Temp_Y=GetCursor_Pos(hout,FALSE)+1;
			sprintf(report_string,"\033[%i;%iR",Temp_Y,Temp_X);
			next_ready=1;
			did_read=4;
			break;
		case CUP:
			if(VTparam[0]==0) {
				VTparam[2]=1;
				VTparam[1]=1;
			}
			if (VTparam[1]==0)
				VTparam[1]=1;
			if (VTparam[2]==0)
				VTparam[2]=1;
			Move_Cursor(hout,VTparam[2]-1,VTparam[1]-1,TRUE,TRUE);
			break;
		}	//end of switch
		vt_flag=0;	// Code found and decoded or Invalid.
		VT_index=0;
		// return;
	} // fi
	// return;
}

/*************************************************************************\
*  FUNCTION:    Move_Cursor
*
*  INPUTS:      Handle to Screen buffer,Coordinates to move Cursor to.
*				Switchs if TRUE coordinates are Absolute else they are 
*				relative to the current Cursor position.
*				Can be set independantly.
*
*  RETURNS:     Nothing.
*
*  COMMENTS:	None.
\**************************************************************************/
static void Move_Cursor( HANDLE hConsole, int X_move, int Y_move,
						BOOL X_abs, BOOL Y_abs)
{
	CONSOLE_SCREEN_BUFFER_INFO csbiInfo;
	
	if( !GetConsoleScreenBufferInfo( hConsole, &csbi )) return;

	GetConsoleScreenBufferInfo(hConsole,&csbi);
	if (X_abs)
		csbiInfo.dwCursorPosition.X=X_move;
	else
		csbiInfo.dwCursorPosition.X=(csbi.dwCursorPosition.X+X_move);

	if (Y_abs)
		csbiInfo.dwCursorPosition.Y=Y_move;
	else
		csbiInfo.dwCursorPosition.Y=(csbi.dwCursorPosition.Y+Y_move);

	SetConsoleCursorPosition(hConsole,csbiInfo.dwCursorPosition);
	return;
}


/*************************************************************************\
*  FUNCTION:    cls_block
*
*  INPUTS:      Handle to Screen buffer,Coordinated of block to clear,
*				reset switch
*
*  RETURNS:     comments
*
*  COMMENTS:	If reset=TRUE Cursor postion will be set to the upper
*				left hand corner of the cleared block, else its unchanged.
\**************************************************************************/
static void cls_block( HANDLE hConsole, int Upper_X, int Upper_Y,
					  int Lower_X, int Lower_Y, BOOL reset )
{
	COORD coordScreen = { 0, 0 };    
	DWORD cCharsWritten;
	DWORD dwConSize;
	
	coordScreen.X=Upper_X;
	coordScreen.Y=Upper_Y;
	dwConSize= ((Lower_Y-Upper_Y)*(Console_W+1) +(Lower_X-Upper_X)+1);

	// Get the number of character cells in the current buffer. 
	if( !GetConsoleScreenBufferInfo( hConsole, &csbi )) return;

	// Fill the entire area with blanks.
	if( !FillConsoleOutputCharacter( hConsole, (TCHAR) ' ',
				dwConSize, coordScreen, &cCharsWritten ))
		return;

	// Get the current text attribute.
	if( !GetConsoleScreenBufferInfo( hConsole, &csbi )) return;

	// Set the buffer's attributes accordingly.
	if( !FillConsoleOutputAttribute( hConsole, csbi.wAttributes,
				dwConSize, coordScreen, &cCharsWritten ))
		return;

	// Put the cursor at its home coordinates.
	if (reset)
		SetConsoleCursorPosition( hConsole, coordScreen );
}


/*************************************************************************\
*  FUNCTION:    GetCursor_Pos
*
*  INPUTS:      Handle to Screen buffer,X/Y switch
*
*  RETURNS:     Current X position if TRUE else returns Y position
*
*  COMMENTS:	
\**************************************************************************/
static int GetCursor_Pos(HANDLE  hConsole,BOOL ord)
{
	if( !GetConsoleScreenBufferInfo( hConsole, &csbi )) return(0);

	if (ord)
		return(csbi.dwCursorPosition.X);
	else
		return (csbi.dwCursorPosition.Y);
}


static void Move_Text_Block(HANDLE hConsole,SMALL_RECT source,COORD dest)
{
	CHAR_INFO Buffer[Console_W * Console_H];
	COORD coordBufSize;
	COORD coordBufCoord;
	SMALL_RECT srctWriteRect;

	coordBufSize.X = source.Right - source.Left+1;
	coordBufSize.Y = source.Bottom - source.Top+1;
	
	coordBufCoord.X=0;
	coordBufCoord.Y=0;
    ReadConsoleOutput( 
       hConsole,        // screen buffer to read from 
       Buffer,      // buffer to copy into 
       coordBufSize,   // col-row size of Buffer 
       coordBufCoord,  // top left dest. cell in Buffer 
       &source); // screen buffer source rectangle 

	// Set the destination rectangle. 
	srctWriteRect.Top = dest.Y;    
	srctWriteRect.Left = dest.X; 
	srctWriteRect.Bottom = dest.Y+coordBufSize.Y; 
	srctWriteRect.Right = dest.X+coordBufSize.X; 
	WriteConsoleOutput( 
        hConsole, // screen buffer to write to 
        Buffer,        // buffer to copy from 
        coordBufSize,     // col-row size of Buffer 
        coordBufCoord,    // top left src cell in Buffer 
        &srctWriteRect);  // dest. screen buffer rectangle 
	return;
}

static void Do_ANSI(HANDLE hout,int Param)
{
//					IRGB					IRGB
//			Background(MS Nibble)	Foreground(LS nibble)

	if (Param==0){			 // Primary rendition [default]
		Foreground=Text_Color;
		Background=Back_Color;
		Bold=FALSE;
		Dim=FALSE;
		Invert=FALSE;
		Underline=FALSE;
	}
	if (Param==1){			// Bold or increased intensity
		Bold=TRUE;
		Dim=FALSE;
	}				
	if (Param==2){			// Faint or decreased intensity
		Bold=FALSE;			// Not used, no console support
		Dim=TRUE;
	}
	if (Param==4)			// UnderScore	
		Underline=TRUE;		// Not used, no console support.

	if (Param==7)			// Negative (reverse) image
		Invert=!Invert;		// Should this toggle??

	if ((Param >=30) && (Param <=37))	// Setting Foreground Color.
		Foreground=BitInvert(Param-30);	// Win Console 3 bit format is RGB
										// But ANSI Spec is BGR.
	if ((Param >=40) && (Param <=47))	// Setting Background Color.
		Background=BitInvert(Param-40);
	text_attrib = Set_attrib(); // FJS // this sets global text_attrib  
//	Set_attrib(); // this sets global text_attrib  
	SetConsoleTextAttribute(hout,text_attrib);
return;
}

#if 0
// This just swaps around a 3 bit int
int BitInvert(int bits)
{
	int stib=0, maskl=1, maskr=4;
	register int index; // FJS
	
	for (stib = index = 0; index++ <= 2; ){
		if (bits & maskl) stib=(stib | maskr);
		maskl = maskl*2;
		maskr = maskr/2;
	}
	return(stib);
}
#endif


static unsigned Set_attrib(void)	//FJS
{
#if 0
	if (Invert)
		text_attrib= Background + (Foreground*16);
			else
				text_attrib= Foreground +(Background * 16);
	if (Bold)
		text_attrib=(text_attrib | 8);

#else
int frgnd = Foreground, bkgnd = Background; // defaults

//                  IRGB                    IRGB
//					8421					8421
//          Background(MS Nibble)   Foreground(LS nibble)

// FJS color changes based on brightness relation -
//		Y(green) > Y(red) > Y(blue)

	// FJS TBD - Double_Size and Fast/Slow blink.

	// have underline change background color, but keep some contrast -
	/*	frgnd	bkgnd near			bkgnd far
		White	White -> Magenta	Magenta -> Blue
		Yellow	Yellow -> Red		Red -> Black
		Cyan	Cyan -> Blue		Blue -> Magenta
		Green	Green -> Black		Black -> Red

		Magenta	Magenta -> White	White -> Cyan
		Red		Red -> Yellow		Yellow -> Green
		Blue	Blue -> Cyan		Cyan -> White
		Black	Black -> Green		Green -> Yellow
	*/
	if (Underline) // change background Green or Red bit
		if ( ((bkgnd ^= 2) & 2) == (frgnd & 2)) // same Green bits ?
			bkgnd ^= 4+2; // restore Green and change Red instead

	// Let Bold/Dim change intensities or foreground color.
	// resolve Dim vs. Bold conflict (prefer Bold) -
	if (Bold) { // increase foreground:background contrast
		// black/grey/white/bright are special cases -
		if (frgnd == 0 /* Black */) { // change background
			if ( (bkgnd ^= 8) == 0 /* Black */) // change Intensity
				bkgnd = 7 /* White */; // was Grey w/ no Underline
		}
		else if (frgnd == 8 /* Grey */) { // change foreground
			frgnd = (bkgnd == 15 /* Bright White */)
					? 0 /* Black */
					: 15 /* Bright White */;
		}
		else { // colored foreground
			if ((frgnd & 8) == (bkgnd & 8)) {	// same f/b intensity?
				if (frgnd & 8)			// foreground already intense?
					bkgnd ^= 8;	// lower background intensity
				else
					frgnd ^= 8;	// raise foreground intensity
			} else	// intensities different, so change foreground color
				frgnd ^= 4+1;			// change Red+Blue
		}
	}
	else if (Dim) { // decrease foreground:background contrast (slightly)
		if ((frgnd & 8) != (bkgnd & 8)) {	// different f/b intensity?
			if ( (frgnd ^= 8) == bkgnd)	// match intensities, ...
				frgnd ^= 4;			// must change Red, too.
		} else								// same intensities already
			if ( (frgnd ^= 4) == bkgnd) // change foreground Red
				frgnd ^= 1;			// change foreground Blue, too
	}

	if (Invert) // Swap foreground and background -
		return (bkgnd | (frgnd << 4));

	// else	
	return (frgnd | (bkgnd << 4));
#endif
}

// If some VT100 App doesn't work right these codes will be helpfull
static void unimplemented (int code)
{
char buf[30];
	sprintf(buf,"Code:%i",code);
	Status_SetText(hwndStatusBar, STATBAR_CONS, 0, buf);
	return;
}

/********************************************************************\
* Translates VT100 alt character set to PC console character set.    *
* Not all characters are representable.                              *
* Input ASCII codes ranging from 95-127                              *
* Output 8 bit PC console character (byte glyph index)               *
\********************************************************************/
static unsigned char Alt_set(char udata)
{
	static unsigned char table[33] = {
		"\040\376\262hfcl\370\361nv"
		"\331\277\332\300\305\040\040\304\040\137\303"
		"\264\301\302\263\363\362\343\330\234\372\040"
		};
	static unsigned char table2[33] = {
		"\253\254\333\334\335\336\337\260\261\262\236"
		"\274\273\311\310\316\364\365\315\251\252\314"
		"\271\312\313\272\256\257\356\367\233\371\255"
		};
	// char Ret_Val=0;
	// short int Char_Pointer /* = 0 */;

	// if (G1_Shift)
		// Char_Pointer = G1_pointer;
	// else
		// Char_Pointer = G0_pointer;

	udata &= 127; // FJS for safety
	switch (G1_Shift ? G1_pointer : G0_pointer) {
		case 0:	// 0 Special Graphics
			if (udata > 94) return table[udata-95]; 
		break;
		case 2:	// 2 Alternate Character ROM Special Graphic
			if (udata > 94) return table2[udata-95];
		break;
		case 3:	// A United Kingdom Set
			if (udata == '$') return '\234'; // FJS Pound sign
			// Ret_Val = udata;
		// break;
		// case 1:	// 1 Alternate Character ROM Standard Character Set
			// Ret_Val = udata;
		// break;
		// default:
		// case 4:	// B USA ASCII Set
			// Ret_Val = udata;
		// break;
	}

	// return(Ret_Val);
	return udata;
}

/* end of file: wincon_io.c */
