/* -------------------------------------------------------------------- */
/*	ROOM2.CPP					Citadel 								*/
/* -------------------------------------------------------------------- */
/*				Overlayed parts of the room database subsystem. 		*/
/* -------------------------------------------------------------------- */
/*	For full information about the room database subsystem, see ROOM.C. */
/* -------------------------------------------------------------------- */
#include "ctdl.h"
#pragma hdrstop

#include "room.h"
#include "roompriv.h"
#include "net.h"
#include "log.h"
#include "group.h"
#include "hall.h"
#include "msg.h"

/* -------------------------------------------------------------------- */
/*								Contents								*/
/* -------------------------------------------------------------------- */
/*								Public									*/
/*																		*/
/*	Functions:															*/
/*		FindFreeRoomSlot()												*/
/*			Find a free (unused) room slot. 							*/
/*																		*/
/*		CreateRoomSummary() 											*/
/*			Creates a summary of a specified room.						*/
/*																		*/
/*		MakeRoomName()													*/
/*			Creates a "Roomname>" type string.                          */
/*																		*/
/*		LoadCurrentRoom()												*/
/*			Loads a specified room as "current".                        */
/*																		*/
/*		DebugAuxmemRoomTab()											*/
/*			Dumps info to the user. 									*/
/*																		*/
/*		PartialRoomExists() 											*/
/*			Checks to see if a room exists, given a partial room name.	*/
/*																		*/
/* createRoomTab()			does what it says							*/
/* destroyRoomTab() 		does just that								*/
/* -------------------------------------------------------------------- */
/*								Private 								*/
/*	Functions:															*/
/* addRtBlock() 			adds another chunk to the rt				*/
/*																		*/
/*	Data types: 														*/
/*																		*/
/*	Data:																*/
/* -------------------------------------------------------------------- */


/* -------------------------------------------------------------------- */
/* -------------------------------------------------------------------- */


/* -------------------------------------------------------------------- */
/*	FindFreeRoomSlot()		Find a free (unused) room slot. 			*/
/*																		*/
/*	Output: 	return		A free room slot. This returns CERROR if it */
/*							cannot find a free slot.					*/
/* -------------------------------------------------------------------- */
r_slot FindFreeRoomSlot(void)
	{
	r_slot roomRover;

	for (roomRover = 0; roomRover < cfg.maxrooms; roomRover++)
		{
		if (!IsRoomInuse(roomRover))
			{
			return (roomRover);
			}
		}

	return (CERROR);
	}


/* -------------------------------------------------------------------- */
/*	CreateRoomSummary() 	Creates a summary of a specified room.		*/
/*																		*/
/*	Input:		RoomIndex	Index in room database of room to sumarize. */
/*							This room should already have been checked	*/
/*							with IsRoomInuse to verify that it used.	*/
/*																		*/
/*	Output: 	buffer		Where to put the summary.					*/
/* -------------------------------------------------------------------- */
void CreateRoomSummary(char *buffer, r_slot RoomIndex)
	{
	label id;
	aRoom *rBuf;

	assert(RoomIndex < cfg.maxrooms);
	assert(RoomIndex >= 0);
	assert(IsRoomInuse(RoomIndex));

	if ((rBuf = new aRoom) == NULL)
		{
		sprintf(buffer, getmsg(188), getmsg(1361));
		}
	else
		{
		getRoom(RoomIndex, rBuf);

		sprintf(buffer, getmsg(901), cfg.Uroom_nym, rBuf->rbname);

		if (rBuf->rbflags.IsShared())
			{
			CopyStringToBuffer(id, rBuf->rbname, LABELSIZE);
			NametoId(id);
			if (id[0])
				{
				char String[128];
				sprintf(String, getmsg(937), id);
				strcat(buffer, String);
				}
			}

		if (rBuf->rbflags.IsGroupOnly())
			{
			char String[128];

			if (rBuf->rbflags.IsBooleanGroup())
				{
				sprintf(String, getmsg(278), cfg.Lgroup_nym);
				}
			else
				{
				sprintf(String, getmsg(938), cfg.Lgroup_nym,
						GroupData->GetEntry(rBuf->rbgrpno)->GetName());
				}

			strcat(buffer, String);
			}

		if (rBuf->rbflags.IsPrivilegedGroup())
			{
			char String[128];

			sprintf(String, getmsg(939), cfg.Lgroup_nym,
					GroupData->GetEntry(rBuf->rbPgrpno)->GetName());

			strcat(buffer, String);
			}

		if (!rBuf->rbflags.IsPublic())
			{
			strcat(buffer, getmsg(940));
			}

		if (rBuf->rbflags.IsAnonymous())
			{
			strcat(buffer, getmsg(941));
			}

		if (rBuf->rbflags.IsBIO())
			{
			strcat(buffer, getmsg(942));
			}

		if (rBuf->rbflags.IsModerated())
			{
			strcat(buffer, getmsg(943));
			}

		if (rBuf->rbflags.IsReadOnly())
			{
			strcat(buffer, getmsg(944));
			}

		if (rBuf->rbflags.IsDownloadOnly())
			{
			strcat(buffer, getmsg(945));
			}

		if (rBuf->rbflags.IsShared())
			{
			strcat(buffer, getmsg(948));
			}

		if (rBuf->rbflags.IsApplication())
			{
			strcat(buffer, getmsg(950));
			}

		if (rBuf->AUTOAPP)
			{
			strcat(buffer, getmsg(965));
			}

		if (rBuf->rbflags.IsPermanent())
			{
			char String[128];

			sprintf(String, getmsg(966), cfg.Lroom_nym);
			strcat(buffer, String);
			}

		if (TI()CurrentUser->IsAide() && rBuf->rbflags.IsMsDOSdir())
			{
			char String[128];

			sprintf(String, getmsg(967), bn, rBuf->rbdirname);
			strcat(buffer, String);
			}

		if (TI()CurrentUser->IsSysop() && rBuf->rbflags.IsApplication())
			{
			char String[128];

			sprintf(String, getmsg(968), bn, rBuf->rbaplic);
			strcat(buffer, String);
			}

		if (rBuf->rbroomtell[0] && TI()CurrentUser->IsSysop())
			{
			char String[128];

			sprintf(String, getmsg(969), bn, cfg.Uroom_nym, rBuf->rbroomtell);
			strcat(buffer, String);
			}

		if (rBuf->descript[0])
			{
			char String[256];

			sprintf(String, getmsg(970), bn, cfg.Uroom_nym, rBuf->descript);
			strcat(buffer, String);
			}

		delete rBuf;
		}
	}


/* -------------------------------------------------------------------- */
/*	MakeRoomName()			Creates a "Roomname>" type string.          */
/*																		*/
/*	Input:		RoomIndex	Index in room database of room to name. 	*/
/*							This room should already have been checked	*/
/*							with IsRoomInuse to verify that it used.	*/
/*																		*/
/*	Output: 	Buffer		A place to write the name to.				*/
/*				return		A pointer to Buffer.						*/
/* -------------------------------------------------------------------- */
char *MakeRoomName(r_slot RoomIndex, char *Buffer)
	{
	Bool Flagged = FALSE;

	char NewFlag[2];
	NewFlag[1] = 0;

	const int flagsIndex = (TI()CurrentUser->IsIBMRoom() &&
			TI()CurrentUser->IsIBMGraph()) ? 49 : 50;

	assert(IsRoomInuse(RoomIndex));

	if (IsRoomShared(RoomIndex))
		{
		strcpy(Buffer, TI()loggedIn ?
				TI()CurrentUser->GetNetPrefix() : cfg.netPrefix);
		}
	else
		{
		Buffer[0] = '\0';
		}

	strcat(Buffer, GetRoomName(RoomIndex));

	if (IsRoomDirectory(RoomIndex))
		{
		*NewFlag = getmsg(flagsIndex)[0];
		strcat(Buffer, NewFlag);
		Flagged = TRUE;
		}

	if (IsRoomGroupOnly(RoomIndex))
		{
		*NewFlag = getmsg(flagsIndex)[1];
		strcat(Buffer, NewFlag);
		Flagged = TRUE;
		}

	if (IsRoomHidden(RoomIndex))
		{
		*NewFlag = getmsg(flagsIndex)[2];
		strcat(Buffer, NewFlag);
		Flagged = TRUE;
		}

	if (IsRoomBIO(RoomIndex))
		{
		*NewFlag = getmsg(flagsIndex)[3];
		strcat(Buffer, NewFlag);
		Flagged = TRUE;
		}

	if (iswindow(RoomIndex))
		{
		*NewFlag = getmsg(flagsIndex)[4];
		strcat(Buffer, NewFlag);

		if (TI()CurrentUser->IsIBMRoom() && TI()CurrentUser->IsIBMGraph())
			{
			Flagged = TRUE;
			}
		}

	if (!Flagged)
		{
		*NewFlag = getmsg(flagsIndex)[5];
		strcat(Buffer, NewFlag);
		}

	return (Buffer);
	}


/* -------------------------------------------------------------------- */
/*	LoadCurrentRoom()		Loads a specified room as "current".        */
/*																		*/
/*	Input:		RoomIndex	Index in room database of room to load. 	*/
/* -------------------------------------------------------------------- */
void LoadCurrentRoom(r_slot RoomIndex)
	{
	getRoom(RoomIndex, roomBuf);
	checkdir();
	}


#ifdef AUXMEM
void destroyRoomTab(void)
	{
	DeleteAuxmemList(&rtList, &roomBlocksInHeap);
	}

void createRoomTab(void)
	{
	r_slot i;

	assert(rtList == NULL);

	for (i = 0; i < cfg.maxrooms; i += ROOMTABPERPAGE)
		{
		if (!AddAuxmemBlock(&rtList, i, &roomBlocksInHeap, 1))
			{
			crashout(getcfgmsg(112), ltoac(i));
			}
		}
	}
#else
void destroyRoomTab(void)
	{
	delete [] roomTab;
	}

void createRoomTab(void)
	{
	if ((roomTab = new rTable[cfg.maxrooms]) == NULL)
		{
		crashout(getcfgmsg(113));
		}
	}
#endif

/* -------------------------------------------------------------------- */
/*	PartialRoomExists() 	Checks to see if a room exists, given a 	*/
/*							partial room name.							*/
/*																		*/
/*	Input:		SearchString	The string to look for. 				*/
/*				StartRoom		The room to start looking in.			*/
/*				IgnoreHalls 	TRUE to look for the room in any hall.	*/
/*								FALSE to look only in the current hall. */
/*																		*/
/*	Output: 	return			Index into room database of the room	*/
/*								if a match is found, else CERROR.		*/
/* -------------------------------------------------------------------- */
r_slot PartialRoomExists(const char *SearchString, r_slot StartRoom, Bool IgnoreHalls)
	{
	assert(StartRoom < cfg.maxrooms && StartRoom >= 0);

	label da_rn;
	CopyStringToBuffer(da_rn, deansi(SearchString), sizeof(da_rn) - 1);

	// if longer than a room name, it cannot be a match...
	if (strlen(da_rn) > LABELSIZE)
		{
		return (CERROR);
		}

	const r_slot rpStartRoom = GetRoomPosInTable(StartRoom);

	const int length = strlen(da_rn);
	int i;
	r_slot rps;
	for (i = 0, rps = rpStartRoom + 1; i < cfg.maxrooms; ++i, ++rps)
		{
		if (rps == cfg.maxrooms)
			{
			rps = 0;
			}
		const int j = RoomPosTableToRoom(rps);

		if (IsRoomInuse(j) && TI()CurrentUser->IsInRoom(j))
			{
			if (
					/* room names match */
					(strnicmp(deansi(GetRoomName(j)), da_rn, length) ==
					SAMESTRING) &&

					/* and it is in the correct hall */
					(IgnoreHalls || (TI()CurrentUser->IsUsePersonalHall() ?
					TI()CurrentUser->IsRoomInPersonalHall(j) :
					roominhall(j, TI()thisHall))) &&

					/* And the user can see the room */
					TI()CurrentUser->CanAccessRoom(j, !IgnoreHalls, FALSE) &&

					/* And the user hasn't excluded it */
					!TI()CurrentUser->IsRoomExcluded(j)
					)
				{
				return (j);
				}
			}
		}

	for (i = 0, rps = rpStartRoom + 1; i < cfg.maxrooms; ++i, ++rps)
		{
		if (rps == cfg.maxrooms)
			{
			rps = 0;
			}
		const int j = RoomPosTableToRoom(rps);

		if (IsRoomInuse(j) && TI()CurrentUser->IsInRoom(j))
			{
			if (
					/* If the names match */
					IsSubstr(GetRoomName(j), da_rn) &&

					/* and it is in the correct hall */
					(IgnoreHalls || (TI()CurrentUser->IsUsePersonalHall() ?
					TI()CurrentUser->IsRoomInPersonalHall(j) :
					roominhall(j, TI()thisHall))) &&

					/* And the user can see the room */
					TI()CurrentUser->CanAccessRoom(j, !IgnoreHalls, FALSE) &&

					/* And the user hasn't excluded it */
					!TI()CurrentUser->IsRoomExcluded(j)
					)
				{
				return (j);
				}
			}
		}

	for (i = 0, rps = rpStartRoom + 1; i < cfg.maxrooms; ++i, ++rps)
		{
		if (rps == cfg.maxrooms)
			{
			rps = 0;
			}
		const int j = RoomPosTableToRoom(rps);

		if (IsRoomInuse(j) && TI()CurrentUser->IsInRoom(j))
			{
			if (
					/* room names match */
					(strnicmp(deansi(GetRoomName(j)), da_rn, length) ==
					SAMESTRING) &&

					/* and it is in the correct hall */
					(IgnoreHalls || (TI()CurrentUser->IsUsePersonalHall() ?
					TI()CurrentUser->IsRoomInPersonalHall(j) :
					roominhall(j, TI()thisHall))) &&

					/* And the user can see the room */
					TI()CurrentUser->CanAccessRoom(j, !IgnoreHalls, FALSE))
				{
				return (j);
				}
			}
		}

	for (i = 0, rps = rpStartRoom + 1; i < cfg.maxrooms; ++i, ++rps)
		{
		if (rps == cfg.maxrooms)
			{
			rps = 0;
			}
		const int j = RoomPosTableToRoom(rps);

		if (IsRoomInuse(j) && TI()CurrentUser->IsInRoom(j))
			{
			if (
					/* If the names match */
					IsSubstr(GetRoomName(j), da_rn) &&

					/* and it is in the correct hall */
					(IgnoreHalls || (TI()CurrentUser->IsUsePersonalHall() ?
					TI()CurrentUser->IsRoomInPersonalHall(j) :
					roominhall(j, TI()thisHall))) &&

					/* And the user can see the room */
					TI()CurrentUser->CanAccessRoom(j, !IgnoreHalls, FALSE))
				{
				return (j);
				}
			}
		}

	return (CERROR);
	}


/* closes invalid directory rooms */
void checkdir(void)
	{
	if (roomBuf->rbflags.IsMsDOSdir())
		{
		if (!changedir(roomBuf->rbdirname))
			{
			roomBuf->rbflags.SetMsDOSdir(FALSE);
			roomBuf->rbflags.SetDownloadOnly(FALSE);
			roomBuf->rbflags.SetUploadOnly(FALSE);

			putRoom(TI()thisRoom, roomBuf);

			Message *Msg = new Message;

			if (Msg)
				{
				Msg->SetTextWithFormat(getmsg(66), roomBuf->rbname,
						roomBuf->rbdirname);

				if (roomBuf->rbflags.IsGroupOnly())
					{
					if (roomBuf->rbflags.IsBooleanGroup())
						{
						Msg->SetGroup(GroupData->GetEntry(1)->GetName());
						}
					else
						{
						Msg->SetGroup(GroupData->GetEntry(roomBuf->rbgrpno)->
								GetName());
						}
					}

				Msg->SetRoomNumber(AIDEROOM);

				systemMessage(Msg);

				delete Msg;
				}
			else
				{
				mPrintf(getmsg(188), getmsg(1315));
				}
			}
		}
	}

