// --------------------------------------------------------------------------
// Citadel: Term.CPP
//
// Terminal emulation stuff.

#include "ctdl.h"
#pragma hdrstop

#include "log.h"
#include "mci.h"
#include "cwindows.h"
#include "term.h"


// --------------------------------------------------------------------------
// Contents
//
// setTerm()		Setup the terminal
// termCap()		Does a terminal command
// putCode()		Sends the escape code to the modem


// --------------------------------------------------------------------------
// putCode(): Sends the escape code to the modem.

void putCode(const char *str)
	{
#ifndef WINCIT
	if (journalfl)
		{
		if (debug)
			{
			fprintf(journalfl, pcts, str);
			}
		}
	else
#endif
		{
		if (TI()OC.Printing && debug)
			{
#ifndef WINCIT
			fprintf(TI()OC.PrintFile, pcts, str);
#endif
			}

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

		while (*str)
			{
			TI()SerialPort.Output(*str);
			str++;
			}
		}
	}


// --------------------------------------------------------------------------
// localTermCap(): Does a local (non-networked) terminal command.

void localTermCap(const char *c)
	{
	char d;

	if (!TI()OC.MCI_goto)
		{
		d = toupper(c[0]);

		if (d == TERM_BS[0])
			{
			doBS();
			pause(5);
			}
		else if (d == TERM_IMPERV[0])
			{
			TI()UserControl.SetOutFlag(NOSTOP);
			}
		else if (d == TERM_PAUSE[0])
			{
			CITWINDOW *w = ScreenSaver.IsOn() ? NULL :
					CitWindowsMsg(NULL, getmsg(1676));

			pause(100);

			if (w)
				{
				destroyCitWindow(w, FALSE);
				}
			}
		else if (d == TERM_HANGUP[0])
			{
			CITWINDOW *w = ScreenSaver.IsOn() ? NULL :
					CitWindowsMsg(NULL, getmsg(713), getmsg(1644));

			Hangup();

			if (w)
				{
				destroyCitWindow(w, FALSE);
				}
			}
		else if (d == TERM_CONT[0])
			{
			TI()UserControl.SetContinuous(TRUE);
			}
		else if (d == TERM_MCI_ON[0])
			{
			TI()OC.UseMCI = TRUE;
			}
		else if (d == TERM_MCI_DEF[0])
			{
			TI()OC.UseMCI = cfg.mci;
			}
		}
	}


// --------------------------------------------------------------------------
// attrtoansi(): Converts attribute byte into ANSI sequence.

const char *attrtoansi(uchar attr, uchar normal)
	{
	char ansi_attribute;

// 7 6 5 4 3 2 1 0
// |  \ /  |  \ /
// B   B   I   F
// L   A   N   O
// I   C   T   R
// N   K   E   E
// K	   N

	union
		{
		struct
			{
			uint foreground : 3;
			uint intensity	: 1;
			uint background : 3;
			uint blink		: 1;
			} breakdown;

		uchar attr;
		} character;

	character.attr = attr;

	if (TI()CurrentUser->IsIBMColor()) // color
		{
		if (character.breakdown.blink)
			{
			ansi_attribute = '5';
			sprintf(TI()attrToANSIstr, getmsg(1466), ESC,
					(normal) ? getmsg(1467) : ns, ansi_attribute);
			}
		else if (character.breakdown.intensity)
			{
			ansi_attribute = '1';

			sprintf(TI()attrToANSIstr, getmsg(1468), ESC,
					(normal) ? getmsg(1467) : ns, ansi_attribute,
					iso_clr[character.breakdown.foreground] + 30,
					iso_clr[character.breakdown.background] + 40);
			}
		else
			{
			ansi_attribute = '0';

			sprintf(TI()attrToANSIstr, getmsg(1468), ESC,
					(normal) ? getmsg(1467) : ns, ansi_attribute,
					iso_clr[character.breakdown.foreground] + 30,
					iso_clr[character.breakdown.background] + 40);
			}
		}
	else // monochrome
		{
		if (character.breakdown.blink)	// Blink
			{
			ansi_attribute = '5';
			}
		else if (character.breakdown.intensity) // Boldface
			{
			ansi_attribute = '1';
			}
		else if (character.attr == 7)	// Normal
			{
			ansi_attribute = '0';
			}
		else if (character.attr == 1)	// Underline
			{
			ansi_attribute = '4';
			}
		else if (character.attr == 112) // Reverse Video
			{
			ansi_attribute = '7';
			}

		sprintf(TI()attrToANSIstr, getmsg(1469), ESC, ansi_attribute);
		}

	return (TI()attrToANSIstr);
	}


// --------------------------------------------------------------------------
// termCap(): Does a terminal command.

void termCap(const char *c)
	{
	if (!*c)
		{
		return;
		}

	char str[10];
	char string[100], *tmp1, *tmp2;

	if (toupper(*c) == 'X')
		{
		MCI(c);
		}
	else
		{
		if (!TI()OC.MCI_goto)
			{
			char ANSICode = c[0];

			if (ANSICode == TERM_RND_BACK[0])
				{
				ANSICode = 'A' + random(8);
				}

			if (ANSICode == TERM_RND_FORE[0])
				{
				ANSICode = 'a' + random(8);
				}

			// ISO COLOR support, should handle damn near any case.

			// Since the ^A0 may actualy be hilighted (or the ^A3 may not!)
			// we need to reset the hilight state if the system is flagged
			// 'dirty'.
			//
			// Also if we reset to non-hilighted we need to reoutput the
			// blinking if it was set.

			if (TI()termCapdirty && ((ANSICode >= 'A' && ANSICode <= 'H') ||
					(ANSICode >= 'a' && ANSICode <= 'h')))
				{
				sprintf(str, getmsg(1470), ESC, TI()termCaphilight ? 1 : 0);
				putCode(str);

				if (TI()termCapblink && !TI()termCaphilight)
					{
					putCode(getmsg(1471));
					}

				TI()termCapdirty = FALSE;
				}

			// Send the background
			if (ANSICode >= 'A' && ANSICode <= 'H')
				{
				if (TI()term.ibmColor)
					{
					sprintf(str, getmsg(1472), ESC, '0' + (ANSICode - 'A'));
					putCode(str);
					TI()OC.ansiattr = (uchar) ((TI()OC.ansiattr & 0x0F) |
							(iso_clr[(ANSICode - 'A')] << 4) |
							(TI()termCapblink ? 0x80 : 0));
					}
				else
					{
					if (cfg.forcetermcap)
						{
						TI()OC.ansiattr = (uchar)((TI()OC.ansiattr & 0x0F) |
								(iso_clr[(ANSICode - 'A')] << 4) |
								(TI()termCapblink ? 0x80 : 0));
						}
					}
				}
			// Send the forground
			else if (ANSICode >= 'a' && ANSICode <= 'h')
				{
				if (TI()term.ibmColor)
					{
					sprintf(str, getmsg(1473), ESC, '0' + (ANSICode - 'a'));
					putCode(str);
					TI()OC.ansiattr = (uchar)((TI()OC.ansiattr & 0xF0) |
							(iso_clr[(ANSICode - 'a')]) |
							(TI()termCaphilight ? 0x08 : 0));
					}
				else
					{
					if (cfg.forcetermcap)
						{
						TI()OC.ansiattr = (uchar)((TI()OC.ansiattr & 0xF0) |
								(iso_clr[(ANSICode - 'a')]) |
								(TI()termCaphilight ? 0x08 : 0));
						}
					}
				}
			// Normal Citadel colors
			else if (ANSICode == TERM_BLINK[0])
				{
				if (*TI()term.blink)
					{
					putCode(TI()term.blink);
					if (!TI()loggedIn || (!cfg.concolors) || (cfg.concolors == 1 && (!onConsole || SerialPort.HaveCarrier())))
						{
						TI()OC.ansiattr = (uchar)(TI()OC.ansiattr | 128);
						}
					else
						{
						TI()OC.ansiattr = TI()CurrentUser->GetAttribute(ATTR_BLINK) == 128 ?
								(uchar)(TI()OC.ansiattr | 128) :
								TI()CurrentUser->GetAttribute(ATTR_BLINK);
						}

					TI()termCapblink = TRUE;
					}
				else
					{
					if (cfg.forcetermcap)
						{
						TI()OC.ansiattr = (uchar)(TI()OC.ansiattr | 128);
						TI()termCapblink = TRUE;
						}
					}
				}
			else if (ANSICode == TERM_REVERSE[0])
				{
				if (*TI()term.inverse)
					{
					putCode(TI()term.inverse);
					if (!TI()loggedIn || (!cfg.concolors) || (cfg.concolors == 1 && (!onConsole || SerialPort.HaveCarrier()) ))
						{
						TI()OC.ansiattr = cfg.wattr;
						}
					else
						{
						TI()OC.ansiattr = TI()CurrentUser->GetAttribute(ATTR_REVERSE);
						}
					TI()termCapdirty = TRUE;
					}
				else
					{
					if (cfg.forcetermcap)
						{
						TI()OC.ansiattr = cfg.wattr;
						TI()termCapdirty = TRUE;
						}
					}
				}
			else if (ANSICode == TERM_BOLD[0])
				{
				if (*TI()term.bold)
					{
					putCode(TI()term.bold);
					if (!TI()loggedIn || (!cfg.concolors) ||
							(cfg.concolors == 1 && (!onConsole || SerialPort.HaveCarrier())))
						{
						TI()OC.ansiattr = cfg.cattr;
						}
					else
						{
						TI()OC.ansiattr = TI()CurrentUser->GetAttribute(ATTR_BOLD);
						}

					TI()termCaphilight = TRUE;
					TI()termCapdirty = FALSE;
					}
				else
					{
					if (cfg.forcetermcap)
						{
						TI()OC.ansiattr = cfg.cattr;
						TI()termCaphilight = TRUE;
						TI()termCapdirty = FALSE;
						}
					}
				}
			else if (ANSICode == TERM_UNDERLINE[0])
				{
				if (*TI()term.under)
					{
					putCode(TI()term.under);
					if (!TI()loggedIn || (!cfg.concolors) ||
							(cfg.concolors == 1 && (!onConsole || SerialPort.HaveCarrier())))
						{
						TI()OC.ansiattr = cfg.uttr;
						}
					else
						{
						TI()OC.ansiattr = TI()CurrentUser->GetAttribute(ATTR_UNDERLINE);
						}

					TI()termCapdirty = TRUE;
					}
				else
					{
					if (cfg.forcetermcap)
						{
						TI()OC.ansiattr = cfg.uttr;
						TI()termCapdirty = TRUE;
						}
					}
				}
			else if (ANSICode == TERM_FMT_OFF[0])
				{
				TI()OC.Formatting = FALSE;
				}
			else if (ANSICode == TERM_FMT_ON[0])
				{
				TI()OC.Formatting = TRUE;
				}
			else if (ANSICode == TERM_USERNAME[0])
				{
				if (MCIRecursionChecker.Start(MCI_USERNAME))
					{
					if (TI()OC.UseMCI)
						{
						if (TI()loggedIn)
							{
							sprintf(string, pcts,
									special_deansi(TI()CurrentUser->GetName(), TERM_USERNAME));
							MCI_DispOrAsgn(string, TI()MCI_str);
							}
						else
							{
							MCI_DispOrAsgn(getmsg(100), TI()MCI_str);
							}
						}
					else
						{
						mFormat(cfg.mci_name);
						}

					MCIRecursionChecker.End(MCI_USERNAME);
					}
				}
			else if (ANSICode == TERM_FIRSTNAME[0])
				{
				if (MCIRecursionChecker.Start(MCI_FIRSTNAME))
					{
					if (TI()OC.UseMCI)
						{
						if (TI()loggedIn)
							{
							strcpy(string, TI()CurrentUser->GetName());

							if (strchr(string, ' '))
								{
								*strchr(string, ' ') = 0;

								if (
										SameString(deansi(string), getmsg(131)) ||
										SameString(deansi(string), getmsg(132)) ||
										SameString(deansi(string), getmsg(133))
									)
									{
									strcpy(string, TI()CurrentUser->GetName());
									}
								else
									{
									for (tmp1 = strchr(string, 0), tmp2 = tmp1 + 1;
											*tmp2; tmp2++)
										{
										if (*tmp2 == CTRL_A)
											{
											*tmp1++ = *tmp2++;
											*tmp1++ = *tmp2;
											}
										}

									*tmp1 = 0;
									}
								}

							MCI_DispOrAsgn(string, TI()MCI_str);
							}
						else
							{
							MCI_DispOrAsgn(getmsg(100), TI()MCI_str);
							}

						}
					else
						{
						mFormat(cfg.mci_firstname);
						}

					MCIRecursionChecker.End(MCI_FIRSTNAME);
					}
				}
			else if (ANSICode == TERM_DATE[0])
				{
				if (MCIRecursionChecker.Start(MCI_DATE))
					{
					if (TI()OC.UseMCI)
						{
						strftime(string, 79,
								TI()CurrentUser->GetVerboseDateStamp(), 0l);

						MCI_DispOrAsgn(string, TI()MCI_str);
						}
					else
						{
						mFormat(cfg.mci_date);
						}

					MCIRecursionChecker.End(MCI_DATE);
					}
				}
			else if (ANSICode == TERM_TIME[0])
				{
				if (MCIRecursionChecker.Start(MCI_TIME))
					{
					if (TI()OC.UseMCI)
						{
						strftime(string, 79,
							TI()CurrentUser->GetDateStamp(), 0l);

						MCI_DispOrAsgn(string, TI()MCI_str);
						}
					else
						{
						mFormat(cfg.mci_time);
						}

					MCIRecursionChecker.End(MCI_TIME);
					}
				}
			else if (toupper(ANSICode) == TERM_POOP[0])
				{
				if (MCIRecursionChecker.Start(MCI_POOP))
					{
					if (TI()OC.UseMCI)
						{
						sprintf(string, pctld, TI()CurrentUser->GetPoopcount());
						MCI_DispOrAsgn(string, TI()MCI_str);
						}
					else
						{
						mFormat(cfg.mci_poop);
						}

					MCIRecursionChecker.End(MCI_POOP);
					}
				}
			else if (ANSICode != TERM_SPLCHK_OFF[0] &&
					ANSICode != TERM_SPLCHK_ON[0])
				{
				if (*TI()term.normal)
					{
					putCode(TI()term.normal);

					if (!TI()loggedIn || !cfg.concolors ||
							(cfg.concolors == 1 &&
							(!onConsole || SerialPort.HaveCarrier())))
						{
						TI()OC.ansiattr = cfg.attr;
						}
					else
						{
						TI()OC.ansiattr =
								TI()CurrentUser->GetAttribute(ATTR_NORMAL);
						}

					TI()termCaphilight = FALSE;
					TI()termCapblink = FALSE;
					TI()termCapdirty = TRUE;
					}
				else
					{
					if (cfg.forcetermcap)
						{
						TI()OC.ansiattr = cfg.attr;
						TI()termCaphilight = FALSE;
						TI()termCapblink = FALSE;
						TI()termCapdirty = TRUE;
						}
					}
				}
			}
		}
	}
