// --------------------------------------------------------------------------
// Citadel: Misc.CPP
//
// Garbage dump: if it ain't elsewhere, it's here.

#include "ctdl.h"
#pragma hdrstop

#include "log.h"
#include "net.h"
#include "events.h"
#include "aplic.h"
#include "miscovl.h"


// --------------------------------------------------------------------------
// Contents
//
// HaveConnectionToUser()	do we have a connection, either console or modem?
// filexists()				Checks for existence of a file
// hash()					Make an int out of a string
// changedir()				changes curent drive and directory
// changedisk() 			change to another drive
// ltoac()					change a long into a number with ','s in it


// --------------------------------------------------------------------------
// filexists(): Does the file exist?

Bool filexists(const char *filename)
	{
	if (debug)
		{
		doccr();
		cPrintf(getdbmsg(53), filename);
		}

	Bool RetVal = access(filename, 4) == 0;

	if (debug)
		{
		if (RetVal)
			{
			cPrintf(getdbmsg(54));
			}
		else
			{
			cPrintf(getdbmsg(55));
			}

		doccr();
		}

	return (RetVal);
	}


// --------------------------------------------------------------------------
// hash(): Make an int out of a string.

uint hash(const char *string)
	{
	const char *str;
	int h, shift;

	assert(string != NULL);

	str = deansi(string);

	for (h = shift = 0; *str; shift = (shift + 1) & 7, str++)
		{
		h ^= (toupper(*str)) << shift;
		}

	return (h);
	}


// --------------------------------------------------------------------------
// changedisk(): Change to another drive.

static void changedisk(char disk)
	{
	disk = (char) toupper(disk);

	union REGS REG;

	assert(disk >= 'A' && disk <= 'Z');

	REG.h.ah = 0x0E;		// select drive

	REG.h.dl = (uchar) (disk - 'A');

	intdos(&REG, &REG);
	}


// --------------------------------------------------------------------------
// changedir(): Changes curent drive and directory.

Bool changedir(const char *path)
	{
	assert(path);

	if (debug)
		{
		doccr();
		cPrintf(getdbmsg(47), path);
		}

	Bool RetVal = TRUE;

	if (*path)
		{
		if (path[1] == ':')
			{
			changedisk(path[0]);
			}

		if (chdir(path) == -1)
			{
			RetVal = FALSE;
			}
		}
	else
		{
		RetVal = FALSE;
		}

	if (debug)
		{
		cPrintf(RetVal ? getdbmsg(13) : getdbmsg(14));
		doccr();
		}

	return (RetVal);
	}


// --------------------------------------------------------------------------
// ltoac(): Change a long into a number with commas in it.

const char *ltoac(long Number)
	{
	if (TI()CurrentUser && TI()CurrentUser->IsRoman())
		{
		return(RomanOutput(Number));
		}
	else
		{
		static char Result[15];
		const Bool Negative = (Number < 0);
		int Pos = 0;

		if (Negative)
			{
			Number = -Number;
			}

		int Counter = -1;

		while (Number > 0)
			{
			if (++Counter == 3)
				{
				if (!TI()CurrentUser || TI()CurrentUser->IsViewCommas())
					{
					Result[Pos++] = getmsg(1383)[0];	// ','
					}

				Counter = 0;
				}

			Result[Pos++] = char (((char) (Number % 10)) + '0');
			Number /= 10;
			}

		if (!Pos)
			{
			Result[Pos++] = '0';
			}

		if (Negative)
			{
			Result[Pos++] = '-';
			}

		Result[Pos] = 0;

		strrev(Result);

		return (Result);
		}
	}


// --------------------------------------------------------------------------
// HaveConnectionToUser(): Do we have a connection, either console or modem

Bool HaveConnectionToUser(void)
	{
	return ((onConsole || (SerialPort.HaveCarrier() && TI()modStat)) && !TI()ExitToMsdos);
	}

// perhaps should be moved. i'll worry about that later...
void getfpath(char *path)
	{
	path[0] = getdisk() + 'A';
	path[1] = ':';
	path[2] = '\\';
	getcurdir(0, path + 3);
	}

#ifndef WINCIT
void doEvent(CITEVENTS eventNum, void (*InitScript)(ScriptInfoS *si))
	{
	assert(eventNum < EVT_NUMEVTS && eventNum >= 0);

	fpath old;
	Bool ran = FALSE;

	for (events *theEvent = eventList; theEvent;
			theEvent = (events *) getNextLL(theEvent))
		{
		if (theEvent->type == eventNum)
			{
			if (!ran)
				{
				getfpath(old);
				ran = TRUE;
				}

			// This is a bit cheezy...
			if (*theEvent->line == '*')
				{
				runScript(theEvent->line + 1, InitScript);
				}
			else
				{
				RunApplication(theEvent->line, NULL, FALSE, TRUE);
				}
			}
		}

	if (ran)
		{
		changedir(old);
		}

	for (intEvents *theIntEvent = intEventList; theIntEvent;
			theIntEvent = (intEvents *) getNextLL(theIntEvent))
		{
		if (theIntEvent->type == eventNum)
			{
			(*theIntEvent->func)(eventNum);
			}
		}
	}
#endif

int rlmEvent(int type, long param1, long param2)
	{
	RLMfuncs *fCur;
	int toRet = 0;

	for (fCur = RLMdo; !toRet && fCur; fCur = (RLMfuncs *) getNextLL(fCur))
		{
		if (fCur->type == type)
			{
			Bool run = TRUE;

			switch (type)
				{
				case LT_KEYSTROKE:
				case LT_DIALOUTKEY:
					{
					if (fCur->moreData && fCur->moreData != param1)
						{
						run = FALSE;
						}

					break;
					}
				}

			if (run)
				{
				toRet = callRLM(fCur->theRLM, fCur->offset, param1, param2);
				}
			}
		}

	return (toRet);
	}

void ShowHelpMessage(int msgNumber)
	{
	if (!TI()CurrentUser->IsExpert())
		{
		CRmPrintfCR(getmsg(msgNumber));
		}
	}

void ClearMsgReadOptions(MsgReadOptions *MROaddr)
	{
	MROaddr->Number 		= FALSE;
	MROaddr->Headerscan 	= FALSE;
	MROaddr->Date			= FALSE;
	MROaddr->DatePrevious	= FALSE;
	MROaddr->All			= FALSE;
	MROaddr->Verbose		= TI()CurrentUser->IsAutoVerbose();
	MROaddr->Reverse		= FALSE;
	MROaddr->CheckDate		= 0;
	MROaddr->MessageNumber	= 0;
	MROaddr->DotoMessage	= NO_SPECIAL;
	}

int max(int a, int b)
	{
	return ((a > b) ? a : b);
	}

int min(int a, int b)
	{
	return ((a < b) ? a : b);
	}

long max(long a, long b)
	{
	return ((a > b) ? a : b);
	}

long min(long a, long b)
	{
	return ((a < b) ? a : b);
	}

Bool isNumeric(const char *str)
	{
	for (; *str; str++)
		{
		if (!isdigit(*str))
			{
			return (FALSE);
			}
		}

	return (TRUE);
	}

void SetDoWhat(dowhattype NewDoWhat)
	{
	if (NewDoWhat == READUSERLOG || NewDoWhat == READMESSAGE ||
			NewDoWhat == READDIR || NewDoWhat == READZIP ||
			NewDoWhat == READINFO)
		{
		TI()UserControl.SetCanR(TRUE);
		}

	TI()DoWhat = NewDoWhat;
	StatusLine.Update();
	}


// --------------------------------------------------------------------------
// frees all extra stuff from memory
// Level = 0: do it quickly
// Level = 1: take more time, but do it better (this is not as "safe")

void compactMemory(int Level)
	{
	VerifyHeap();

	if (Level > 0)
		{
		PurgeAllSubMessages();

		VerifyHeap();

		if (msgfl)
			{
			bufferedFlush(msgfl, TRUE);
			}
		}

	VerifyHeap();

	if (encbuf)
		{
		delete [] encbuf;
		encbuf = NULL;
		}

	VerifyHeap();

	freeHufTree();

	VerifyHeap();

	purgeAllRLMs();

	VerifyHeap();

	if (Level > 0)
		{
		ReloadAllSubMessages();
		}

	VerifyHeap();
	}

#undef fopen
#undef unlink
#undef rename
#undef strdup

FILE *CitFopen(const char *FileName, const char *Mode)
	{
	if (debug)
		{
		doccr();
		cPrintf(getdbmsg(12), FileName, Mode);
		}

	FILE *f = _fsopen(FileName, Mode, SH_DENYNONE);

	if (debug)
		{
		if (f)
			{
			cPrintf(getdbmsg(13));
			}
		else
			{
			cPrintf(getdbmsg(14));
			}

		doccr();
		}

	return (f);
	}

int CitUnlink(const char *Path)
	{
	if (debug)
		{
		doccr();
		cPrintf(getdbmsg(56), Path);
		}

	int RetVal = unlink(Path);

	if (debug)
		{
		if (!RetVal)
			{
			cPrintf(getdbmsg(13));
			}
		else
			{
			cPrintf(getdbmsg(14));
			}

		doccr();
		}

	return (RetVal);
	}

int CitRename(const char *Orig, const char *Dest)
	{
	if (debug)
		{
		doccr();
		cPrintf(getdbmsg(57), Orig, Dest);
		}

	int RetVal = rename(Orig, Dest);

	if (debug)
		{
		if (!RetVal)
			{
			cPrintf(getdbmsg(13));
			}
		else
			{
			cPrintf(getdbmsg(14));
			}

		doccr();
		}

	return (RetVal);
	}

char *CitStrdup(const char *Orig)
	{
	assert(Orig);

	char *Copy = new char[strlen(Orig) + 1];

	if (Copy)
		{
		strcpy(Copy, Orig);
		}

	return (Copy);
	}

DaysE EvaluateDay(const char *Str)
	{
	int i = atoi(Str);

	if (i > 0 && i <= NUM_DAYS)
		{
		return ((DaysE) (i - 1));
		}
	else
		{
		for (i = 0; i < NUM_DAYS; i++)
			{
			if (SameString(Str, days[i]) ||
					SameString(Str, fulldays[i]))
				{
				return ((DaysE) i);
				}
			}
		}

	return (NUM_DAYS);
	}

MonthsE EvaluateMonth(const char *Str)
	{
	int i = atoi(Str);

	if (i > 0 && i <= NUM_MONTHS)
		{
		return ((MonthsE) (i - 1));
		}
	else
		{
		for (i = 0; i < NUM_MONTHS; i++)
			{
			if (SameString(Str, monthTab[i]) ||
					SameString(Str, fullmnts[i]))
				{
				return ((MonthsE) i);
				}
			}
		}

	return (NUM_MONTHS);
	}


void CitIsIdle(void)
	{
#ifdef WINCIT
	letWindowsMultitask();
#else
	if (!KBReady())
		{
		union REGS t_regs;

		switch (OSType)
			{
			case OS_OS2:
			case OS_WIN3:
			case OS_WINS:
				{
				t_regs.x.ax = 0x1680;
				int86(0x2f,&t_regs,&t_regs);
				break;
				}

			case OS_DV:
				{
				t_regs.x.ax = 0x1000;
				int86(0x15,&t_regs,&t_regs);
				break;
				}
			}
		}
#endif
	}

void cdecl noPrintf(const char *, ...)
	{
	}

void noDoCR(void)
	{
	}
