///////////////////////////////////////////////////////////////////////////////
//Telnet Win32 : an ANSI telnet client.
//Copyright (C) 1998-2000 Paul Brannan
//Copyright (C) 1998 I.Ioannou
//Copyright (C) 1997 Brad Johnson
//
//This program is free software; you can redistribute it and/or
//modify it under the terms of the GNU General Public License
//as published by the Free Software Foundation; either version 2
//of the License, or (at your option) any later version.
//
//This program is distributed in the hope that it will be useful,
//but WITHOUT ANY WARRANTY; without even the implied warranty of
//MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
//GNU General Public License for more details.
//
//You should have received a copy of the GNU General Public License
//along with this program; if not, write to the Free Software
//Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
//
//I.Ioannou
//roryt@hol.gr
//
///////////////////////////////////////////////////////////////////////////

///////////////////////////////////////////////////////////////////////////////
//
// Module:		tconsole.cpp
//
// Contents:	screen functions
//
// Product:		telnet
//
//
// Revisions: Mar. 29, 2000 pbranna@clemson (Paul Brannan)
//            June 15, 1998 pbranna@clemson.edu
//            May 16, 1998	pbranna@clemson.edu
//            05. Sep.1997  roryt@hol.gr (I.Ioannou)
//            11.May,1997   roryt@hol.gr
//            06.April,1997 roryt@hol.gr
//            30.M„rz.1997  Titus_Boxberg@public.uni-hamburg.de
//		      5.Dec.1996    jbj@nounname.com
//            Version 2.0
//            02.Apr.1995	igor.milavec@uni-lj.si
//					  Original code
//
///////////////////////////////////////////////////////////////////////////////

#include <windows.h>
#include "tconsole.h"

// argsused doesn't work on MSVC++
#ifdef __BORLANDC__
#pragma argsused
#endif

TConsole::TConsole(HANDLE h) {
	hConsole = h;

	GetConsoleScreenBufferInfo(hConsole, &ConsoleInfo);

	// Start with correct colors
	int color_fg = ini.get_normal_fg();
	int color_bg = ini.get_normal_bg();
	if(color_fg == -1)
		color_fg = defaultfg = origfg = ConsoleInfo.wAttributes & 0xF;
	else
		defaultfg = origfg = color_fg;
	if(color_bg == -1)
		color_bg = defaultbg = origbg = (ConsoleInfo.wAttributes >> 4) & 0xF;
	else
		defaultbg = origbg = color_bg;
	wAttributes = color_fg | (color_bg << 4);
	reverse = blink = underline = false;
	SetConsoleTextAttribute(hConsole, wAttributes);

	insert_mode = 0;
	
	// Set the screen size
	SetWindowSize(ini.get_term_width(), ini.get_term_height());

	iScrollStart = -1;
	iScrollEnd = -1;
}

TConsole::~TConsole() {
	wAttributes = origfg | (origbg << 4);
	SetCursorPosition(0, CON_HEIGHT);
	SetConsoleTextAttribute(hConsole, wAttributes);
	WriteCtrlChar('\x0a');
}

// Paul Brannan 8/2/98
void TConsole::SetWindowSize(int width, int height) {
	SMALL_RECT sr = {
		CON_LEFT,
		CON_TOP,
		(width == -1) ? CON_RIGHT : CON_LEFT + width - 1,
		(height == -1) ? CON_BOTTOM : CON_TOP + height - 1
	};
	ConsoleInfo.dwSize.X = width;
	if(ConsoleInfo.dwSize.Y < height) ConsoleInfo.dwSize.Y = height;
	SetConsoleScreenBufferSize(hConsole, ConsoleInfo.dwSize);
	SetConsoleWindowInfo(hConsole, TRUE, &sr);
	SetConsoleScreenBufferSize(hConsole, ConsoleInfo.dwSize);
	sync();
}

// Paul Brannan 5/15/98
void TConsole::sync() {
	GetConsoleScreenBufferInfo(hConsole, &ConsoleInfo);
}

void TConsole::HighVideo() {
	wAttributes = wAttributes | (unsigned char) 8;
}

void TConsole::LowVideo() {
	wAttributes = wAttributes & (unsigned char) (0xff-8);
}

void TConsole::Normal() {
	// I.Ioannou 11 May 1997
	// Color 7 is correct on some systems (for example Linux)
	// but not with others (for example SCO)
	// we must preserve the colors :
	// 06/04/98 thanks to Paul a .ini parameter from now on
	
	BlinkOff();
	UnderlineOff();
	if(ini.get_preserve_colors()) {
		ReverseOff();
		LowVideo();
	} else {
		fg = defaultfg;
		bg = defaultbg;
		wAttributes = (unsigned char)fg | (bg << 4);
		reverse = false;
	}
}

void TConsole::SetForeground(unsigned char wAttrib) {
	if(reverse) bg = wAttrib; else fg = wAttrib;
	wAttributes = (wAttributes & (unsigned char)0x88) | 
		(unsigned char)fg | (bg << 4);
}

void TConsole::SetBackground(unsigned char wAttrib) {
	if(reverse) fg = wAttrib; else bg = wAttrib;
	wAttributes = (wAttributes & (unsigned char)0x88) | 
		(unsigned char)fg | (bg << 4);
}

// As far as I can tell, there's no such thing as blink in Windows Console.
// I tried using some inline asm to turn off high-intensity backgrounds,
// but I got a BSOD.  Perhaps there is an undocumented function?
// (Paul Brannan 6/27/98)
void TConsole::BlinkOn() {
	blink = 1;
	if(underline) {
		UlBlinkOn();
	} else {
		if(ini.get_blink_bg() != -1) {
			wAttributes &= 0x8f;					// turn off bg
			wAttributes |= ini.get_blink_bg() << 4;
		}
		if(ini.get_blink_fg() != -1) {
			wAttributes &= 0xf8;					// turn off fg
			wAttributes |= ini.get_blink_fg();
		}
		if(ini.get_blink_bg() == -1 && ini.get_blink_fg() == -1)
			wAttributes |= 0x80;
	}
}

// Added by I.Ioannou 06 April, 1997
void TConsole::BlinkOff() {
	blink = 0;
	if(underline) {
		UlBlinkOff();
	} else {
		if(ini.get_blink_bg() != -1) {
			wAttributes &= 0x8f;					// turn off bg
			wAttributes |= defaultbg << 4;
		}
		if(ini.get_blink_fg() != -1) {
			wAttributes &= 0xf8;					// turn off fg
			wAttributes |= defaultfg;
		}
		if(ini.get_blink_bg() == -1 && ini.get_blink_fg() == -1)
			wAttributes &= 0x7f;
	}
}

// Paul Brannan 6/27/98
void TConsole::UnderlineOn() {
	underline = 1;
	if(blink) {
		UlBlinkOn();
	} else {
		if(ini.get_underline_bg() != -1) {
			wAttributes &= 0x8f;					// turn off bg
			wAttributes |= ini.get_underline_bg() << 4;
		}
		if(ini.get_underline_fg() != -1) {
			wAttributes &= 0xf8;					// turn off fg
			wAttributes |= ini.get_underline_fg();
		}
		if(ini.get_underline_bg() == -1 && ini.get_underline_fg() == -1)
			wAttributes |= 0x80;
	}
}

// Paul Brannan 6/27/98
void TConsole::UnderlineOff() {
	underline = 0;
	if(blink) {
		UlBlinkOff();
	} else {
		if(ini.get_blink_bg() != -1) {
			wAttributes &= 0x8f;					// turn off bg
			wAttributes |= defaultbg << 4;
		}
		if(ini.get_blink_fg() != -1) {
			wAttributes &= 0xf8;					// turn off fg
			wAttributes |= defaultfg;
		}
		if(ini.get_blink_bg() == -1 && ini.get_blink_fg() == -1)
			wAttributes &= 0x7f;
	}
}

// Paul Brannan 6/27/98
void TConsole::UlBlinkOn() {
	if(ini.get_ulblink_bg() != -1) {
		wAttributes &= 0x8f;					// turn off bg
		wAttributes |= ini.get_ulblink_bg() << 4;
	}
	if(ini.get_ulblink_fg() != -1) {
		wAttributes &= 0xf8;					// turn off fg
		wAttributes |= ini.get_ulblink_fg();
	}
	if(ini.get_ulblink_bg() == -1 && ini.get_ulblink_fg() == -1)
		wAttributes |= 0x80;
}

// Paul Brannan 6/27/98
void TConsole::UlBlinkOff() {
	if(blink) {
		BlinkOn();
	} else if(underline) {
		UnderlineOn();
	} else {
		Normal();
	}
}

// Paul Brannan 6/26/98
void TConsole::Lightbg() {
	WORD *pAttributes = new WORD[CON_COLS];
	DWORD Result;

	// Paul Brannan 8/5/98
	// Correction: processing more than one line at a time causes a segfault
	// if the screen width != 80
	for(int i = CON_TOP; i <= CON_BOTTOM; i++) {
		COORD Coord = {CON_LEFT, i};

		ReadConsoleOutputAttribute(hConsole, pAttributes, (DWORD)(CON_COLS),
			Coord, &Result);
	
		for(DWORD j = 0; j < Result; j++) pAttributes[j] |= 0x80;

		WriteConsoleOutputAttribute(hConsole, pAttributes, Result, Coord,
			&Result);
	}

	delete[] pAttributes; // clean up

	wAttributes |= (unsigned char)0x80;
	bg |= 8;
}

// Paul Brannan 6/26/98
void TConsole::Darkbg() {
	WORD *pAttributes = new WORD[CON_COLS];
	DWORD Result;

	// Paul Brannan 8/5/98
	// Correction: processing more than one line at a time causes a segfault
	// if the screen width != 80
	for(int i = CON_TOP; i <= CON_BOTTOM; i++) {
		COORD Coord = {CON_LEFT, i};

		ReadConsoleOutputAttribute(hConsole, pAttributes, (DWORD)(CON_COLS),
			Coord, &Result);
	
		for(DWORD j = 0; j < Result; j++) pAttributes[j] &= 0x7f;

		WriteConsoleOutputAttribute(hConsole, pAttributes, Result, Coord,
			&Result);
	}

	delete[] pAttributes; // clean up


	wAttributes &= (unsigned char)0x7f;
	bg &= 7;
}

// Added by I.Ioannou 11.May,1997
void TConsole::ReverseOn() {
	if (!reverse) {
		reverse = true;

		// atl  : forground attributes without the intensity
		// ath  : backgound attributes without the blink
		// bl   : the blink state
		// ints : the intensity
		unsigned char atl   = wAttributes & (unsigned char) 0x07;
		unsigned char ath   = wAttributes & (unsigned char) 0x70;
		unsigned char bl    = wAttributes & (unsigned char) 0x80;
		unsigned char ints  = wAttributes & (unsigned char) 0x08;
		wAttributes = bl | (atl << 4) | ints | (ath >> 4);
	}
}

// Added by I.Ioannou 11.May,1997
void TConsole::ReverseOff() {
	if (reverse) {
		reverse = false;
		wAttributes = fg | (bg << 4);
	}
}

unsigned long TConsole::WriteText(const char *pszString, unsigned long cbString) {
	DWORD Result;

	if(insert_mode) {
		InsertCharacter(cbString);
	}

	WriteConsoleOutputCharacter(hConsole, (char *)pszString, cbString,
		ConsoleInfo.dwCursorPosition, &Result);
	FillConsoleOutputAttribute(hConsole, wAttributes, cbString,
		ConsoleInfo.dwCursorPosition, &Result);
	return Result;
}

// Formerly ConWriteString (Paul Brannan 6/28/98)
unsigned long TConsole::WriteStringFast(const char* pszString, unsigned long cbString) {
	DWORD Result;

	SetConsoleTextAttribute(hConsole, wAttributes);

	//check to see if the line is longer than the display
	if (!getLineWrap() && ((unsigned)CON_CUR_X + cbString) >= (unsigned)CON_COLS) {
		// Take care of the last line last colum exception...
		// The display scrolls up if you use the normal char out
		//   function even if you only write to the last place
		//   on the line. :-(
		if ((unsigned)CON_CUR_Y >= (unsigned)CON_HEIGHT) {
			unsigned long iFakeResult = cbString;
			cbString = CON_COLS - CON_CUR_X - 1;

			// FIX ME !!! This will avoid the exception when cbString
			// is <= 0 but still doesn't work :-(
			if (cbString > 0)
				WriteConsole(hConsole, pszString, cbString, &Result, 0);

			COORD dwBufferCoord;
			dwBufferCoord.X = 0;
			dwBufferCoord.Y = 0;

			CHAR_INFO ciBuffer;
			ciBuffer.Char.AsciiChar = *(pszString+cbString);
			ciBuffer.Attributes = wAttributes;
			SMALL_RECT srWriteRegion;
			srWriteRegion.Top =		(SHORT) CON_BOTTOM;
			srWriteRegion.Bottom =	(SHORT) CON_BOTTOM;
			srWriteRegion.Left =	(SHORT) CON_RIGHT;
			srWriteRegion.Right =	(SHORT) CON_RIGHT;

			COORD bufSize = {1,1};

			WriteConsoleOutput(hConsole, &ciBuffer, bufSize,
				dwBufferCoord, &srWriteRegion);

			// We need to update the ConsoleInfo struct now (Paul Brannan 5/9/98)
			ConsoleInfo.dwCursorPosition.X = CON_RIGHT;

			return iFakeResult; // Skip the chars that did not fit
		}
		// just write the line up to the end
		else {
			int iFakeResult = cbString;
			cbString = CON_COLS - CON_CUR_X;

			if(cbString > 0) {
				WriteConsole(hConsole, pszString, cbString, &Result, 0);

				// We need to update the ConsoleInfo struct now (Paul Brannan 5/9/98)
				ConsoleInfo.dwCursorPosition.X += (unsigned short)Result;
			}

			return iFakeResult; // Skip the chars that did not fit
		}
	} else {
		// If custom scrolling is enabled we must take care of it
		if(iScrollStart != -1 || iScrollEnd != -1) {
			return WriteString(pszString, cbString);
		}

		// Apparently VT100 terminals have an invisible "81st" column that
		// can hold a cursor until another character is printed.  I'm not sure
		// exactly how to handle this, but here's a hack (Paul Brannan 5/28/98)
		if(ini.get_vt100_mode() && cbString + (unsigned)CON_CUR_X == (unsigned)CON_COLS) {

			cbString--;
			if(cbString >= 0) WriteConsole(hConsole, pszString, cbString, &Result, 0);

			COORD dwBufferCoord;
			dwBufferCoord.X = 0;
			dwBufferCoord.Y = 0;

			CHAR_INFO ciBuffer;
			ciBuffer.Char.AsciiChar = *(pszString+cbString);
			ciBuffer.Attributes = wAttributes;
			SMALL_RECT srWriteRegion;
			srWriteRegion.Top =    (SHORT) ConsoleInfo.dwCursorPosition.Y;
			srWriteRegion.Bottom = (SHORT) ConsoleInfo.dwCursorPosition.Y;
			srWriteRegion.Left =   (SHORT) CON_RIGHT;
			srWriteRegion.Right =  (SHORT) CON_RIGHT;

			COORD bufSize = {1,1};

			WriteConsoleOutput(hConsole, &ciBuffer, bufSize,
				dwBufferCoord, &srWriteRegion);

			// Update the ConsoleInfo struct
			ConsoleInfo.dwCursorPosition.X = CON_RIGHT + 1;

			return Result + 1;
		}

		// normal line will wrap normally or not to the end of buffer
		WriteConsole(hConsole, pszString, cbString, &Result, 0);

		// We need to update the ConsoleInfo struct now (Paul Brannan 5/9/98)
		// FIX ME!!! This is susceptible to the same problem as above.
		// (e.g. we write out 160 characters)
		ConsoleInfo.dwCursorPosition.X += (unsigned short)Result;
		while(CON_CUR_X > CON_WIDTH) {
			ConsoleInfo.dwCursorPosition.X -= ConsoleInfo.dwSize.X;
			if((unsigned)CON_CUR_Y < (unsigned)CON_HEIGHT) {
				ConsoleInfo.dwCursorPosition.Y++;
			} else {
				// If we aren't at the bottom of the window, then we need to
				// scroll down (Paul Brannan 4/14/2000)
				if(ConsoleInfo.srWindow.Bottom < ConsoleInfo.dwSize.Y - 1) {
					ConsoleInfo.srWindow.Top++;
					ConsoleInfo.srWindow.Bottom++;
					ConsoleInfo.dwCursorPosition.Y++;
					SetConsoleWindowInfo(hConsole, TRUE, &ConsoleInfo.srWindow);
				}
			}
		}
	}

	return Result;

}

unsigned long TConsole::WriteString(const char* pszString, unsigned long cbString) {
	DWORD Result = 0;
	
	SetConsoleTextAttribute(hConsole, wAttributes);

	//check to see if the line is longer than the display
	if (!getLineWrap()){
		unsigned long iFakeResult = cbString;
		if((CON_CUR_X + cbString) >= (unsigned int)CON_COLS)
			cbString = CON_COLS - CON_CUR_X;
		if(cbString > 0)
			Result = WriteText(pszString, cbString);

		// We need to update the ConsoleInfo struct now (Paul Brannan 5/9/98)
		ConsoleInfo.dwCursorPosition.X += (unsigned short)Result;
		SetConsoleCursorPosition(hConsole, ConsoleInfo.dwCursorPosition);

		return iFakeResult; // Skip the chars that did not fit
	} else {
		// Write up to the end of the line
		unsigned long temp = cbString;
		if((CON_CUR_X + temp) > (unsigned int)CON_COLS) {
			temp = CON_COLS - CON_CUR_X;
		} else {
			Result = WriteText(pszString, temp);
			ConsoleInfo.dwCursorPosition.X += (unsigned short)Result;
			SetConsoleCursorPosition(hConsole, ConsoleInfo.dwCursorPosition);
			return Result;
		}
		if(temp > 0) {
			Result = WriteText(pszString, temp);
			ConsoleInfo.dwCursorPosition.X += (unsigned short)Result;
			temp = (unsigned short)Result;
		}

		// keep writing lines until we get to less than 80 chars left
		while((temp + (unsigned int)CON_COLS) < cbString) {
			index(); // LF
			ConsoleInfo.dwCursorPosition.X = 0; // CR
			Result = WriteText(&pszString[temp], CON_COLS);
			temp += (unsigned short)Result;
		}

		// write out the last bit
		if(temp < cbString) {
			index();
			ConsoleInfo.dwCursorPosition.X = 0;
			Result = WriteText(&pszString[temp], cbString - temp);
			temp += (unsigned short)Result;
		}

		// Apparently VT100 terminals have an invisible "81st" column that
		// can hold a cursor until another character is printed.  I'm not sure
		// exactly how to handle this, but here's a hack (Paul Brannan 5/28/98)
		if(!ini.get_vt100_mode() && cbString + (unsigned)ConsoleInfo.dwCursorPosition.X
			== (unsigned int)CON_COLS) {
			index();
			ConsoleInfo.dwCursorPosition.X = 0;
		}

		SetConsoleCursorPosition(hConsole, ConsoleInfo.dwCursorPosition);

		return temp;
	}

	return 0;
}

// This is for multi-character control strings (Paul Brannan 6/26/98)
unsigned long TConsole::WriteCtrlString(const char *pszString, unsigned long cbString) {
	unsigned long total = 0;
	while(total < cbString) {
		unsigned long Result = WriteCtrlChar(*(pszString + total));
		if(Result == 0) {
			Result = WriteStringFast(pszString + total, 1);
			if(Result == 0) return total;
		}
		total += Result;
	}
	return total;
}

// This is for printing single control characters
// WriteCtrlString uses this (Paul Brannan 6/26/98)
unsigned long TConsole::WriteCtrlChar(char c) {
	// The console does not handel the CR/LF chars as we might expect
	// when using color. The attributes are not set correctly, so we
	// must interpret them manualy to preserve the colors on the screen.
	
	unsigned long Result = 0; // just in case (Paul Brannan 6/26/98)
	switch (c) {
    case '\x09': // horizontal tab
		SetCursorPosition((((CON_CUR_X/8)+1)*8), CON_CUR_Y);
		Result = 1;
		break;
		
    case '\x0a': // line feed
		index();
		Result = 1;
		break;
    case '\x0d': // carrage return
		SetCursorPosition(CON_LEFT, CON_CUR_Y); // move to beginning of line
		Result = 1;
		break;
	case '\b': // backspace
		// Added support for backspace so the cursor position can be changed
		// (Paul Brannan 5/25/98)
		MoveCursorPosition(-1, 0);
		Result = 1;
    default :    // else just write it like normal
		break;
	}
	
	return Result;
}

void TConsole::index() {
	// if on the last line scroll up
	// This must work with scrolling (Paul Brannan 5/13/98)
	if(iScrollEnd != -1 && (signed)CON_CUR_Y >= iScrollEnd) {
		ScrollDown(iScrollStart, iScrollEnd, -1);
	} else if ((iScrollEnd == -1 && (signed)CON_CUR_Y >= (signed)CON_HEIGHT)) {
		DWORD Result;
		WriteConsole(hConsole, "\n", 1, &Result, NULL);
		
		// If we aren't at the bottom of the buffer, then we need to
		// scroll down (Paul Brannan 4/14/2000)
		if(iScrollEnd == -1 && ConsoleInfo.srWindow.Bottom < ConsoleInfo.dwSize.Y - 1) {
			ConsoleInfo.srWindow.Top++;
			ConsoleInfo.srWindow.Bottom++;
			ConsoleInfo.dwCursorPosition.Y++;
			// SetConsoleWindowInfo(hConsole, TRUE, &ConsoleInfo.srWindow);
		} else {
			ClearLine();
		}
	} else {     // else move cursor down to the next line
		SetCursorPosition(CON_CUR_X, CON_CUR_Y + 1);
	}
}

void TConsole::reverse_index() {
	// if on the top line scroll down
	// This must work with scrolling (Paul Brannan 5/13/98)
	// We should be comparing against iScrollStart, not iScrollEnd (PB 12/2/98)
	if (iScrollStart == -1 && (signed)CON_CUR_Y <= 0) {
		ScrollDown(iScrollStart, -1, 1);
	} else if (iScrollStart != -1 && (signed)CON_CUR_Y <= iScrollStart) {
			ScrollDown(iScrollStart, iScrollEnd, 1);
	} else       // else move cursor up to the previous line
		SetCursorPosition(CON_CUR_X,CON_CUR_Y - 1);
}

void TConsole::ScrollDown( int iStartRow , int iEndRow, int bUp ){
	CHAR_INFO ciChar;
	SMALL_RECT srScrollWindow;
	
	// Correction from I.Ioannou 11 May 1997
	// check the scroll region
	if (iStartRow < iScrollStart) iStartRow = iScrollStart;
	
	// Correction from I.Ioannou 11 May 1997
	// this will make Top the CON_TOP
	if ( iStartRow == -1) iStartRow = 0;

	// Correction from I.Ioannou 18 Aug 97
	if ( iEndRow == -1)	{
		if ( iScrollEnd == -1 )
			iEndRow = CON_HEIGHT;
		else
			iEndRow = ((CON_HEIGHT <= iScrollEnd) ? CON_HEIGHT : iScrollEnd);
	}
	//

	if ( iStartRow > CON_HEIGHT) iStartRow = CON_HEIGHT;
	if ( iEndRow > CON_HEIGHT) iEndRow = CON_HEIGHT;
	
	srScrollWindow.Left =           (CON_LEFT);
	srScrollWindow.Right =  (SHORT) (CON_RIGHT);
	srScrollWindow.Top =    (SHORT) (CON_TOP + iStartRow );
	srScrollWindow.Bottom = (SHORT) (CON_TOP + iEndRow); // don't subtract 1 (PB 5/28)

	ciChar.Char.AsciiChar = ' ';           // fill with spaces
	ciChar.Attributes = wAttributes;       // fill with current attrib
	
	// This should speed things up (Paul Brannan 9/2/98)
	COORD dwDestOrg = {srScrollWindow.Left, srScrollWindow.Top + bUp};
	
	// Note that iEndRow and iStartRow had better not be equal to -1 at this
	// point.  There are four cases to consider for out of bounds.  Two of
	// these cause the scroll window to be cleared; the others cause the
	// scroll region to be modified.  (Paul Brannan 12/3/98)
	if(dwDestOrg.Y > CON_TOP + iEndRow) {
		// We are scrolling past the end of the scroll region, so just
		// clear the window instead (Paul Brannan 12/3/98)
		ClearWindow(CON_TOP + iStartRow, CON_TOP + iEndRow);
		return;
	} else if(dwDestOrg.Y + (iEndRow-iStartRow+1) < CON_TOP + iStartRow) {
		// We are scrolling past the end of the scroll region, so just
		// clear the window instead (Paul Brannan 12/3/98)
		ClearWindow(CON_TOP + iStartRow, CON_TOP + iEndRow);
		return;
	} else if(dwDestOrg.Y < CON_TOP + iStartRow) {
		// Modify the scroll region (Paul Brannan 12/3/98)
		dwDestOrg.Y = CON_TOP + iStartRow;
		srScrollWindow.Top -= bUp;
	} else 	if(dwDestOrg.Y + (iEndRow-iStartRow+1) > CON_TOP + iEndRow) {
		// Modify the scroll region (Paul Brannan 12/3/98)
		srScrollWindow.Bottom -= bUp;
	}
	
	ScrollConsoleScreenBuffer(hConsole, &srScrollWindow,
		0, dwDestOrg, &ciChar);
}

// This allows us to clear the screen with an arbitrary character
// (Paul Brannan 6/26/98)
void TConsole::ClearScreen(char c) {
	DWORD dwWritten;
	COORD Coord = {CON_LEFT, CON_TOP};
	FillConsoleOutputCharacter(hConsole, c, (DWORD)(CON_COLS)*
		(DWORD)(CON_LINES), Coord, &dwWritten);
	FillConsoleOutputAttribute(hConsole, wAttributes, (DWORD)(CON_COLS)*
		(DWORD)(CON_LINES), Coord, &dwWritten);
}

// Same as clear screen, but only affects the scroll region
void TConsole::ClearWindow(int iStartRow, int iEndRow, char c) {
	DWORD dwWritten;
	COORD Coord = {CON_LEFT, CON_TOP + iStartRow};
	FillConsoleOutputCharacter(hConsole, c, (DWORD)(CON_COLS)*
		(DWORD)(iEndRow-iStartRow+1), Coord, &dwWritten);
	FillConsoleOutputAttribute(hConsole, wAttributes, (DWORD)(CON_COLS)*
		(DWORD)(CON_LINES), Coord, &dwWritten);
}

// Clear from cursor to end of screen
void TConsole::ClearEOScreen(char c)
{
	DWORD dwWritten;
	COORD Coord = {CON_LEFT, CON_TOP + CON_CUR_Y + 1};
	FillConsoleOutputCharacter(hConsole, c, (DWORD)(CON_COLS)*
		(DWORD)(CON_HEIGHT - CON_CUR_Y), Coord, &dwWritten);
	FillConsoleOutputAttribute(hConsole, wAttributes, (DWORD)(CON_COLS)*
		(DWORD)(CON_LINES - CON_CUR_Y), Coord, &dwWritten);
	ClearEOLine();
}

// Clear from beginning of screen to cursor
void TConsole::ClearBOScreen(char c)
{
	DWORD dwWritten;
	COORD Coord = {CON_LEFT, CON_TOP};
	FillConsoleOutputCharacter(hConsole, c, (DWORD)(CON_COLS)*
		(DWORD)(CON_CUR_Y), Coord, &dwWritten);
	FillConsoleOutputAttribute(hConsole, wAttributes, (DWORD)(CON_COLS)*
		(DWORD)(CON_CUR_Y), Coord, &dwWritten);
	ClearBOLine();
}

void TConsole::ClearLine(char c)
{
	DWORD dwWritten;
	COORD Coord = {CON_LEFT, CON_TOP + CON_CUR_Y};
	FillConsoleOutputCharacter(hConsole, c, (DWORD)(CON_COLS),
		Coord, &dwWritten);
	FillConsoleOutputAttribute(hConsole, wAttributes, (DWORD)(CON_COLS),
		Coord, &dwWritten);
	GetConsoleScreenBufferInfo(hConsole, &ConsoleInfo);
}

void TConsole::ClearEOLine(char c)
{
	DWORD dwWritten;
	COORD Coord = {CON_LEFT + CON_CUR_X, CON_TOP + CON_CUR_Y};
	FillConsoleOutputAttribute(hConsole, wAttributes,
		(DWORD)(CON_RIGHT - CON_CUR_X) +1, Coord, &dwWritten);
	FillConsoleOutputCharacter(hConsole, c, (DWORD)(CON_RIGHT - CON_CUR_X) +1,
		Coord, &dwWritten);
	GetConsoleScreenBufferInfo(hConsole, &ConsoleInfo);
}

void TConsole::ClearBOLine(char c)
{
	DWORD dwWritten;
	COORD Coord = {CON_LEFT, CON_TOP + CON_CUR_Y};
	FillConsoleOutputCharacter(hConsole, c, (DWORD)(CON_CUR_X) + 1, Coord,
		&dwWritten);
	FillConsoleOutputAttribute(hConsole, wAttributes, (DWORD)(CON_CUR_X) + 1,
		Coord, &dwWritten);
	GetConsoleScreenBufferInfo(hConsole, &ConsoleInfo);
}


//	Inserts blank lines to the cursor-y-position
//	scrolls down the rest. CURSOR MOVEMENT (to Col#1) ???
void TConsole::InsertLine(int numlines)
{
	COORD		to;
	SMALL_RECT	from;
	SMALL_RECT	clip;
	CHAR_INFO		fill;
	int		acty;
	
	// Rest of screen would be deleted
	if ( (acty = GetCursorY()) >= CON_LINES - numlines ) {
		ClearEOScreen();    // delete rest of screen
		return;
	} /* IF */
	
	//	Else scroll down the part of the screen which is below the
	//	cursor.
	from.Left =		CON_LEFT;
	from.Top =		CON_TOP + (SHORT)acty;
	from.Right =	CON_LEFT + (SHORT)CON_COLS;
	from.Bottom =	CON_TOP + (SHORT)CON_LINES;
	
	clip = from;
	to.X = 0;
	to.Y = (SHORT)(from.Top + numlines);
	
	fill.Char.AsciiChar = ' ';
	fill.Attributes = 7; 		// WHICH ATTRIBUTES TO TAKE FOR BLANK LINE ??
	
	ScrollConsoleScreenBuffer(hConsole, &from, &clip, to, &fill);
} /* InsertLine */

//	Inserts blank characters under the cursor
void TConsole::InsertCharacter(int numchar)
{
	int		actx;
	SMALL_RECT	from;
	SMALL_RECT	clip;
	COORD			to;
	CHAR_INFO		fill;
	
	if ( (actx = GetCursorX()) >= CON_COLS - numchar ) {
		ClearEOLine();
		return;
	} /* IF */
	
	from.Left =		CON_LEFT + (SHORT)actx;
	from.Top =		CON_TOP + (SHORT)GetCursorY();
	from.Right =	CON_LEFT + (SHORT)CON_COLS;
	from.Bottom =	CON_TOP + (SHORT)from.Top;
	
	clip = from;
	to.X = (SHORT)(actx + numchar);
	to.Y = from.Top;
	
	fill.Char.AsciiChar = ' ';
	fill.Attributes = wAttributes; // WHICH ATTRIBUTES TO TAKE FOR BLANK CHAR ??
	
	ScrollConsoleScreenBuffer(hConsole, &from, &clip, to, &fill);
} /* InsertCharacter */

// Deletes characters under the cursor
// Note that there are cases in which all the following lines should shift by
// a character, but we don't handle these.  This could break some
// VT102-applications, but it shouldn't be too much of an issue.
void TConsole::DeleteCharacter(int numchar)
{
	int		actx;
	SMALL_RECT	from;
	SMALL_RECT	clip;
	COORD			to;
	CHAR_INFO		fill;
	
	if ( (actx = GetCursorX()) >= CON_COLS - numchar ) {
		ClearEOLine();
		return;
	} /* IF */
	
	from.Left =		CON_LEFT + (SHORT)actx;
	from.Top =		CON_TOP + (SHORT)GetCursorY();
	from.Right =	CON_LEFT + (SHORT)CON_COLS;
	from.Bottom =	CON_TOP + from.Top;
	
	clip = from;
	to.X = (SHORT)(actx - numchar);
	to.Y = from.Top;
	
	fill.Char.AsciiChar = ' ';
	fill.Attributes = wAttributes; // WHICH ATTRIBUTES TO TAKE FOR BLANK CHAR ??
	
	ScrollConsoleScreenBuffer(hConsole, &from, &clip, to, &fill);
} /* DeleteCharacter */

void TConsole::SetRawCursorPosition(int x, int y) {
	if (x > CON_WIDTH)  x = CON_WIDTH;
	if (x < 0)			x = 0;
	if (y > CON_HEIGHT)	y = CON_HEIGHT;
	if (y < 0)			y = 0;
	COORD Coord = {(short)(CON_LEFT + x), (short)(CON_TOP + y)};
	SetConsoleCursorPosition(hConsole, Coord);
	
	// Update the ConsoleInfo struct (Paul Brannan 5/9/98)
	ConsoleInfo.dwCursorPosition.Y = Coord.Y;
	ConsoleInfo.dwCursorPosition.X = Coord.X;
	
	// bug fix in case we went too far (Paul Brannan 5/25/98)
	if(ConsoleInfo.dwCursorPosition.X < CON_LEFT)
		ConsoleInfo.dwCursorPosition.X = CON_LEFT;
	if(ConsoleInfo.dwCursorPosition.X > CON_RIGHT)
		ConsoleInfo.dwCursorPosition.X = CON_RIGHT;
	if(ConsoleInfo.dwCursorPosition.Y < CON_TOP)
		ConsoleInfo.dwCursorPosition.Y = CON_TOP;
	if(ConsoleInfo.dwCursorPosition.Y > CON_BOTTOM)
		ConsoleInfo.dwCursorPosition.Y = CON_BOTTOM;
}

// The new SetCursorPosition takes scroll regions into consideration
// (Paul Brannan 6/27/98)
void TConsole::SetCursorPosition(int x, int y) {
	if (x > CON_WIDTH)  x = CON_WIDTH;
	if (x < 0)			x = 0;
	if(iScrollEnd != -1) {
		if(y > iScrollEnd)		y = iScrollEnd;
	} else {
		if(y > CON_HEIGHT)		y = CON_HEIGHT;
	}
	if(iScrollStart != -1) {
		if(y < iScrollStart)	y = iScrollStart;
	} else {
		if(y < 0)				y = 0;
	}

	COORD Coord = {(short)(CON_LEFT + x), (short)(CON_TOP + y)};
	SetConsoleCursorPosition(hConsole, Coord);
	
	// Update the ConsoleInfo struct
	ConsoleInfo.dwCursorPosition.Y = Coord.Y;
	ConsoleInfo.dwCursorPosition.X = Coord.X;
}

void TConsole::MoveCursorPosition(int x, int y) {
	SetCursorPosition(CON_CUR_X + x, CON_CUR_Y + y);
}

void TConsole::SetExtendedMode(int iFunction, BOOL bEnable)
{
	// Probably should do something here...
	// Should change the screen mode, but do we need this?
}

void TConsole::SetScroll(int start, int end) {
	iScrollStart = start;
	iScrollEnd = end;
}

void TConsole::Beep() {
	if(ini.get_do_beep()) {
		if(!ini.get_speaker_beep()) printit("\a");
		else ::Beep(400, 100);
	}
}

void TConsole::SetCursorSize(int pct) {
	CONSOLE_CURSOR_INFO ci = {(pct != 0)?pct:1, pct != 0};
	SetConsoleCursorInfo(hConsole, &ci);
}

void saveScreen(CHAR_INFO *chiBuffer) {
	HANDLE hStdout;
	CONSOLE_SCREEN_BUFFER_INFO ConsoleInfo;
	SMALL_RECT srctReadRect;
	COORD coordBufSize;
	COORD coordBufCoord;
	
	hStdout = GetStdHandle(STD_OUTPUT_HANDLE);
	GetConsoleScreenBufferInfo(hStdout, &ConsoleInfo);
	
    srctReadRect.Top = CON_TOP;    /* top left: row 0, col 0  */
    srctReadRect.Left = CON_LEFT;
    srctReadRect.Bottom = CON_BOTTOM; /* bot. right: row 1, col 79 */
    srctReadRect.Right = CON_RIGHT;
	
    coordBufSize.Y = CON_BOTTOM-CON_TOP+1;
    coordBufSize.X = CON_RIGHT-CON_LEFT+1;
	
    coordBufCoord.X = CON_TOP;
    coordBufCoord.Y = CON_LEFT;
	
    ReadConsoleOutput(
		hStdout,        /* screen buffer to read from       */
		chiBuffer,      /* buffer to copy into              */
		coordBufSize,   /* col-row size of chiBuffer        */
		
		coordBufCoord,  /* top left dest. cell in chiBuffer */
		&srctReadRect); /* screen buffer source rectangle   */
}

void restoreScreen(CHAR_INFO *chiBuffer) {
	HANDLE hStdout;
	CONSOLE_SCREEN_BUFFER_INFO ConsoleInfo;
	SMALL_RECT srctReadRect;
	COORD coordBufSize;
	COORD coordBufCoord;
	
	hStdout = GetStdHandle(STD_OUTPUT_HANDLE);
	GetConsoleScreenBufferInfo(hStdout, &ConsoleInfo);
	
	// restore screen
    srctReadRect.Top = CON_TOP;    /* top left: row 0, col 0  */
    srctReadRect.Left = CON_LEFT;
    srctReadRect.Bottom = CON_BOTTOM; /* bot. right: row 1, col 79 */
    srctReadRect.Right = CON_RIGHT;
	
    coordBufSize.Y = CON_BOTTOM-CON_TOP+1;
    coordBufSize.X = CON_RIGHT-CON_LEFT+1;
	
    coordBufCoord.X = CON_TOP;
    coordBufCoord.Y = CON_LEFT;
    WriteConsoleOutput(
        hStdout, /* screen buffer to write to    */
        chiBuffer,        /* buffer to copy from          */
        coordBufSize,     /* col-row size of chiBuffer    */
        coordBufCoord, /* top left src cell in chiBuffer  */
        &srctReadRect); /* dest. screen buffer rectangle */
	// end restore screen
    
}

CHAR_INFO* newBuffer() {
    CONSOLE_SCREEN_BUFFER_INFO ConsoleInfo;
    HANDLE hStdout = GetStdHandle(STD_OUTPUT_HANDLE);
    GetConsoleScreenBufferInfo(hStdout, &ConsoleInfo);
    CHAR_INFO * chiBuffer;
    chiBuffer = new CHAR_INFO[(CON_BOTTOM-CON_TOP+1)*(CON_RIGHT-CON_LEFT+1)];
	return chiBuffer;
}

void deleteBuffer(CHAR_INFO* chiBuffer) {
	delete[] chiBuffer;
}

