// --------------------------------------------------------------------------
// Citadel: MsgCfg.CPP
//
// Message-base configuration code.

#include "ctdl.h"
#pragma hdrstop

#include "auxtab.h"
#include "msg.h"
#include "config.h"

// --------------------------------------------------------------------------
// Contents
//
// msgInit()		builds message table from msg.dat					*/
// zapMsgFile() 	initializes msg.dat 								*/
// slidemsgTab()	frees slots at beginning of msg table				*/


static void slidemsgTab(ulong howmany);

// --------------------------------------------------------------------------
// msgInit(): Scans over MSG.DAT builds the table.

#ifdef WINCIT
	#define InitProgressIndicator()
#else
	#define InitProgressIndicator() 							\
		int ccol, crow; 										\
		int loc = 0, nowloc;									\
		const long neatoconstant = (cfg.messagek * 16L);		\
		const int twirllen = strlen(cfg.twirly);				\
																\
		msgDisp(getcfgmsg(182), cfg.Lmsg_nym);					\
		doccr();												\
																\
		strrev(cfg.twirly); 									\
																\
		outCon('|');                                            \
																\
		for (nowloc = 0; nowloc < 64; nowloc++) 				\
			{													\
			outCon(cfg.fuelbarempty);							\
			}													\
																\
		outCon('|');                                            \
		readpos(&crow, &ccol);									\
		ccol -= 65; 											\
		position(crow, ccol);
#endif

#ifdef WINCIT
	#define UpdateProgressIndicator() msgDisp(getcfgmsg(179), ltoac(cfg.newest))
#else
	#define UpdateProgressIndicator()							\
		nowloc = (int) (bufferedTell(msgfl) / neatoconstant);	\
		while (loc < nowloc)									\
			{													\
			outCon(cfg.fuelbarfull);							\
			loc++;												\
			}													\
																\
		outCon(cfg.twirly[(int) (CurrentMessageNumber % twirllen)]);	\
		readpos(&crow, &ccol);									\
		ccol--; 												\
		position(crow, ccol);
#endif

#ifdef WINCIT
	#define DoneWithProgressIndicator() msgDisp(NULL)
#else
	#define DoneWithProgressIndicator() 						\
		while (loc < 64)										\
			{													\
			outCon(cfg.fuelbarfull);							\
			loc++;												\
			}													\
																\
		doccr();												\
		label Newest, Oldest;									\
		strcpy(Newest, ltoac(cfg.newest));						\
		strcpy(Oldest, ltoac(cfg.oldest));						\
																\
		cPrintf(getcfgmsg(180), 								\
				ltoac(cfg.newest - cfg.oldest + 1), 			\
				(cfg.newest - cfg.oldest + 1 == 1) ?			\
				cfg.Lmsg_nym : cfg.Lmsgs_nym, Oldest, Newest);	\
																\
		doccr();												\
		doccr();												\
																\
		strrev(cfg.twirly);
#endif


void msgInit(void)
	{
	Message *Msg = new Message;

	if (Msg)
		{
		ulong FirstMessageFound;
		ulong CurrentMessageNumber;

		for (CurrentMessageNumber = 0; CurrentMessageNumber < cfg.nmessages;
				CurrentMessageNumber++)
			{
			getFlags(CurrentMessageNumber)->SetInuse(FALSE);
			}

		CurrentMessageNumber = 0;

		InitProgressIndicator();

		bufferedWinReOpen(msgfl);

		// Find a message to start with. There has to be a message...
		ReadMessageStatus Status;

		bufferedSeek(msgfl, 0l);

		do
			{
			Status = Msg->ReadAll(RMC_NORMAL, NULL);

			if (Status == RMS_NOMESSAGE)
				{
				crashout(getcfgmsg(181));
				}
			} while (Status != RMS_OK);

		// We have a message. Good. Put it into the table.
		FirstMessageFound = atol(Msg->GetLocalID());
		cfg.newest = cfg.oldest = cfg.mtoldest = FirstMessageFound;
		indexmessage(Msg);

		// And keep track of where it ended, so we know were to write next.
		cfg.catLoc = bufferedTell(msgfl);

		// Now, we have a table to build onto. Have at it.
		for (;;)
			{
			bufferedWinCloseTmp(msgfl);
			KBReady();
			letWindowsMultitask();
			bufferedWinReOpen(msgfl);

			UpdateProgressIndicator();

			do
				{
				Status = Msg->ReadAll(RMC_NORMAL, NULL);

				if (Status == RMS_NOMESSAGE)
					{
					break;
					}
				} while (Status != RMS_OK);

			if (Status == RMS_NOMESSAGE)
				{
				break;
				}

			// Okay: we have succeeded in reading another message. Get its
			// number and see if we can use it.
			CurrentMessageNumber = atol(Msg->GetLocalID());

			if (CurrentMessageNumber == FirstMessageFound)
				{
				// whee! back to where we started.
				break;
				}

			if (CurrentMessageNumber > cfg.newest)
				{
				cfg.catLoc = bufferedTell(msgfl);

				// put it on the end of the table
				long HowManyToScroll = CurrentMessageNumber -
						(cfg.mtoldest + cfg.nmessages - 1);

				if (HowManyToScroll > 0)
					{
					crunchmsgTab(HowManyToScroll);
					}

				cfg.newest = CurrentMessageNumber;

				indexmessage(Msg);
				}
			else if (CurrentMessageNumber < cfg.oldest)
				{
				// new oldest message
				ulong HowManyToInsert = min((long) (cfg.mtoldest - CurrentMessageNumber),
						(long) (cfg.nmessages - (cfg.newest - cfg.mtoldest + 1)));

				if (HowManyToInsert > 0)
					{
					slidemsgTab(HowManyToInsert);
					}

				cfg.mtoldest = cfg.mtoldest - HowManyToInsert;
				cfg.oldest = CurrentMessageNumber;

				if (CurrentMessageNumber >= cfg.mtoldest)
					{
					// we have made enough room for this message.
					indexmessage(Msg);
					}
				}
			else if (CurrentMessageNumber < cfg.mtoldest)
				{
				// this is a missing message. do nothing with it.
				}
			else
				{
				// this is somewhere in the table...

				// indexslot() cannot fail: we have already checked the
				// range of CurrentMessageNumber ourselves.
				assert(indexslot(CurrentMessageNumber) != M_SLOT_ERROR);

				if (!getFlags(indexslot(CurrentMessageNumber))->IsInuse())
					{
					// this slot is not inuse right now...
					indexmessage(Msg);
					}
				else
					{
					// slot already in use: just ignore it.
					}
				}
			}


		bufferedWinCloseTmp(msgfl);

		DoneWithProgressIndicator();

		// make sure all are in good rooms.
		m_slot TableSize = sizetable();
		for (m_slot slot = 0; slot < TableSize; slot++)
			{
			if (getRoomNum(slot) >= cfg.maxrooms)
				{
				changeheader(cfg.mtoldest + slot, DUMP, UCHAR_MAX);
				}
			}

		delete Msg;
		}
	else
		{
		crashout(getmsg(188), getcfgmsg(167));
		}
	}


// --------------------------------------------------------------------------
// zapMsgFile(): Initializes MSG.DAT.

Bool zapMsgFile(void)
	{
	long Written;
	size_t ToWrite;
	size_t BufferSize = 63 * 1024;		// Up to 63K buffer
	char *Buffer = new char[BufferSize];


	while (!Buffer)
		{
		BufferSize -= 1024;

		// At least 1K buffer
		if (!BufferSize)
			{
			illegal(getmsg(188), getcfgmsg(153));
			}

		Buffer = new char[BufferSize];
		}

	memset(Buffer, 0, BufferSize);

	// put first message...

	Buffer[0] = 0xFF;	// Message start
	Buffer[1] = '\0';   // Normal attribute
	Buffer[2] = LOBBY;	// Message lives in Dump>
	Buffer[3] = '1';    // Message number 1...
	Buffer[4] = '\0';   // ...message number 1

	// Author, Room, Date, Message
	sprintf(Buffer + 5, getcfgmsg(142), cfg.nodeTitle, 0, getcfgmsg(144),
			0, time(NULL), 0);

	// A little bit out of place, perhaps... how many blocks to write?
	Written = ToWrite = (size_t) min(cfg.messagek * 1024, (long) BufferSize);

	// And write the first block.
	if (bufferedWrite(Buffer, ToWrite, msgfl) != ToWrite)
		{
		illegal(getcfgmsg(149));
		}

	// Re-clear to remove first message from buffer
	memset(Buffer, 0, BufferSize);

	msgCaption(getcfgmsg(150), ltoac(cfg.messagek));
	doccr();

	msgDisp(getcfgmsg(143), ltoac(Written / 1024), br);

	// And write the rest.
	while (Written < cfg.messagek * 1024)
		{
		ToWrite = (size_t) min(cfg.messagek * 1024 - Written,
				(long) BufferSize);

		if (bufferedWrite(Buffer, ToWrite, msgfl) != ToWrite)
			{
			illegal(getcfgmsg(149));
			}

		Written += ToWrite;
		msgDisp(getcfgmsg(143), ltoac(Written / 1024), br);
		}

	doccr(); 
	doccr();

	delete [] Buffer;
	return (TRUE);
	}


#ifdef AUXMEM
// --------------------------------------------------------------------------
// slidemsgTab(): Frees slots at the beginning of the message table.
//					Auxmem version.
//
// Notes:
//	this is way boo boo slow, but as it is only called once, and that is when
//	building the message table, we can ignore the slowness in favor of
//	program size: even if the function was just a return, building the
//	message table would still take forever...

static void slidemsgTab(ulong howmany)
	{
	m_slot i;

	for (i = cfg.nmessages - 1; i >= howmany; i--)
		{
		memcpy (getMsgTab(i), getMsgTab(i - howmany), sizeof(messagetable));
		}

	for (i = 0; i < howmany; i++)
		{
		memset(getMsgTab(i), 0, sizeof(messagetable));
		}
	}

#else

#ifdef WINCIT
// --------------------------------------------------------------------------
// slidemsgTab(): Frees slots at the beginning of the message table.
//					Windows version.
//
// Notes:
//	this is way boo boo slow, but as it is only called once, and that is when
//	building the message table, we can ignore the slowness in favor of
//	program size: even if the function was just a return, building the
//	message table would still take forever...

static void slidemsgTab(ulong howmany)
	{
	m_slot i;

	for (i = cfg.nmessages - 1; i >= howmany; i--)
		{
		memcpy (&(msgTabWin[i]), &(msgTabWin[i - howmany]), sizeof(messagetable));
		}

	for (i = 0; i < howmany; i++)
		{
		memset(&(msgTabWin[i]), 0, sizeof(messagetable));
		}
	}

#else

// --------------------------------------------------------------------------
// slidemsgTab(): Frees slots at the beginning of the message table.
//					Regular version.

static void slidemsgTab(ulong how_many)
	{
	uint numnuked;

	int howmany = (int) how_many;

	numnuked = cfg.nmessages - howmany;

	memmove(&msgTab_mtmsgflags[howmany], msgTab_mtmsgflags,
			(uint)(numnuked * (sizeof(*msgTab_mtmsgflags))));
	memset(msgTab_mtmsgflags, 0, howmany * sizeof(*msgTab_mtmsgflags));

	memmove(&msgTab_mtmsgLocLO[howmany], msgTab_mtmsgLocLO,
			(uint)(numnuked * (sizeof(*msgTab_mtmsgLocLO))));
	memset(msgTab_mtmsgflags, 0, howmany * sizeof(*msgTab_mtmsgLocLO));

	memmove(&msgTab_mtmsgLocHI[howmany], msgTab_mtmsgLocHI,
			(uint)(numnuked * (sizeof(*msgTab_mtmsgLocHI))));
	memset(msgTab_mtmsgflags, 0, howmany * sizeof(*msgTab_mtmsgLocHI));

	memmove(&msgTab_mtroomno[howmany], msgTab_mtroomno,
			(uint)(numnuked * (sizeof(*msgTab_mtroomno))));
	memset(msgTab_mtmsgflags, 0, howmany * sizeof(*msgTab_mtroomno));

	memmove(&msgTab_mttohash[howmany], msgTab_mttohash,
			(uint)(numnuked * (sizeof(*msgTab_mttohash))));
	memset(msgTab_mtmsgflags, 0, howmany * sizeof(*msgTab_mttohash));

	memmove(&msgTab_mtauthhash[howmany], msgTab_mtauthhash,
			(uint)(numnuked * (sizeof(*msgTab_mtauthhash))));
	memset(msgTab_mtmsgflags, 0, howmany * sizeof(*msgTab_mtauthhash));

	memmove(&msgTab_mtomesg[howmany], msgTab_mtomesg,
			(uint)(numnuked * (sizeof(*msgTab_mtomesg))));
	memset(msgTab_mtmsgflags, 0, howmany * sizeof(*msgTab_mtomesg));
	}
#endif
#endif
