/* PR-QWK - QWK Importer for UMS
 * Copyright (C) 1998 J.Ross Nicoll
 *
 * This program is free software; you can redistribute it and/or
 * modify it under the terms of the GNU General Public License
 * as published by the Free Software Foundation; either version 2
 * of the License, or (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program; if not, write to the Free Software
 * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
 */

#include <dos/dosextens.h>
#include <utility/tagitem.h>

#include <proto/alib.h>
#include <proto/asyncio.h>
#include <proto/dos.h>
#include <proto/intuition.h>
#include <proto/ums.h>
#include <proto/utility.h>

#include <stdlib.h>
#include <string.h>

#define CATCOMP_NUMBERS

#include "locale.h"
#include "pr-qwk.h"

static BOOL ErrorReading = FALSE;
static BOOL ErrorWriting = FALSE;
static const TEXT MessagesFilename[] = {"MESSAGES.DAT"};
static STRPTR ReadMsgBuffer;
static STRPTR WriteMsgBuffer;
static ULONG ReadMsgBufferSize;

#define Prototype extern
#include "PR-QWK-protos.h"
Prototype BOOL ExportMessages(STRPTR);
Prototype BOOL ImportMessages(VOID);
Prototype BOOL ReadMessage(struct AsyncFile *, struct QWKMessage *);
Prototype BOOL WriteMessage(struct AsyncFile *, struct UMSMessage *);
Prototype VOID NeatenCase(STRPTR);
Prototype VOID UpperCase(STRPTR);

BOOL ExportMessages(STRPTR To)
{
	struct AsyncFile *file;
	struct UMSMessage message;
	struct TagItem readTags[] = {
		UMSTAG_RMsgNum,	0L,
		UMSTAG_RFromName,	(ULONG)&message.From,
		UMSTAG_RToName,	(ULONG)&message.To,
		UMSTAG_RMsgDate,	(ULONG)&message.Date,
		UMSTAG_RSubject,	(ULONG)&message.Subject,
		UMSTAG_RGroup,		(ULONG)&message.Conference,
		UMSTAG_RMsgText,	(ULONG)&WriteMsgBuffer,
		TAG_DONE,			0L
	};
	struct TagItem searchTags[] = {
		UMSTAG_SearchLast, 0,
		UMSTAG_SearchLocal, TRUE,
		UMSTAG_SearchMask, 1,
		UMSTAG_SearchMatch, 1,
		TAG_DONE, 0
	};
	struct TagItem selectTags[] = {
		UMSTAG_SelMask, (UMSUSTATF_PostPoned | UMSUSTATF_Old | UMSUSTATF_ViewAccess),
		UMSTAG_SelMatch, UMSUSTATF_ViewAccess,
		UMSTAG_SelSet, 1,
		UMSTAG_SelWriteLocal, TRUE,
		TAG_DONE, 0
	};
	TEXT tempStore[128];
	ULONG date;
	ULONG endTime;
	ULONG errors = 0;
	ULONG messages;
	ULONG mps;
	ULONG msgNum;
	ULONG readError;
	ULONG size;
	ULONG startTime;

	/* Find all messages that I can read, and are not postponed or old */
	messages = UMSSelect(Account, selectTags);
	if(messages == 0)
	{
		PutStr(GetString(INFOMSG_NO_MESSAGES_EXPORT));
		return(2);
	}
	messages = 0;

	/* Open to file to export to */
	file = OpenAsync(To, MODE_WRITE, BufferSize);
	if(file == NULL)
	{
		file = OpenAsync(To, MODE_WRITE, 4096);
	}

	if(file != NULL)
	{
		REPCpy(tempStore, Site, 128);
		if(WriteAsync(file, tempStore, 128) == 128)
		{
			CurrentTime(&startTime, &readError);
			msgNum = UMSSearch(Account, searchTags);
			while(msgNum != 0)
			{
				searchTags[0].ti_Data = msgNum;
				readTags[0].ti_Data = msgNum;
				readError = UMSReadMsg(Account, readTags);
				if(readError == FALSE)
				{
					PutStr("E\n");
					if(HandleUMSError() == FALSE)
					{
						CloseAsync(file);
						return(FALSE);
					}
					errors++;
				}
				else
				{
					if((message.Conference != NULL) &&
						(Strnicmp(message.Conference, BaseGroup, BaseGroupLen) != 0))
					{
						PutStr("E\n");
						errors++;
					}
					else
					{
						message.Conference = message.Conference + BaseGroupLen;
						message.ConferenceNum = atoi(message.Conference);
						PutC('.');
						Amiga2Text(date, &message.Date, &message.Time);
						size = strlen(WriteMsgBuffer);
						message.Blocks = (size >> 7);
						if((message.Blocks << 7) < size)message.Blocks++;

						if(WriteMessage(file, &message) == FALSE) return(FALSE);

						messages++;
						UMSFreeMsg(Account, msgNum);
					}
				}

				msgNum = UMSSearch(Account, searchTags);
			}
			if(ErrorWriting == TRUE) errors++;
			CurrentTime(&endTime, &readError);
			PutC('\n');

			CloseAsync(file);

			startTime = endTime - startTime;
			if(startTime == 0) startTime = 1;
			mps = messages / startTime;
			SPrintF(FormatBuffer, GetString(INFOMSG_EXPORTED_NUM), messages, errors, startTime, mps);
			PutStr(FormatBuffer);

			if(messages == 0) return(2);

			return(TRUE);
		}
		else
		{
			HandleIOError(IOMSG_WRITE);
		}
		CloseAsync(file);
	}
	else
	{
		HandleIOError(IOMSG_OPEN);
	}

	return(FALSE);
}

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

BOOL ImportMessages(VOID)
{
	struct AsyncFile *file;
	struct QWKMessage message;
	struct TagItem selectTags[] = {
		UMSTAG_SelSet,		UMSUSTATF_Old,
		UMSTAG_SelMsg,		0,
		UMSTAG_SelWriteUser, (ULONG)ControlData.Username,
		TAG_DONE, 			0
	};
	struct TagItem writeTags[] = {
		UMSTAG_WFromName,	(ULONG)message.From,
		UMSTAG_WFromAddr,	(ULONG)SiteAddr,
		UMSTAG_WToName,	(ULONG)message.To,
		UMSTAG_WCreationDate, (ULONG)message.Date,
		UMSTAG_WSubject,	(ULONG)message.Subject,
		UMSTAG_WGroup,		0L,
		UMSTAG_WMsgText,	0L,
		TAG_DONE,			0L
	};
	ULONG allocation;
	ULONG endTime;
	ULONG errors = 0;
	ULONG messages = 0;
	ULONG mps;
	ULONG startTime;
	ULONG temp;

	/* Create the buffer to read the message into */
	ReadMsgBufferSize = 128;
	allocation = (ReadMsgBufferSize + 1);
	ReadMsgBuffer = AllocPoolVec(allocation);
	if(ReadMsgBuffer != NULL)
	{
		/* Open the file to read from */
		file = OpenAsync(MessagesFilename, MODE_READ, BufferSize);
		if(file == NULL)
		{
			file = OpenAsync(MessagesFilename, MODE_READ, 4096);
		}

		if(file != NULL)
		{
			/* Skip the copyright notice at the start of the packet */
			if(ReadAsync(file, ReadMsgBuffer, 128) == 128)
			{
				CurrentTime(&startTime, &temp);
				/* We could read in as many messages as are in CONTROL.DAT, but
				 * some doors give incorrect numbers.
				 */
				while(ReadMessage(file, &message) == TRUE)
				{
					/* This was the best way I could find of doing this. If anyone
					 * has any better suggestions, I'm listening. Oh, and using a
					 * pre-defined conference for messages that have none is more
					 * trouble than it's worth, IMHO.
					 */
					if(message.Conference != NULL)
					{
						/* Setup the tags */
						writeTags[5].ti_Data = (ULONG)message.Conference -> Node.ln_Name;
						writeTags[6].ti_Data = (ULONG)ReadMsgBuffer;

						/* Attempt to write the message to the database */
						temp = UMSWriteMsg(Account, writeTags);
					}

					if((temp == 0) ||
						(message.Conference == 0))
					{
						/* The attempt failed, so display an error */
						PutStr("E\n");

						if(temp == 0)
						{
							HandleUMSError();
							if(FatalUMSError == TRUE)
							{
								/* Fatal UMS error; panic and exit */
								FreePoolVec(ReadMsgBuffer);
								CloseAsync(file);

								return(FALSE);
							}
						}
						else
						{
							SPrintF(FormatBuffer, GetString(ERRMSG_NO_SUCH_CONFERENCE), message.ConferenceNum);
							PutStr(FormatBuffer);
						}

						errors++;
					}
					else
					{
						/* If the message is marked in the packet as having been
						 * read, then set the message as old.
						 */
						if(message.Status != 0)
						{
							selectTags[1].ti_Data = temp;
							UMSSelect(Account, selectTags);
						}

						PutC('.');
						Flush(OutputHandle);
						messages++;
					}
				}

				CurrentTime(&endTime, &temp);
				PutC('\n');
				if(ErrorReading == TRUE) errors++;

				if(ReadMsgBuffer != NULL) FreePoolVec(ReadMsgBuffer);
				CloseAsync(file);

				/* Calculate the number of messages imported, per second */
				startTime = endTime - startTime;
				if(startTime == 0) startTime = 1;
				mps = (messages / startTime);
				SPrintF(FormatBuffer, GetString(INFOMSG_IMPORTED_NUM), messages, errors, startTime, mps);
				PutStr(FormatBuffer);

				return(TRUE);
			}
			else
			{
				HandleIOError(IOMSG_READ);
				FreePoolVec(ReadMsgBuffer);
			}

			CloseAsync(file);
		}
		else
		{
			HandleIOError(IOMSG_OPEN);
			FreePoolVec(ReadMsgBuffer);

			return(TRUE);
		}
	}
	else
	{
		HandleMemoryError(allocation, MISCMSG_MESSAGE_BUFFER);
	}

	return(FALSE);
}

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

BOOL ReadMessage(struct AsyncFile *File, struct QWKMessage *Message)
{
	TEXT loadBuffer[128];
	UBYTE charStore;
	STRPTR work;
	ULONG allocation;
	ULONG size;

	/* Read in the message header */
	if(ReadAsync(File, loadBuffer, 128) == 128)
	{
		charStore = loadBuffer[0];
		Message -> Status = charStore;
		if(charStore != 0)
		{
			if(charStore != '-' && charStore != '`' && charStore != '^' && charStore != '#' && charStore != '*')
			{
				Message -> Status = 0;
			}

			/* Find out the message number */
			charStore = loadBuffer[8];
			loadBuffer[8] = 0;
			StrToLong(&loadBuffer[1], &Message -> MsgNum);
			loadBuffer[8] = charStore;

			/* Get the date and time that the message was written */
			strncpy(Message -> Date, &loadBuffer[8], 8);
			/* Message -> Date[8] = ' '; */
			Message -> Date[8] = 0;
			strncpy(Message -> Time, &loadBuffer[16], 5);
			Message -> Time[5] = 0;

			/* Read in the name of the person the message is from, trimming off
			 * any padding, and then correct the case.
			 */
			QWKCpy(Message -> From, &loadBuffer[46], 25);
			NeatenCase(Message -> From);

			/* Read in the name of the person the message is to, trimming off
			 * any padding, and then correct the case.
			 */
			QWKCpy(Message -> To, &loadBuffer[21], 25);
			NeatenCase(Message -> To);

			/* Read in the subject of the message, or set it to " " if none is
			 * supplied.
			 */
			QWKCpy(Message -> Subject, &loadBuffer[71], 25);
			if(*Message -> Subject == 0)
			{
				*Message -> Subject = ' ';
				*(Message -> Subject + 1) = 0;
			}

			/* This should indicate if this message is active or should be
			 * killed, but we don't pay any attention to it.
			 */
			Message -> Flag = loadBuffer[122];

			/* Find out the number of blocks in the message */
			loadBuffer[122] = 0;
			StrToLong(&loadBuffer[116], &Message -> Blocks);
			Message -> Blocks--;

			/* Conference number. Byte swapped if necessary */
			if(ControlData.HighConference < 256)
			{
				Message -> ConferenceNum = loadBuffer[123];
			}
			else
			{
				Message -> ConferenceNum = *((UWORD *)&loadBuffer[123]);
				Message -> ConferenceNum = SwapBytes(Message -> ConferenceNum);
			}

			size = Message -> Blocks * 128;
			if(ReadMsgBufferSize < size)
			{
				ReadMsgBufferSize = size;
				FreePoolVec(ReadMsgBuffer);

				allocation = (ReadMsgBufferSize + 1);
				ReadMsgBuffer = AllocPoolVec(allocation);
			}

			if(ReadMsgBuffer != NULL)
			{
				if(ReadAsync(File, ReadMsgBuffer, size) == size)
				{
					*(ReadMsgBuffer + size) = 0;
					StripTrailing(ReadMsgBuffer);

					for(work = ReadMsgBuffer; *work != NULL; work++)
					{
						if(*work == 227) *work = 10;
					}

					Message -> Conference = FindConference(Message -> ConferenceNum);

					return(TRUE);
				}
			}
			else
			{
				PutStr("\n");
				HandleMemoryError(allocation, MISCMSG_MESSAGE_BUFFER);
				ErrorReading = TRUE;
			}
		}
	}

	return(FALSE);
}

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

BOOL WriteMessage(struct AsyncFile *File, struct UMSMessage *Message)
{
	TEXT saveBuffer[128];
	TEXT charStore[7];
	TEXT pi = 227;
	STRPTR work;
	ULONG size;

	PutStr("Writing message\n");
	Flush(Output());
	memset(saveBuffer, ' ', 128);
	if(Message -> Conference == NULL)
	{
		saveBuffer[0] = '*';
	}
	else
	{
		saveBuffer[0] = ' ';
	}

	SPrintF(charStore, "%d", Message -> ConferenceNum);
	Flush(Output());
	REPCpy(&saveBuffer[1], charStore, 6);

	REPCpy(&saveBuffer[8], Message -> Date, 8);

	REPCpy(&saveBuffer[16], Message -> Time, 5);

	UpperCase(Message -> To);
	REPCpy(&saveBuffer[21], Message -> To, 25);

	UpperCase(Message -> From);
	REPCpy(&saveBuffer[46], Message -> From, 25);

	REPCpy(&saveBuffer[71], Message -> Subject, 25);

	saveBuffer[122] = 225;

	SPrintF(charStore, "%d", (Message -> Blocks + 1));
	REPCpy(&saveBuffer[116], charStore, 6);

	Message -> ConferenceNum = SwapBytes(Message -> ConferenceNum);
	*((UWORD *)&saveBuffer[123]) = Message -> ConferenceNum;

	for(work = strchr(WriteMsgBuffer, 10); work != NULL; work = strchr(work, 10)) *work = 227;

	if(WriteAsync(File, saveBuffer, 128) == 128)
	{
		size = strlen(WriteMsgBuffer) + 1;
		work = WriteMsgBuffer;
		while(size > 127)
		{
			if(WriteAsync(File, work, 128) != 128)
			{
				break;
			}

			size = size - 128;
			work = work + 128;
		}

		if(size <= 127)
		{
			if(WriteAsync(File, work, size) == size)
			{
				size = 127 - size;
				if(WriteAsync(File, &pi, 1) == 1)
				{
					if(WriteAsync(File, &Blank, size) == size)
					{
						return(TRUE);
					}
				}
			}
		}
	}

	return(FALSE);
}
