// --------------------------------------------------------------------------
// Citadel: Console.CPP
//
// This code handles the console on the IBM PC and compatible computers. This
// code will need heavy modification for use with other computers.

#include "ctdl.h"
#pragma hdrstop

#include "log.h"
#include "events.h"
#include "cwindows.h"
#include "timechk.h"

// --------------------------------------------------------------------------
// Contents
//
// doccr			Do CR on console, used to not scroll the window
// KBReady 			returns TRUE if a console char is ready
// ciChar			Get character from console			   
// outCon			put a character out to the console	   
// kb_hit			returns TRUE if character waiting	   
// special_pressed true if ALT, Shift, or CTRL pressed 	   

extern ConsoleLockC ConsoleLock;		// conovl.cpp


// --------------------------------------------------------------------------
// doccr(): Do CR on console, used to not scroll the window.

void doccr(void)
	{
	int row, col;

	if (TI()menu69)
		{
		TI()menu69 = FALSE;
		doCR();
		TI()menu69 = TRUE;
		return;
		}

#ifdef WINCIT
	if (!haveMainWindow)
		{
		return;
		}
#endif

	if (!TI()OC.Console)
		{
		return;
		}

	readpos(&row, &col);

	if (row >= scrollpos)
		{
		scroll(scrollpos, 1, cfg.wysiwyg ? TI()OC.ansiattr : cfg.attr);
		position(scrollpos, 0);
		}
	else
		{
		row++;
		position(row, 0);
		}
	}


// --------------------------------------------------------------------------
// KBReady(): Returns TRUE if a console char is ready.

Bool KBReady(void)
	{
	if (TI()KeyboardBufferMonitor && KeyboardBuffer.IsDirty())
		{
		(TI()KeyboardBufferMonitor->func)(EVT_DRAWINT, 0, 0,
				TI()KeyboardBufferMonitor);

		KeyboardBuffer.SetDirty(FALSE);
		}

	if (TI()SerialPortMonitor && SerialPort.IsDirty())
		{
		(TI()SerialPortMonitor->func)(EVT_DRAWINT, 0, 0,
				TI()SerialPortMonitor);

		SerialPort.SetDirty(FALSE);
		}

	if (kb_hit())
		{
		if (TI()KeyboardBuffer.Peek() == 12)
			{
			TI()KeyboardBuffer.Retrieve();

			if (!ConsoleLock.IsLocked(TRUE))
				{
				sysopkey = TRUE;
				}
			}
		else
			{
			return (TRUE);
			}
		}

	return (FALSE);
	}


// --------------------------------------------------------------------------
// ciChar(): Get character from console.

int ciChar(void)
	{
	while (!kb_hit());

	TI()SerialPort.IncReceived();
	return (TI()KeyboardBuffer.Retrieve());
	}


// --------------------------------------------------------------------------
// outCon(): Put a character out to the console.

void outCon(char c)
	{
	int row, col;

	if (!TI()OC.Console || (c == BELL && cfg.noBells) || (c == 26))
		{
		return;
		}

	if (c == ESC || TI()OC.inANSI)	// ESC || ANSI sequence
		{
		ansi(c);
		return;
		}

	// if we don't have carrier then count what goes to console
	if (!TI()modStat)
		{
		TI()SerialPort.IncTransmitted();
		}

	if (c == '\n')                  // Newline
		{
		doccr();
		}
	else if (c == '\r')             // Return
		{
		readpos(&row, &col);
		position(row, 0);
		}
	else if (c == BELL) 			// Bell
		{
		doEvent(EVT_BEEP);
#ifdef WINCIT
		MessageBeep(0);
#else
		putchar(c);
#endif
		}
	else if (c == '\b')             // Backspace
		{
		readpos(&row, &col);

		if (col == 0 && TI()prevChar != 10)
			{
			row--;
			col = conCols;
			}

		position(row, col - 1);
		(*charattr)(' ', TI()OC.ansiattr, FALSE);
		position(row, col - 1);

		if (CitWindowsVid)
			{
			CitWindowsTTYUpdate(row, row);
			}
		}
	else							// Other Character
		{
		if ((*charattr)(c, TI()OC.ansiattr, FALSE))
			{
			if (CitWindowsVid)
				{
				readpos(&row, &col);
				CitWindowsTTYUpdate(row, row++);
				}

			doccr();
			}
		else
			{
			// now do cursor positioning here
			readpos(&row, &col);
			position(row, ++col);
			}

		if (CitWindowsVid)
			{
			CitWindowsTTYUpdate(row, row);
			}
		}
	}


// --------------------------------------------------------------------------
// kb_hit(): Returns TRUE if character waiting.

// Moving this outside of the kb_hit() function gives us a slight speed
// increase: it is initialized when Citadel loads instead of the first time
// that kb_hit is called. This avoids a little check inserted by the compiler
// to check if it is the first calling.
static TimeChangeCheckerC UpdateTime;

Bool kb_hit(void)
	{
	// Update clock and cron monitor when needed
	if (
			(	// Nobody on
				!TI()loggedIn ||

				// Or screen-saver is on
				ScreenSaver.IsOn() ||

				// Or status screen is up
				StatusLine.IsFullScreen() ||

				// Or CronMonitor is on
				TI()CronMonitor
			)
		&&
			UpdateTime.Check()
		)
		{
		if (TI()CronMonitor)
			{
			(TI()CronMonitor->func)(EVT_DRAWINT, 0, 0, TI()CronMonitor);
			}

		StatusLine.Update();
		UpdateTime.Reset();
		}

	if (allWindows)
		{
		CitWindowsProcessMouse();
		}

	if (CitWindowsKbd)
		{
		CitWindowsProcessKbd();
		}

	if (!(cfg.FullConLock && ConsoleLock.IsLocked()) &&
			(*sp_press)())
		{
		ScreenSaver.Update();
		}

	if (!CitWindowsKbd)
		{
		while ((*statcon)() && !TI()KeyboardBuffer.IsFull())
			{
			int c = (*getcon)();

			if (c < 256)
				{
				if (cfg.FullConLock && ConsoleLock.IsLocked())
					{
					if (!ScreenSaver.IsOn() && cfg.VerboseConsole)
						{
						CitWindowsNote(NULL, getmsg(1635));
						}
					}
				else
					{
					if (!TI()KeyboardBuffer.Add(c))
						{
						putchar(BELL);
						}
					}
				}
			else
				{
				fkey(c);
				}
			}
		}

	if (TI()KeyboardBuffer.IsEmpty())
		{
		// Screensaver timeout?
		ScreenSaver.Check();

		return (FALSE);
		}

	if (!(cfg.FullConLock && ConsoleLock.IsLocked()))
		{
		ScreenSaver.Update();
		}

	return (TRUE);
	}

#ifndef WINCIT
// --------------------------------------------------------------------------
// special_pressed(): TRUE if ALT, Shift, or CTRL pressed.

#define SPECIAL_KEYS	0xF00F
Bool cdecl special_pressed(void)
	{
	return (*((uint *) MK_FP(0x0000,0x0417)) & SPECIAL_KEYS);
	}
#endif

char KeyboardBufferC::Retrieve(void)
	{
	while (IsEmpty());

	char c;

	if (InsertedStrings)
		{
		c = *InsertedStrings->string;

		for (char *d = InsertedStrings->string; *d; d++)
			{
			*d = *(d + 1);
			}

		if (!*InsertedStrings->string)
			{
			deleteLLNode((void **) &InsertedStrings, 1);
			}
		}
	else
		{
		c = Buffer[Start];

		Start = (Start + 1) % KEYBUFSIZE;
		}

	Dirty = TRUE;
	return (c);
	}

Bool KeyboardBufferC::InsertString(const char *NewString)
	{
	strList *Inserted = (strList *) insertLLNode((void **) &InsertedStrings,
			1, sizeof(*InsertedStrings) + strlen(NewString));

	if (Inserted)
		{
		strcpy(Inserted->string, NewString);

		for (char *Chr = Inserted->string; *Chr; Chr++)
			{
			*Chr = filterChar(*Chr);
			}

		Dirty = TRUE;
		}

	return (!!Inserted);
	}
