// --------------------------------------------------------------------------
// Citadel: Misc2.CPP
//
// Overlayed miscellaneous stuff that is used often; compare with Misc3.CPP.

#include "ctdl.h"
#pragma hdrstop

#include "cwindows.h"
#include "room.h"
#include "tallybuf.h"
#include "libovl.h"
#include "log.h"
#include "hall.h"
#include "blurbs.h"
#include "cfgfiles.h"
#include "term.h"


// --------------------------------------------------------------------------
// Contents
//
// offhook()		sysop fn to take modem off hook
// greeting()		System-entry blurb etc
// logo()			prints out system logo at startup
// editBorder() 	edit a border line.
// digitbaud()		Calculates digit baud rate (0-4) from number (300+)


// --------------------------------------------------------------------------
// greeting(): gives system-entry blurb, etc.

void greeting(void)
	{
	TI()MRO.Verbose = FALSE;

	if (TI()loggedIn)
		{
		terminate(FALSE);
		}

	TI()OC.Echo = BOTH;
	setio();

	setdefaultconfig(FALSE);

	pause(10);

	cls(SCROLL_SAVE);

	doccr();

	StatusLine.Update();

	if (TI()modStat)
		{
		if (cfg.connectwait)
			{
			CITWINDOW *w = ScreenSaver.IsOn() ? NULL :
					CitWindowsMsg(NULL, getmsg(1642));

			pause(cfg.connectwait * 100);

			if (w)
				{
				destroyCitWindow(w, FALSE);
				}
			}

		TI()SerialPort.Flush();
		}

	// make sure we want to talk to this baud rate
	if (TI()modStat && (TI()ModemSpeed < cfg.minbaud))
		{
		dispBlb(B_TOOLOW);

		CITWINDOW *w = ScreenSaver.IsOn() ? NULL :
				CitWindowsMsg(NULL, getmsg(1643));

		Hangup();
		pause(200);

		if (w)
			{
			destroyCitWindow(w, FALSE);
			}
		}
	else
		{
		// set terminal
		autoansi();

		TI()UserControl.SetOutFlag(OUTOK);

		if (TI()modStat || debug)
			{
			hello();
			doCR();
			}

		TI()UserControl.SetOutFlag(OUTOK);

		mPrintfCR(getmsg(749), cfg.nodeTitle, cfg.nodeRegion, cfg.nodeCountry);

		mPrintfCR(getmsg(750), cfg.softverb, cfg.softverb[0] ? spc : ns,
				programName, version);

		mPrintf(pcts, Author);

#if VERSION != RELEASE
		doCR();
		CRCRmPrintfCR(" 2=== 1NOTE02 ===0");
#ifndef NDEBUG
		CRmPrintf("This BBS is running pre-release software.  Because this is a test version ");
		mPrintf("of the software, it has extra code to assure that things are running as ");
		mPrintf("they should.  This may cause a noticeable slow-down in the operation of ");
		mPrintf("the board.  Also, because this is pre-release software, it may contain ");
		mPrintf("bugs that could cause a loss of data.  This is not likely, but it is best ");
		mPrintfCR("to be aware of potential problems before they surprise you.");
#else
		mPrintf("This BBS is running pre-release software.  Because this is pre-release ");
		mPrintf("software, it may contain ");
		mPrintf("bugs that could cause a loss of data.  This is not likely, but it is best ");
		mPrintfCR("to be aware of potential problems before they surprise you.");
#endif
		CRmPrintfCR(" 2=== 1NOTE02 ===0");
#endif


		char dtstr[80];
		strftime(dtstr, 79, cfg.vdatestamp, 0l);

		doCR();
		CRmPrintf(pcts, dtstr);

		if (!cfg.forcelogin)
			{
			CRmPrintf(getmsg(760));
			CRmPrintf(getmsg(761));
			CRmPrintf(getmsg(762));
			}

		TI()thisRoom = LOBBY;
		LoadCurrentRoom(LOBBY);

		const ulong messages = GetRoomMessages(TI()thisRoom);

		CRmPrintfCR(getmsg(1542), ltoac(messages),
			(messages == 1) ? cfg.Lmsg_nym : cfg.Lmsgs_nym);

		TI()SerialPort.Flush();
		}
	}


// --------------------------------------------------------------------------
// editBorder(): Edit a border line.

void editBorder(void)
	{
	uint i;

	SetDoWhat(ENTERBORDER);

	doCR();
	doCR();

	if (!cfg.borders)
		{
		mPrintfCR(getmsg(754));
		return;
		}

	TI()UserControl.SetOutFlag(OUTOK);

	for (i = 0; i < cfg.maxborders; i++)
		{
		mPrintf(getmsg(755), i + 1);
		mPrintf(spc);

		if (borders[i * (BORDERSIZE + 1)])
			{
			mPrintf(pcts, &(borders[i * (BORDERSIZE + 1)]));
			termCap(TERM_NORMAL);
			}
		else
			{
			mPrintf(getmsg(756));
			}

		doCR(2);
		}

	i = (int) getNumber(getmsg(757), 0L, (long) cfg.maxborders, 0L, FALSE, NULL);

	if (i)
		{
		CRmPrintf(getmsg(763), i);

		if (borders[(i - 1) * (BORDERSIZE + 1)])
			{
			mPrintf(pcts, &(borders[(i - 1) * (BORDERSIZE + 1)]));
			termCap(TERM_NORMAL);
			}
		else
			{
			mPrintf(getmsg(756));
			}

		char stuff[BORDERSIZE + 1];

		doCR();
		getString(getmsg(758), stuff, BORDERSIZE, FALSE, ECHO, ns);

		if (*stuff)
			{
			CopyStringToBuffer(&(borders[(i - 1) * (BORDERSIZE + 1)]), stuff,
					BORDERSIZE);
			normalizeString(&(borders[(i - 1) * (BORDERSIZE + 1)]));

			writeBordersDat();
			}
		}
	}


// --------------------------------------------------------------------------
// digitbaud(): Calculates PortSpeedE baud rate from number
//
// Input:
//	bigbaud: The baud to find the PortSpeedE of. This can be in PortSpeedE
//		format already, or in 300/600/1200... format.
//
// Return value:
//	PortSpeedE format of bigbaud's rate.
//
// Notes:
//	If bigbaud could not be understood, this returns PS_ERROR.

PortSpeedE digitbaud(long bigbaud)
	{
	if (bigbaud < PS_NUM - 1)
		{
		// This is a little bit of a kludge to keep people from having to
		// change data files when 600 baud was introduced. Pretty ugly, huh?

		if (bigbaud > 0)
			{
			bigbaud++;
			}

		return ((PortSpeedE) bigbaud);
		}
	else
		{
		for (PortSpeedE i = (PortSpeedE) 0; i < PS_NUM;
				i = (PortSpeedE) (i + 1))
			{
			if (bigbaud == bauds[i])
				{
				return (i);
				}
			}

		return (PS_ERROR);
		}
	}


// --------------------------------------------------------------------------
// digitConnectbaud(): Calculates ModemSpeedE baud rate from number
//
// Input:
//	bigbaud: The baud to find the ModemSpeedE of. This can be in ModemSpeedE
//		format already, or in 300/600/1200... format.
//
// Return value:
//	ModemSpeedE format of bigbaud's rate.
//
// Notes:
//	If bigbaud could not be understood, this returns MS_ERROR.

ModemSpeedE digitConnectbaud(long bigbaud)
	{
	if (bigbaud < MS_NUM - 1)
		{
		// This is a little bit of a kludge to keep people from having to
		// change data files when 600 baud was introduced. Pretty ugly, huh?

		if (bigbaud > 0)
			{
			bigbaud++;
			}

		return ((ModemSpeedE) bigbaud);
		}
	else
		{
		for (ModemSpeedE i = (ModemSpeedE) 0; i < MS_NUM;
				i = (ModemSpeedE) (i + 1))
			{
			if (bigbaud == connectbauds[i])
				{
				return (i);
				}
			}

		return (MS_ERROR);
		}
	}


// --------------------------------------------------------------------------
// offhook(): Take the modem off-hook.

void offhook(void)
	{
	CITWINDOW *w = CitWindowsMsg(NULL, getmsg(1611));

	Initport();

	if (w)
		{
		destroyCitWindow(w, FALSE);
		}

	outstring(cfg.offhookstr);
	outstring(br);
	TI()SerialPort.Disable();
	}

void displayOnOff(const char *string, int value)
	{
	mPrintf(string, value ? getmsg(549) : getmsg(550));
	}

void displayYesNo(const char *string, int value)
	{
	mPrintf(string, value ? getmsg(521) : getmsg(522));
	}

Bool changeOnOff(const char *str, Bool value)
	{
	mPrintfCR(str, value ? getmsg(550) : getmsg(549));
	return (!value);
	}

Bool changeYesNo(const char *str, Bool value)
	{
	mPrintfCR(str, value ? getmsg(522) : getmsg(521));
	return (!value);
	}

void cdecl nullFunc(void)
	{
	}

void cyclesignature(void)
	{
	FILE *fBuf;
	char line[256];
	char *words[256];
	Bool found = FALSE;
	char path[80];

	// there is only one signature line, so get out and use it
	if (cfg.sig_current_pos == LONG_MAX)
		{
		return;
		}

	sprintf(path, sbs, cfg.homepath, citfiles[C_CONFIG_CIT]);

	if ((fBuf = fopen(path, FO_R)) == NULL) // ASCII mode
		{
		return;
		}

	fseek(fBuf, cfg.sig_current_pos, SEEK_SET);

	for (; !found;)
		{
		// if end of file cycle back up
		if (fgets(line, 254, fBuf) == NULL)
			{
			fseek(fBuf, cfg.sig_first_pos, SEEK_SET);
			continue;
			}

		cfg.sig_current_pos = ftell(fBuf);

		if (line[0] != '#')
			{
			continue;
			}

		if (strnicmp(line, getmsg(1543), strlen(getmsg(1543))) != SAMESTRING)
			{
			// okay we've gone past the last signature, cycle back up
			fseek(fBuf, cfg.sig_first_pos, SEEK_SET);
			continue;
			}
		else
			{
			found = TRUE;
			}

		parse_it(words, line);

		CopyStringToBuffer(cfg.nodeSignature, words[1],
				sizeof(cfg.nodeSignature) - 1);

		cfg.sig_current_pos = ftell(fBuf);
		}

	fclose(fBuf);
	}


// --------------------------------------------------------------------------
// theAlgorithm(): Determine's users ability to get to a room.
//
// Input:
//	r_slot rm: The room we wonder about
//	h_slot hl: pass starting hall (usually TI()thisHall)
//	Bool exclude: TRUE means that excluded rooms should be considered
//		inaccessible
//
// Return value:
//	TRUE: The room can be accessed
//	FALSE: The room cannot be accessed
//
// Notes:
//	This is a recursive function.
//	If this function runs out of memory, it returns FALSE for almost all rooms

Bool theAlgorithm(r_slot rm, h_slot hl, Bool exclude)
	{
	assert(rm >= -1);
	assert(rm < cfg.maxrooms);
	assert(hl >= -1);
	assert(hl < cfg.maxhalls);

	static BitBag *hallCheck;
	Bool lowest = FALSE;

	if (!hallCheck)
		{
		if (rm == TI()thisRoom || (rm == LOBBY && cfg.subhubs == 4))
			{
			return (TRUE);
			}

		if ((exclude && TI()CurrentUser->IsRoomExcluded(rm)) ||
				!TI()CurrentUser->CanAccessRoom(rm, FALSE, FALSE))
			{
			return (FALSE);
			}

		hallCheck = new BitBag(cfg.maxhalls);

		lowest = TRUE;
		}

	if (!hallCheck)
		{
		mPrintf(getmsg(188), getmsg(111));
		}
	else
		{
		if (hallCheck->BitTest(hl))
			{
			return (FALSE);
			}
		else
			{
			hallCheck->BitSet(hl, TRUE);

			if (roominhall(rm, hl))
				{
				delete hallCheck;
				hallCheck = NULL;

				return (TRUE);
				}

			for (r_slot rmCnt = 0; rmCnt < cfg.maxrooms; rmCnt++)
				{
				if (TI()CurrentUser->CanAccessRoom(rmCnt, FALSE, FALSE) &&
						(!exclude || !TI()CurrentUser->IsRoomExcluded(rmCnt)) &&
						roominhall(rmCnt, hl) && iswindow(rmCnt))
					{
					for (h_slot hlCnt = 0; hlCnt < cfg.maxhalls; hlCnt++)
						{
						if (HallData->GetEntry(hlCnt)->IsInuse() &&
								TI()CurrentUser->CanAccessHall(hlCnt) && HallData->GetEntry(
								hlCnt)->IsWindowedIntoHall(rmCnt))
							{
							if (theAlgorithm(rm, hlCnt, exclude))
								{
								return (TRUE);
								}
							}
						}
					}
				}
			}
		}

	if (lowest && hallCheck)
		{
		delete hallCheck;
		hallCheck = NULL;
		}

	return (FALSE);
	}


// --------------------------------------------------------------------------
// theOtherAlgorithm(): Determine user's ability to get to each room on the
//	system
//
// Input:
//	Bool *rooms: A porinter to a cfg.maxrooms array of Bools
//	h_slot hl: The hall to start in; usually TI()thisHall
//	Bool exclude: TRUE means that excluded rooms should be considered
//		inaccessible
//
// Output:
//	Bool *rooms: Each member of the array is set to FALSE (user cannot
//		access the corresponding room) or TRUE (user can access it)

void theOtherAlgorithm(Bool *rooms, h_slot hl, Bool exclude)
	{
	assert(hl >= -1);
	assert(hl < cfg.maxhalls);

	static BitBag *hallCheck;
	Bool lowest = FALSE;

	if (!hallCheck)
		{
		hallCheck = new BitBag(cfg.maxhalls);
		memset(rooms, 0, sizeof(Bool) * cfg.maxrooms);

		lowest = TRUE;
		}

	if (!hallCheck)
		{
		mPrintf(getmsg(188), getmsg(362));
		}
	else
		{
		if (hallCheck->BitTest(hl))
			{
			return;
			}
		else
			{
			hallCheck->BitSet(hl, TRUE);

			for (r_slot rmCnt = 0; rmCnt < cfg.maxrooms; rmCnt++)
				{
				if (!rooms[rmCnt] && TI()CurrentUser->CanAccessRoom(rmCnt, FALSE, FALSE) &&
						roominhall(rmCnt, hl) &&
						(!exclude || !TI()CurrentUser->IsRoomExcluded(rmCnt)))
					{
					rooms[rmCnt] = TRUE;

					if (iswindow(rmCnt))
						{
						for (h_slot hlCnt = 0; hlCnt < cfg.maxhalls; hlCnt++)
							{
							if (HallData->GetEntry(hlCnt)->IsInuse() &&
									TI()CurrentUser->CanAccessHall(hlCnt) && HallData->
									GetEntry(hlCnt)->IsWindowedIntoHall(rmCnt))
								{
								theOtherAlgorithm(rooms, hlCnt, exclude);
								}
							}
						}
					}
				}
			}
		}

	if (lowest && hallCheck)
		{
		delete hallCheck;
		hallCheck = NULL;
		}
	}

typedef struct
	{
	void *ptr;
	char srch[80];
	} memoryDumpData;

#define nb(a) (a && (a != '\t') ? a : ' ')      // don't want tab expansion

typedef enum
	{
	CTRL_GETADDR,	CTRL_SEARCH,
	};

static Bool mdHandler(EVENT evt, long param, int more, CITWINDOW *wnd)
	{
	switch (evt)
		{
		case EVT_DRAWINT:
			{
			if (buildClipArray(wnd))
				{
				memoryDumpData *md = (memoryDumpData *) wnd->LocalData;
				SRECT r = wnd->extents;
				int i, j;
				uint seg, ofs;
				seg = FP_SEG(md->ptr);
				ofs = FP_OFF(md->ptr);

				for (i = 0, j = r.top + 1; j < r.bottom; i++, j++)
					{
					char str[128];
					uchar val[16];
					int x;

					for (x = 0; x < 16; x++)
						{
						val[x] = *((uchar *) MK_FP(seg, ofs++));
						}

					sprintf(str, getmsg(1544),
							MK_FP(seg, ofs - 16),
							val[0], val[1], val[2], val[3],
							val[4], val[5], val[6], val[7],
							val[8], val[9], val[10], val[11],
							val[12], val[13], val[14], val[15],
							nb(val[0]), nb(val[1]), nb(val[2]), nb(val[3]),
							nb(val[4]), nb(val[5]), nb(val[6]), nb(val[7]),
							nb(val[8]), nb(val[9]), nb(val[10]), nb(val[11]),
							nb(val[12]), nb(val[13]), nb(val[14]), nb(val[15])
							);

					CitWindowOutStr(wnd, 1, i + 1, str, cfg.attr);

					for (x = strlen(str) + 1; x < r.right - r.left; x++)
						{
						CitWindowOutChr(wnd, x, i + 1, ' ', cfg.attr);
						}
					}

				freeClipArray();
				}

			break;
			}

		case EVT_CTRLRET:
			{
			CONTROLINFO *ci = (CONTROLINFO *) param;

			switch (ci->id)
				{
				case CTRL_GETADDR:
					{
					memoryDumpData *md = (memoryDumpData *) wnd->LocalData;
					label tmp;
					uint seg, ofs;

					strcpy(tmp, (char *) ci->ptr);

					if (strchr(tmp, ':'))
						{
						*strchr(tmp, ':') = 0;
						}

					seg = (int) strtol(tmp, NULL, 16);

					if (strchr((char *) ci->ptr, ':'))
						{
						strcpy(tmp, strchr((char *) ci->ptr, ':') + 1);
						ofs = (int) strtol(tmp, NULL, 16);
						}
					else
						{
						ofs = 0;
						}

					md->ptr = MK_FP(seg, ofs);
					break;
					}

				case CTRL_SEARCH:
					{
					char *srch = (char *) ci->ptr;
					uint rseg, rofs;

					rseg = FP_SEG(srch) + (FP_OFF(srch) >> 4);
					rofs = FP_OFF(srch) & 0x0f;
					srch = (char *) MK_FP(rseg, rofs);

					if (*srch)
						{
						memoryDumpData *md = (memoryDumpData *) wnd->LocalData;
						uint seg, offstart = FP_OFF(md->ptr) + 1;
						uint len = strlen(srch);
						Bool found = FALSE;
						CITWINDOW *w;

						w = CitWindowsMsg(wnd, getmsg(1545));

						for (seg = FP_SEG(md->ptr); !found && seg <= 0xf000;
								seg += 0x1000)
							{
							uint ofs;

							for (ofs = offstart; !found && ofs < 0xffff; ofs++)
								{
								char *m, *s;
								uint i;
								rseg = seg + (ofs >> 4);
								rofs = ofs & 0x0f;

								m = (char *) MK_FP(rseg, rofs);
								s = srch;

								if (m != s) 	// don't look at search string
									{
									for (i = 0; i < len; i++)
										{
										if (toupper(*(s++)) != toupper(*(m++)))
											{
											break;
											}
										}

									if (i == len)
										{
										// found it...
										md->ptr = MK_FP(rseg, rofs);
										found = TRUE;
										}
									}
								}

							offstart = 0;

							if (seg == 0xf000)
								{
								break;
								}
							}

						if (w)
							{
							destroyCitWindow(w, FALSE);
							}

						if (found)
							{
							(wnd->func)(EVT_DRAWINT, 0, 0, wnd);
							}
						else
							{
							CitWindowsError(wnd, getmsg(1546));
							}

						strcpy(md->srch, srch);
						}

					break;
					}
				}

			break;
			}

		case EVT_INKEY:
			{
			memoryDumpData *md = (memoryDumpData *) wnd->LocalData;

			if (md)
				{
				if (param < 256)
					{
					switch (toupper((int)param))
						{
						case 'G':       // goto
							{
							char df[12];

							sprintf(df, getmsg(1547), md->ptr);

							CitWindowsGetString(getmsg(1548), 11,
									df, wnd, CTRL_GETADDR, NULL, FALSE);

							return (TRUE);
							}

						case 'R':       // refresh
							{
							(wnd->func)(EVT_DRAWINT, 0, 0, wnd);
							return (TRUE);
							}

						case 'S':       // search
							{
							memoryDumpData *md = (memoryDumpData *) wnd->LocalData;

							CitWindowsGetString(getmsg(1549), 80,
									md->srch, wnd, CTRL_SEARCH, NULL, FALSE);

							return (TRUE);
							}
						}
					}
				else
					{
					switch (param >> 8)
						{
						case CTL_CURS_HOME:
							{
							md->ptr = NULL;
							(wnd->func)(EVT_DRAWINT, 0, 0, wnd);
							return (TRUE);
							}

						case CURS_UP:
							{
							md->ptr = MK_FP(FP_SEG(md->ptr),
									FP_OFF(md->ptr) - 16);
							(wnd->func)(EVT_DRAWINT, 0, 0, wnd);
							return (TRUE);
							}

						case CURS_DOWN:
							{
							md->ptr = MK_FP(FP_SEG(md->ptr),
									FP_OFF(md->ptr) + 16);
							(wnd->func)(EVT_DRAWINT, 0, 0, wnd);
							return (TRUE);
							}

						case CURS_LEFT:
							{
							md->ptr = MK_FP(FP_SEG(md->ptr),
									FP_OFF(md->ptr) - 1);
							(wnd->func)(EVT_DRAWINT, 0, 0, wnd);
							return (TRUE);
							}

						case CURS_RIGHT:
							{
							md->ptr = MK_FP(FP_SEG(md->ptr),
									FP_OFF(md->ptr) + 1);
							(wnd->func)(EVT_DRAWINT, 0, 0, wnd);
							return (TRUE);
							}

						case PGUP:
							{
							md->ptr = MK_FP(FP_SEG(md->ptr) - 1,
									FP_OFF(md->ptr));
							(wnd->func)(EVT_DRAWINT, 0, 0, wnd);
							return (TRUE);
							}

						case PGDN:
							{
							md->ptr = MK_FP(FP_SEG(md->ptr) + 1,
									FP_OFF(md->ptr));
							(wnd->func)(EVT_DRAWINT, 0, 0, wnd);
							return (TRUE);
							}

						case CURS_HOME:
							{
							md->ptr = MK_FP(FP_SEG(md->ptr) - 16,
									FP_OFF(md->ptr));
							(wnd->func)(EVT_DRAWINT, 0, 0, wnd);
							return (TRUE);
							}

						case CURS_END:
							{
							md->ptr = MK_FP(FP_SEG(md->ptr) + 16,
									FP_OFF(md->ptr));
							(wnd->func)(EVT_DRAWINT, 0, 0, wnd);
							return (TRUE);
							}
						}
					}
				}

			return (defaultHandler(evt, param, more, wnd));
			}

		default:
			{
			return (defaultHandler(evt, param, more, wnd));
			}
		}

	return (TRUE);
	}

void memoryDump(void)
	{
	memoryDumpData *ld;

	if ((ld = new memoryDumpData) != NULL)
		{
		CITWINDOW *w;
		WINDOWFLAGS flags;
		SRECT rect;

		memset(&flags, 0, sizeof(flags));

		flags.visible = TRUE;
		flags.showTitle = TRUE;
		flags.moveable = TRUE;
		flags.minimize = TRUE;
		flags.maximize = TRUE;
		flags.resize = TRUE;
		flags.close = TRUE;

		rect.top = 0;
		rect.left = 0;
		rect.bottom = scrollpos / 2;
		rect.right = conCols - 1;

		ld->ptr = NULL;
		*ld->srch = 0;

		if ((w = makeCitWindow(mdHandler, NULL, getmsg(1550), flags,
				rect, ld, FALSE)) != NULL)
			{
			setFocus(w);
			}
		}
	}
