// log.h
#ifndef __LOG_H

#define __LOG_H

/* -------------------------------------------------------------------- */
/*	This class contains a user's flags. This is used in both the full   */
/*	structure of a user and in the in-RAM index.						*/
/* -------------------------------------------------------------------- */
class LogFlags
	{
	Bool	INUSE		: 1;	// Is this slot in use?
	Bool	UCMASK		: 1;	// Uppercase?
	Bool	LFMASK		: 1;	// Linefeeds?
	Bool	EXPERT		: 1;	// Expert?
	Bool	AIDE		: 1;	// Aide?
	Bool	TABS		: 1;	// Tabs?
	Bool	OLDTOO		: 1;	// Print out last oldmessage on N>ew?
	Bool	PROBLEM 	: 1;	// Twit bit
	Bool	UNLISTED	: 1;	// List in Userlog?
	Bool	PERMANENT	: 1;	// Permanent log entry?
	Bool	SYSOP		: 1;	// Sysop special access.
	Bool	NODE		: 1;	// Are they a node?
	Bool	NETUSER 	: 1;	// Can they send networked messages?
	Bool	NOACCOUNT	: 1;	// Accounting disabled for this person?
	Bool	NOMAIL		: 1;	// Can user send exclusive mail?
	Bool	ROOMTELL	: 1;	// Room-descriptions?

public:
	void Clear(void)
		{
		memset(this, 0, sizeof(*this));
		}

	LogFlags(void)
		{
		Clear();
		}

	Bool IsInuse(void) const		{ assert(this); return (INUSE); }
	void SetInuse(Bool New) 		{ assert(this); INUSE = !!New; }
	Bool IsUpperOnly(void) const	{ assert(this); return (UCMASK); }
	void SetUpperOnly(Bool New) 	{ assert(this); UCMASK = !!New; }
	Bool IsLinefeeds(void) const	{ assert(this); return (LFMASK); }
	void SetLinefeeds(Bool New) 	{ assert(this); LFMASK = !!New; }
	Bool IsExpert(void) const		{ assert(this); return (EXPERT); }
	void SetExpert(Bool New)		{ assert(this); EXPERT = !!New; }
	Bool IsAide(void) const 		{ assert(this); return (AIDE || SYSOP); }
	void SetAide(Bool New)			{ assert(this); AIDE = !!New; }
	Bool IsTabs(void) const 		{ assert(this); return (TABS); }
	void SetTabs(Bool New)			{ assert(this); TABS = !!New; }
	Bool IsOldToo(void) const		{ assert(this); return (OLDTOO); }
	void SetOldToo(Bool New)		{ assert(this); OLDTOO = !!New; }
	Bool IsProblem(void) const		{ assert(this); return (PROBLEM && !IsAide()); }
	void SetProblem(Bool New)		{ assert(this); PROBLEM = !!New; }
	Bool IsUnlisted(void) const 	{ assert(this); return (UNLISTED); }
	void SetUnlisted(Bool New)		{ assert(this); UNLISTED = !!New; }
	Bool IsPermanent(void) const	{ assert(this); return (PERMANENT); }
	void SetPermanent(Bool New) 	{ assert(this); PERMANENT = !!New; }
	Bool IsSysop(void) const		{ assert(this); return (SYSOP); }
	void SetSysop(Bool New) 		{ assert(this); SYSOP = !!New; }
	Bool IsNode(void) const 		{ assert(this); return (NODE); }
	void SetNode(Bool New)			{ assert(this); NODE = !!New; }
	Bool IsNetUser(void) const		{ assert(this); return (NETUSER); }
	void SetNetUser(Bool New)		{ assert(this); NETUSER = !!New; }
	Bool IsAccounting(void) const	{ assert(this); return (!NOACCOUNT && !NODE); }
	void SetAccounting(Bool New)	{ assert(this); NOACCOUNT = !New; }
	Bool IsMail(void) const 		{ assert(this); return (!NOMAIL); }
	void SetMail(Bool New)			{ assert(this); NOMAIL = !New; }
	Bool IsViewRoomDesc(void) const { assert(this); return (ROOMTELL); }
	void SetViewRoomDesc(Bool New)	{ assert(this); ROOMTELL = !!New; }

	void CopyFlags(const LogFlags &ToCopy);
	};

/* -------------------------------------------------------------------- */
/*	This class is the base class for all of the log-related classes 	*/
/*	that get saved to disk. It is used to store the file name to use	*/
/*	and to store information about the file if it is currently open.	*/
/* -------------------------------------------------------------------- */
class LogStarter
	{
	char	FileName[13];
	char	PAD;
	FILE	*File;

public:
	// All derived classes have a default file name (LOG.DAT, LOG2.DAT, etc.)
	// that their data are saved to. Each one defines its own version of
	// this function to set its default.
	virtual void ResetFileName(void) = 0;

	LogStarter(void)
		{
		assert(this);
		VerifyHeap();

		FileName[0] = 0;
		File = NULL;
		}

	~LogStarter(void)
		{
		assert(this);
		VerifyHeap();

		if (File)
			{
			fclose(File);
			}
		}

	const char *GetFileName(void) const
		{
		assert(this);
		return (FileName);
		}

	void SetFileName(const char *New)
		{
		assert(this);
		assert(New);

		CopyStringToBuffer(FileName, New, sizeof(FileName) - 1);
		}

	void OpenFile(void);
	void CloseFile(void);

	FILE *GetFile(void) const
		{
		assert(this);

		return (File);
		}

	void SetFile(FILE *New)
		{
		assert(this);

		File = New;
		}
	};

#include "bitbag.h"


/* -------------------------------------------------------------------- */
/*	Several of the log files (LOG2.DAT, LOG3.DAT, LOG4.DAT, and 		*/
/*	LOG6.DAT) keep track of arrays of bits. As all of their data are	*/
/*	stored in the same way in memory and disk, they all are based on	*/
/*	the same class. Their only differences are the default filenames to */
/*	use for disk storage, and the names used to access their data.		*/
/* -------------------------------------------------------------------- */
class LogBitBag : public LogStarter, public BitBag
	{
public:
	LogBitBag(g_slot NumGroups) : BitBag(NumGroups)
		{
		VerifyHeap();
		}

	Bool Load(l_index Index);
	Bool Save(l_index Index) const;
	};

/* -------------------------------------------------------------------- */
/*	Here are all of the classes used to store information in LOG*.DAT.	*/
/* -------------------------------------------------------------------- */
#include "log1.h"
#include "log2.h"
#include "log3.h"
#include "log4.h"
#include "log5.h"
#include "log6.h"

/* -------------------------------------------------------------------- */
/*	In addition to LOG.DAT, LOG2.DAT, LOG3.DAT, LOG4.DAT, LOG5.DAT, and */
/*	LOG6.DAT, which all store structured information, Citadel uses log	*/
/*	extension files to store non-structured data. This includes 		*/
/*	anything that might change in number and length, such as words in	*/
/*	the user's personal dictionary. Each user gets one file. The name   */
/*	is derived from the user's location in the LOG.DAT file: LE###.DAT, */
/*	where ### is replaced by an ASCII representation of the user's      */
/*	location (in decimal).												*/
/* -------------------------------------------------------------------- */
struct userDefLE
	{
	userDefLE *next;
	label Code;
	char Data[1];
	};

class LogExtensions
	{
	char		FileExtension[4];	// Extension to use for file.
	strList 	*le_kuser;			// Log extension: kill user 
	strList 	*le_knode;			// Log extension: kill node 
	strList 	*le_ktext;			// Log extension: kill text 
	strList 	*le_kreg;			// Log extension: kill region
	pairedStrings *le_tuser;		// Log extension: tag user
	pairedStrings *le_replace;		// Log extension: replace
	strList 	*le_dict;			// Log extension: dictionary
	userDefLE	*le_userdef;		// User defined data (scripts)
	char		*le_Finger; 		// User's finger stuff.
	jumpback	*jb;				// pointer to jumpback stack
	int 		jb_start;			// where valid data starts...
	int 		jb_end; 			// ...and ends
	int 		jb_length;			// how much we can store plus 1.
	Message 	*Msg;				// Lost message.
	r_slot		MsgRoom;			// Room for lost message.

public:
	Bool Load(l_index Index);
	Bool Save(l_index Index, const char *Name) const;

	Message *GetMessage(void) const
		{
		assert(this);
		return (Msg);
		}

	r_slot GetMessageRoom(void) const
		{
		assert(this);
		return (MsgRoom);
		}

	void SetMessageRoom(r_slot NewMsgRoom)
		{
		assert(this);
		MsgRoom = NewMsgRoom;
		}

	void SetMessage(Message *NewMsg);
	void ClearMessage(void);

	const char *GetFinger(void) const
		{
		assert(this);
		return (le_Finger);
		}

	Bool SetFinger(const char *NewFinger)
		{
		assert(this);
		assert(NewFinger);
		delete [] le_Finger;

		if (*NewFinger)
			{
			le_Finger = strdup(NewFinger);
			return (le_Finger != NULL);
			}
		else
			{
			le_Finger = NULL;
			return (TRUE);
			}
		}

	void Clear(void);

	LogExtensions(int MaxJumpback)
		{
		memset(this, 0, sizeof(*this));
		jb_length = MaxJumpback;
		jb = new jumpback[MaxJumpback];
		ResetFileExtension();
		}

	~LogExtensions()
		{
		Clear();
		delete [] jb;
		}

	LogExtensions& operator =(const LogExtensions &Original);
	LogExtensions(const LogExtensions &Original);

	void SetFileExtension(const char *NewExtension)
		{
		assert(this);

		CopyStringToBuffer(FileExtension, NewExtension,
				sizeof(FileExtension) - 1);
		}

	void ResetFileExtension(void)
		{
		assert(this);
		strcpy(FileExtension, "dat");
		}

	const void *GetPointer1(void) const
		{
		assert(this);
		return (le_kuser);
		}

	const void *GetPointer2(void) const
		{
		assert(this);
		return (le_knode);
		}

	const void *GetPointer3(void) const
		{
		assert(this);
		return (le_ktext);
		}

	const void *GetPointer4(void) const
		{
		assert(this);
		return (le_kreg);
		}

	const void *GetPointer5(void) const
		{
		assert(this);
		return (le_tuser);
		}

	const void *GetPointer6(void) const
		{
		assert(this);
		return (le_dict);
		}

	const void *GetPointer7(void) const
		{
		assert(this);
		return (le_Finger);
		}

	const void *GetPointer8(void) const
		{
		assert(this);
		return (Msg);
		}

	const void *GetPointer9(void) const
		{
		assert(this);
		return (jb);
		}

	const void *GetPointer10(void) const
		{
		assert(this);
		return (le_userdef);
		}

	const void *GetPointer11(void) const
		{
		assert(this);
		return (le_replace);
		}

	strList *GetKillUserPointer(void)			// Should not be const, richard.
		{
		assert(this);
		return (le_kuser);
		}

	strList *GetKillNodePointer(void)			// Should not be const, richard.
		{
		assert(this);
		return (le_knode);
		}

	strList *GetKillTextPointer(void)			// Should not be const, richard.
		{
		assert(this);
		return (le_ktext);
		}

	strList *GetKillRegionPointer(void) 		// Should not be const, richard.
		{
		assert(this);
		return (le_kreg);
		}

	strList **GetKillUserPointerPointer(void)	// should not be const
		{
		assert(this);
		return (&le_kuser);
		}

	strList **GetKillNodePointerPointer(void)	// should not be const
		{
		assert(this);
		return (&le_knode);
		}

	strList **GetKillTextPointerPointer(void)	// should not be const
		{
		assert(this);
		return (&le_ktext);
		}

	strList **GetKillRegionPointerPointer(void) // should not be const
		{
		assert(this);
		return (&le_kreg);
		}

	const pairedStrings *GetTagUserPointer(void) const
		{
		assert(this);
		return (le_tuser);
		}

	const pairedStrings *GetReplacePointer(void) const
		{
		assert(this);
		return (le_replace);
		}

	const strList *GetDictionaryPointer(void) const
		{
		assert(this);
		return (le_dict);
		}

	Bool AddKillUser(const char *User);
	Bool AddKillNode(const char *Node);
	Bool AddKillText(const char *Text);
	Bool AddKillRegion(const char *Region);
	Bool RemoveKillUser(const char *User);
	Bool RemoveKillNode(const char *Node);
	Bool RemoveKillText(const char *Text);
	Bool RemoveKillRegion(const char *Region);
	Bool AddTagUser(const char *User, const char *Tag);
	Bool AddReplace(const char *Orig, const char *Repl);
	Bool AddWordToDictionary(const char *Word);
	Bool RemoveUserTag(const char *User);
	Bool RemoveReplace(const char *Orig);
	Bool RemoveWordFromDictionary(const char *Word);
	Bool IsWordInDictionary(const char *Word) const;
	Bool IsKillUser(const char *User) const;
	Bool IsKillNode(const char *Node) const;
	Bool IsKillRegion(const char *Region) const;
	Bool IsKillText(const char *Text) const;
	void ShowTagsForUser(const char *User) const;
	Bool SetUserDefined(const char *Code, const char *Data);
	Bool RemoveUserDefined(const char *Code);
	const char *GetUserDefined(const char *Code) const;
	Bool IsUserDefined(const char *Code) const;

	void JumpbackPush(jumpback NewJB)
		{
		if (jb)
			{
			jb_end = ++jb_end % jb_length;

			if (jb_end == jb_start)
				{
				jb_start++;
				}

			jb[jb_end] = NewJB;
			}
		}


	Bool JumpbackPop(jumpback *Where)
		{
		// if we were not able to allocate jb, these will always be the same
		if (jb_start != jb_end)
			{
			*Where = jb[jb_end];

			if (--jb_end < 0)
				{
				jb_end = jb_length - 1;
				}

			return (TRUE);
			}
		else
			{
			return (FALSE);
			}
		}
	};

/* -------------------------------------------------------------------- */
/*	All of this comes together in the LogEntry class, which stores		*/
/*	everything known about the user.									*/
/* -------------------------------------------------------------------- */
class LogEntry : public LogEntry1, public LogEntry2, public LogEntry3,
		public LogEntry4, public LogEntry5, public LogEntry6,
		public LogExtensions
	{
public:
	Bool IsInGroup(g_slot GroupSlot) const; 	// overrides LOG2.H's

	LogEntry(r_slot NumRooms, g_slot NumGroups, int MaxJumpback);
	LogEntry(const LogEntry &Original);
	LogEntry& operator = (const LogEntry &Original);

	Bool Load(l_index Index);
	Bool LoadByName(const char *name);

	Bool Save(l_index Index) const;

	void Clear(void);

	Bool IsValid(void) const;

	Bool CanAccessRoom(r_slot RoomIndex, Bool ThisHall = FALSE, Bool IgnoreHidden = FALSE) const;
	Bool CanModerateRoom(r_slot RoomIndex) const;
	Bool HasGroupAccessToRoom(r_slot roomslot) const;
	Bool CanAccessHall(h_slot hallslot) const;
	Bool HasRoomPrivileges(r_slot roomslot) const;
	};

// And this is our in-RAM index of the users.
#include "logtab.h"

// log.cpp
int nodexists(const char *name);
int addressexists(const char *name);
l_slot FindPersonByPartialName(const char *name);
l_slot FindPersonByName(const char *name);
Bool LoadPersonByName(const char *name);

void storeLog(void);
void displaypw(const char *name, const char *in, const char *pw);
void normalizepw(const char *InitPw, char *Initials, char *passWord);
void log2tab(LTable *Table, const LogEntry1 *Log, l_index Slot);

// cyclelog.cpp
void cyclegroup(int slot);
void cycleroom(int slot, Bool GiveAccess, Bool ClearIgnore);

// logovl.cpp
void ListUsers(Bool ListUnlisted);

extern LogTable 		*LogTab;

#ifdef MAIN
char *logextensions[] =
	{
	"KILLUSER",         "KILLTEXT",         "KILLNODE",
	"KILLREG",          "TAGUSER",          "MESSAGE",
	"DICTWORD",         "JB",               "FINGER",
	"USERDEF",          "REPLACE",

	NULL
	};
#else
extern char *logextensions[];
#endif

enum
	{
	LE_KUSER,			LE_KTEXT,			LE_KNODE,
	LE_KREG,			LE_TUSER,			LE_MESSAGE,
	LE_DICTWORD,		LE_JUMPBACK,		LE_FINGER,
	LE_USERDEF, 		LE_REPLACE,

	MAXLOGEXT,
	};
#endif
