// --------------------------------------------------------------------------
// Citadel: MsgTab.CPP
//
// Message table related code.

#include "ctdl.h"
#pragma hdrstop

#include "msg.h"
#include "filemake.h"

/* -------------------------------------------------------------------- */
/*	indexmessage()	builds one message index from *Msg					*/
/* -------------------------------------------------------------------- */
void indexmessage(Message *Msg)
	{
	if (roomBuild)
		{
		buildroom(Msg);
		}

	ulong here = atol(Msg->GetLocalID());
	m_slot slot = indexslot(here);

	// Should we handle this gracefully in the debugging version?
	assert(slot != M_SLOT_ERROR);

	// This is about as gracefully as we can handle it.
	if (slot == M_SLOT_ERROR)
		{
		return;
		}

	ulong copy;

#ifdef AUXMEM
	messagetable *lmt;
	msgflags *lmf;

	lmt = getMsgTab(slot);
	lmf = &(lmt->mtmsgflags);
#endif

#ifdef AUXMEM
	#ifdef GOODBYE
		lmt->mtmsgLoc		= 0;

		lti->mf.MAIL		= 0;
		lti->mf.RECEIVED	= 0;
		lti->mf.REPLY		= 0;
		lti->mf.PROBLEM 	= 0;
		lti->mf.MADEVIS 	= 0;
		lti->mf.LIMITED 	= 0;
		lti->mf.MODERATED	= 0;
		lti->mf.COPY		= 0;
		lti->mf.NET 		= 0;
		lti->mf.FORWARDED	= 0;
		lti->mf.NETWORKED	= 0;

		lti->mf.CENSORED	= 0;
		lti->mf.MASSEMAIL	= 0;
	#else
		* (int *) lmf	= 0;
	#endif

	lmt->mtauthhash = 0;
	lmt->mttohash	= 0;
	lmt->mtomesg	= 0;

	lmt->mtroomno	= DUMP;

	/* --- */

	lmt->mtmsgLoc = Msg->GetHeadLoc();

#else
	#ifdef WINCIT
		*(int *) &(msgTabWin[slot].mtmsgflags) = 0;
		msgTabWin[slot].mtauthhash = 0;
		msgTabWin[slot].mttohash = 0;
		msgTabWin[slot].mtomesg = 0;
		msgTabWin[slot].mtroomno = DUMP;
		msgTabWin[slot].mtmsgLoc = Msg->GetHeadLoc();
	#else
		*(int *) &(msgTab_mtmsgflags[slot]) = 0;

		msgTab_mtauthhash[slot] = 0;
		msgTab_mttohash[slot]	= 0;
		msgTab_mtomesg[slot]	= 0;

		msgTab_mtroomno[slot]	= DUMP;

		/* --- */

		msgTab_mtmsgLocLO[slot] = long_LO(Msg->GetHeadLoc());
		msgTab_mtmsgLocHI[slot] = long_HI(Msg->GetHeadLoc());
	#endif
#endif

//	if (IsRoomInuse(Msg->GetRoomNumber()))
//		{
		if (Msg->GetSourceID()[0])
			{
			ulong oid = atol(Msg->GetSourceID());

			#ifdef AUXMEM
				lmt->mtomesg = (uint) oid;
				lmf->SetNetworked(TRUE);
			#else
				#ifdef WINCIT
					msgTabWin[slot].mtomesg = (uint) oid;
					msgTabWin[slot].mtmsgflags.SetNetworked(TRUE);
				#else
					msgTab_mtomesg[slot] = (uint) oid;
					msgTab_mtmsgflags[slot].SetNetworked(TRUE);
				#endif
			#endif
			}

		if (*Msg->GetAuthor())
			{
			#ifdef AUXMEM
				lmt->mtauthhash = hash(Msg->GetAuthor());
			#else
				#ifdef WINCIT
					msgTabWin[slot].mtauthhash = hash(Msg->GetAuthor());
				#else
					msgTab_mtauthhash[slot] = hash(Msg->GetAuthor());
				#endif
			#endif
			}

		if (*Msg->GetToUser() && *Msg->GetGroup() &&
				(SameString(Msg->GetToUser(), Msg->GetGroup())))
			{
			#ifdef AUXMEM
				lmf->SetMassemail(TRUE);
				lmt->mttohash = hash(Msg->GetGroup());
			#else
				#ifdef WINCIT
					msgTabWin[slot].mtmsgflags.SetMassemail(TRUE);
					msgTabWin[slot].mttohash = hash(Msg->GetGroup());
				#else
					msgTab_mtmsgflags[slot].SetMassemail(TRUE);
					msgTab_mttohash[slot] = hash(Msg->GetGroup());
				#endif
			#endif
			}
		else
			{
			if (*Msg->GetToUser())
				{
				#ifdef AUXMEM
					lmt->mttohash = hash(Msg->GetToUser());
					lmf->SetMail(TRUE);
				#else
					#ifdef WINCIT
						msgTabWin[slot].mttohash = hash(Msg->GetToUser());
						msgTabWin[slot].mtmsgflags.SetMail(TRUE);
					#else
						msgTab_mttohash[slot]	= hash(Msg->GetToUser());
						msgTab_mtmsgflags[slot].SetMail(TRUE);
					#endif
				#endif

				if (*Msg->GetForward())
					{
					#ifdef AUXMEM
						lmf->SetForwarded(TRUE);
					#else
						#ifdef WINCIT
							msgTabWin[slot].mtmsgflags.SetForwarded(TRUE);
						#else
							msgTab_mtmsgflags[slot].SetForwarded(TRUE);
						#endif
					#endif

					if (!Msg->GetSourceID()[0])
						{
						/* use the mtomesg field */
						#ifdef AUXMEM
							lmt->mtomesg = hash(Msg->GetForward());
						#else
							#ifdef WINCIT
								msgTabWin[slot].mtomesg = hash(Msg->GetForward());
							#else
								msgTab_mtomesg[slot] = hash(Msg->GetForward());
							#endif
						#endif
						}
					else
						{
						/* use the mttohash and lose the recipient */
						#ifdef AUXMEM
							lmt->mttohash = hash(Msg->GetForward());
						#else
							#ifdef WINCIT
								msgTabWin[slot].mttohash = hash(Msg->GetForward());
							#else
								msgTab_mttohash[slot] = hash(Msg->GetForward());
							#endif
						#endif
						}
					}
				}

			if (*Msg->GetGroup())
				{
				#ifdef AUXMEM
					lmt->mttohash = hash(Msg->GetGroup());
					lmf->SetLimited(TRUE);
				#else
					#ifdef WINCIT
						msgTabWin[slot].mttohash = hash(Msg->GetGroup());
						msgTabWin[slot].mtmsgflags.SetLimited(TRUE);
					#else
						msgTab_mttohash[slot] = hash(Msg->GetGroup());
						msgTab_mtmsgflags[slot].SetLimited(TRUE);
					#endif
				#endif
				}
			}

		if (Msg->IsNetworkedMail() && !Msg->IsNetworkedMailForHere())
			{
			#ifdef AUXMEM
				lmf->SetNet(TRUE);
			#else
				#ifdef WINCIT
					msgTabWin[slot].mtmsgflags.SetNet(TRUE);
				#else
					msgTab_mtmsgflags[slot].SetNet(TRUE);
				#endif
			#endif
			}

		#ifdef AUXMEM
			if (*Msg->GetX() == 'Y')
				{
				lmf->SetProblem(TRUE);
				}

			if (*Msg->GetX() == 'M')
				{
				lmf->SetModerated(TRUE);
				}
		#else
			#ifdef WINCIT
				if (*Msg->GetX() == 'Y')
					{
					msgTabWin[slot].mtmsgflags.SetProblem(TRUE);
					}

				if (*Msg->GetX() == 'M')
					{
					msgTabWin[slot].mtmsgflags.SetModerated(TRUE);
					}
			#else
				if (*Msg->GetX() == 'Y')
					{
					msgTab_mtmsgflags[slot].SetProblem(TRUE);
					}

				if (*Msg->GetX() == 'M')
					{
					msgTab_mtmsgflags[slot].SetModerated(TRUE);
					}
			#endif
		#endif

	#ifdef AUXMEM
		lmf->SetReceived(
	#else
		#ifdef WINCIT
			msgTabWin[slot].mtmsgflags.SetReceived(
		#else
			msgTab_mtmsgflags[slot].SetReceived(
		#endif
	#endif
				Msg->IsReceived());


	#ifdef AUXMEM
		lmf->SetReply(
	#else
		#ifdef WINCIT
			msgTabWin[slot].mtmsgflags.SetReply(
		#else
			msgTab_mtmsgflags[slot].SetReply(
		#endif
	#endif
				Msg->IsRepliedTo());

	#ifdef AUXMEM
		lmf->SetCensored(
	#else
		#ifdef WINCIT
			msgTabWin[slot].mtmsgflags.SetCensored(
		#else
			msgTab_mtmsgflags[slot].SetCensored(
		#endif
	#endif
				Msg->IsCensored());

	#ifdef AUXMEM
		lmf->SetMadevis(
	#else
		#ifdef WINCIT
			msgTabWin[slot].mtmsgflags.SetMadevis(
		#else
			msgTab_mtmsgflags[slot].SetMadevis(
		#endif
	#endif
				Msg->IsMadeVisible());

	#ifdef AUXMEM
		lmf->SetLocal(
	#else
		#ifdef WINCIT
			msgTabWin[slot].mtmsgflags.SetLocal(
		#else
			msgTab_mtmsgflags[slot].SetLocal(
		#endif
	#endif
				Msg->IsLocal());

	#ifdef AUXMEM
		lmf->SetInuse(
	#else
		#ifdef WINCIT
			msgTabWin[slot].mtmsgflags.SetInuse(
		#else
			msgTab_mtmsgflags[slot].SetInuse(
		#endif
	#endif
				TRUE);

	#ifdef AUXMEM
		lmt->mtroomno = Msg->GetRoomNumber();
	#else
		#ifdef WINCIT
			msgTabWin[slot].mtroomno = Msg->GetRoomNumber();
		#else
			msgTab_mtroomno[slot] = Msg->GetRoomNumber();
		#endif
	#endif

		/* This is special. */
		if	(*Msg->GetCopyOfMessage())
			{
			#ifdef AUXMEM
				lmf->SetCopy(TRUE);
			#else
				#ifdef WINCIT
					msgTabWin[slot].mtmsgflags.SetCopy(TRUE);
				#else
					msgTab_mtmsgflags[slot].SetCopy(TRUE);
				#endif
			#endif

			/* get the ID# */
			copy = atol(Msg->GetCopyOfMessage());

			#ifdef AUXMEM
				lmt->mttohash = long_LO(here - copy);
				lmt->mtauthhash = long_HI(here - copy);
			#else
				#ifdef WINCIT
					msgTabWin[slot].mttohash = long_LO((uint) (here - copy));
					msgTabWin[slot].mtauthhash = long_HI((uint) (here - copy));
				#else
					msgTab_mttohash[slot] = long_LO((uint) (here - copy));
					msgTab_mtauthhash[slot] = long_HI((uint) (here - copy));
				#endif
			#endif
			}

	#if defined(AUXMEM) || defined(WINCIT)
		assert(getRoomNumLMT(slot) < cfg.maxrooms);

		// insert it into room list in message table
		if (FirstMessageInRoom[getRoomNumLMT(slot)] == M_SLOT_ERROR)
			{
			// first message in this room...
			assert(LastMessageInRoom[getRoomNumLMT(slot)] == M_SLOT_ERROR);

			#ifdef AUXMEM
				lmt->PrevRoomMsg = M_SLOT_ERROR;
				lmt->NextRoomMsg = M_SLOT_ERROR;
			#else
				msgTabWin[slot].PrevRoomMsg = M_SLOT_ERROR;
				msgTabWin[slot].NextRoomMsg = M_SLOT_ERROR;
			#endif

			FirstMessageInRoom[getRoomNumLMT(slot)] = here;
			LastMessageInRoom[getRoomNumLMT(slot)] = here;
			}
		else
			{
			assert(LastMessageInRoom[getRoomNumLMT(slot)] != M_SLOT_ERROR);
			assert(FirstMessageInRoom[getRoomNumLMT(slot)] != M_SLOT_ERROR);

			if (here < FirstMessageInRoom[getRoomNumLMT(slot)])
				{
				// this message is older then the old first message in room...
				#ifdef AUXMEM
					lmt->NextRoomMsg = FirstMessageInRoom[lmt->mtroomno];
					lmt->PrevRoomMsg = M_SLOT_ERROR;
				#else
					msgTabWin[slot].NextRoomMsg = FirstMessageInRoom[getRoomNumLMT(slot)];
					msgTabWin[slot].PrevRoomMsg = M_SLOT_ERROR;
				#endif

				// remember: if we put more than one getMsgTab here, we may
				// loose lmt, as it may have to be swapped out.
				assert(indexslot(FirstMessageInRoom[getRoomNumLMT(slot)]) != M_SLOT_ERROR);
				getMsgTab(indexslot(FirstMessageInRoom[getRoomNumLMT(slot)]))->PrevRoomMsg =
						here;

				FirstMessageInRoom[getRoomNumLMT(slot)] = here;
				}
			else if (here > LastMessageInRoom[getRoomNumLMT(slot)])
				{
				// this message is newer than the old last message in room...
				#ifdef AUXMEM
					lmt->PrevRoomMsg = LastMessageInRoom[lmt->mtroomno];
					lmt->NextRoomMsg = M_SLOT_ERROR;
				#else
					msgTabWin[slot].PrevRoomMsg = LastMessageInRoom[getRoomNumLMT(slot)];
					msgTabWin[slot].NextRoomMsg = M_SLOT_ERROR;
				#endif

				// remember: if we put more than one getMsgTab here, we may
				// loose lmt, as it may have to be swapped out.
				assert(indexslot(LastMessageInRoom[getRoomNumLMT(slot)]) != M_SLOT_ERROR);
				getMsgTab(indexslot(LastMessageInRoom[getRoomNumLMT(slot)]))->NextRoomMsg =
						here;

				LastMessageInRoom[getRoomNumLMT(slot)] = here;
				}
			else
				{
				// insert it somewhere...
				m_slot CurrentMessage, LastMessage;

				for (CurrentMessage = FirstMessageInRoom[getRoomNumLMT(slot)];
						CurrentMessage < here;
						LastMessage = CurrentMessage,
						assert(indexslot(CurrentMessage) != M_SLOT_ERROR),
						CurrentMessage = getNextRoomMsg(indexslot(CurrentMessage)))
					{
					assert(CurrentMessage != M_SLOT_ERROR);
					}

				assert(LastMessage != here);

				// insert slot between LastMessage and CurrentMessage
				// remember: lmt is no longer good, because getNextRoomMsg()
				// may have swapped it out of memory.
				assert(indexslot(CurrentMessage) != M_SLOT_ERROR);
				getMsgTab(indexslot(CurrentMessage))->PrevRoomMsg = here;

				assert(indexslot(LastMessage) != M_SLOT_ERROR);
				getMsgTab(indexslot(LastMessage))->NextRoomMsg = here;
				getMsgTab(slot)->PrevRoomMsg = LastMessage;
				getMsgTab(slot)->NextRoomMsg = CurrentMessage;
				}
			}
	#endif
//		}
//	else
//		{
//#ifdef AUXMEM
//		lmf->INUSE =
//#else
//	#ifdef WINCIT
//			msgTabWin[slot].mtmsgflags.INUSE =
//	#else
//			msgTab_mtmsgflags[slot].INUSE =
//	#endif
//#endif
//				FALSE;
//		}

#if VERSION == ALPHA
	VerifyMsgTab();
#endif
	}


#ifndef NDEBUG
#define CHECKINTERVAL 180*60		// how often to check table, maximum

#include "cwindows.h"

void VerifyMsgTab(void)
	{
	static time_t LastChecked;

	if (time(NULL) >= LastChecked + CHECKINTERVAL)
		{
		CITWINDOW *w = CitWindowsMsg(NULL, "Verifying message table...");

		#if defined(AUXMEM) || defined(WINCIT)
			m_slot TableSize = sizetable();
			m_slot CurrentMsg, LastMsg;
			r_slot CurrentRoom;

			for (CurrentMsg = 0; CurrentMsg < TableSize; CurrentMsg++)
				{
				getMsgTab(CurrentMsg)->Checked = !getFlags(CurrentMsg)->IsInuse();
				}

			for (CurrentRoom = 0; CurrentRoom < cfg.maxrooms; CurrentRoom++)
				{
				if (FirstMessageInRoom[CurrentRoom] != M_SLOT_ERROR)
					{
					for (LastMsg = M_SLOT_ERROR,
							CurrentMsg = FirstMessageInRoom[CurrentRoom];

							CurrentMsg != M_SLOT_ERROR;

							LastMsg = CurrentMsg,
							CurrentMsg = getNextRoomMsg(indexslot(CurrentMsg)))
						{
						m_slot CMSlot = indexslot(CurrentMsg);

						if (CMSlot == M_SLOT_ERROR)
							{
							if (w)
								{
								destroyCitWindow(w, FALSE);
								}

							doccr();
							cPrintf("Bad: %ld", CurrentMsg);
							doccr();
							assert(FALSE);
							}

						if (getMsgTab(CMSlot)->Checked)
							{
							if (w)
								{
								destroyCitWindow(w, FALSE);
								}

							doccr();
							cPrintf("Bad: %ld", CurrentMsg);
							doccr();
							assert(FALSE);
							}

						if (getRoomNum(CMSlot) != CurrentRoom)
							{
							if (w)
								{
								destroyCitWindow(w, FALSE);
								}

							doccr();
							cPrintf("Bad: %ld; room: %d:%d", CurrentMsg,
									CurrentRoom, getRoomNum(CMSlot));
							doccr();
							assert(FALSE);
							}

						getMsgTab(CMSlot)->Checked = TRUE;
						}

					if (LastMessageInRoom[CurrentRoom] != LastMsg)
						{
						if (w)
							{
							destroyCitWindow(w, FALSE);
							}

						doccr();
						cPrintf("LMIR: %ld, LM: %ld, room: %d",
								LastMessageInRoom[CurrentRoom], LastMsg,
								CurrentRoom);
						doccr();
						assert(FALSE);
						}
					}
				else
					{
					if (LastMessageInRoom[CurrentRoom] != M_SLOT_ERROR)
						{
						if (w)
							{
							destroyCitWindow(w, FALSE);
							}

						doccr();
						cPrintf("LMIR: %ld, Room: %d",
								LastMessageInRoom[CurrentRoom], CurrentRoom);
						doccr();
						assert(FALSE);
						}
					}
				}

			for (CurrentMsg = 0; CurrentMsg < TableSize; CurrentMsg++)
				{
				if (!getMsgTab(CurrentMsg)->Checked)
					{
					if (w)
						{
						destroyCitWindow(w, FALSE);
						}

					doccr();
					cPrintf("Bad: %ld", CurrentMsg);
					doccr();
					assert(FALSE);
					}

				getMsgTab(CurrentMsg)->Checked = !getFlags(CurrentMsg)->IsInuse();
				}

			for (CurrentRoom = 0; CurrentRoom < cfg.maxrooms; CurrentRoom++)
				{
				if (LastMessageInRoom[CurrentRoom] != M_SLOT_ERROR)
					{
					for (LastMsg = M_SLOT_ERROR,
							CurrentMsg = LastMessageInRoom[CurrentRoom];

							CurrentMsg != M_SLOT_ERROR;

							LastMsg = CurrentMsg,
							CurrentMsg = getPrevRoomMsg(indexslot(CurrentMsg)))
						{
						m_slot CMSlot = indexslot(CurrentMsg);

						if (CMSlot == M_SLOT_ERROR)
							{
							if (w)
								{
								destroyCitWindow(w, FALSE);
								}

							doccr();
							cPrintf("Bad: %ld", CurrentMsg);
							doccr();
							assert(FALSE);
							}

						if (getMsgTab(CMSlot)->Checked)
							{
							if (w)
								{
								destroyCitWindow(w, FALSE);
								}

							doccr();
							cPrintf("Bad: %ld", CurrentMsg);
							doccr();
							assert(FALSE);
							}

						if (getRoomNum(CMSlot) != CurrentRoom)
							{
							if (w)
								{
								destroyCitWindow(w, FALSE);
								}
							doccr();
							cPrintf("Bad: %ld; room: %d:%d", CurrentMsg,
									CurrentRoom, getRoomNum(CMSlot));
							doccr();
							assert(FALSE);
							}

						getMsgTab(CMSlot)->Checked = TRUE;
						}

					if (FirstMessageInRoom[CurrentRoom] != LastMsg)
						{
						if (w)
							{
							destroyCitWindow(w, FALSE);
							}

						doccr();
						cPrintf("LMIR: %ld, LM: %ld, room: %d",
								LastMessageInRoom[CurrentRoom], LastMsg,
								CurrentRoom);
						doccr();
						assert(FALSE);
						}
					}
				}

			for (CurrentMsg = 0; CurrentMsg < TableSize; CurrentMsg++)
				{
				if (!getMsgTab(CurrentMsg)->Checked)
					{
					if (w)
						{
						destroyCitWindow(w, FALSE);
						}

					doccr();
					cPrintf("Bad: %ld", CurrentMsg);
					doccr();
					assert(FALSE);
					}
				}
		#else
			m_slot TableSize = sizetable();
			m_slot CurrentMsg;

			for (CurrentMsg = 0; CurrentMsg < TableSize; CurrentMsg++)
				{
				if (w && (getRoomNum(CurrentMsg) >= cfg.maxrooms))
					{
					destroyCitWindow(w, FALSE);
					}

				assert(FALSE);
				}
		#endif

		LastChecked = time(NULL);
		if (w)
			{
			destroyCitWindow(w, FALSE);
			}
		}
	}
#endif

