/******************************************************************************
 GMApp.cc

	<Description>

	BASE CLASS = public JXApplication

	Copyright © 1997 by Glenn W. Bach.  All rights reserved.
	
	Base code generated by Codemill v0.1.0

 *****************************************************************************/

#include <GMApp.h>
#include <GMMDIServer.h>
#include <GMessageTableDir.h>
#include <GMessageEditDir.h>
#include <GCheckPGPTask.h>
#include <GPrefsMgr.h>
#include <GInboxMgr.h>
#include <GLockFileTask.h>
#include <GPOPRetriever.h>
#include "GMAboutDialog.h"
#include "GMailboxTreeDir.h"
#include "GAddressBookTreeDir.h"
#include "GMailboxChooseSaveFile.h"

#include <JXUserNotification.h>
#include <JXWindow.h>
#include <JXDisplay.h>
#include <JXChooseSaveFile.h>

#include <JString.h>
#include <JFileArray.h>
#include <JStaticBuffer.h>

#include <GMGlobals.h>

#include <jDirUtil.h>
#include <jFileUtil.h>
#include <jUNIXUtil.h>
#include <jStrStreamUtil.h>

#include <fstream.h>

#include <jAssert.h>

extern const JCharacter* kArrowVersionNumberStr;

static const JCharacter* kStateFileName 		= ".arrow.session";
static const JCharacter* kStateFileSignature	= "arrow_sig";
static const JFileVersion kCurrentSessionVersion = 1;

/******************************************************************************
 Constructor

 *****************************************************************************/

GMApp::GMApp
	(
	int* 		argc, 
	char* 		argv[],
	JBoolean*	displayAbout,
	JString*	prevVersStr
	)
	:
	JXApplication(argc, argv),
	itsPOPRetriever(NULL)
{
	*displayAbout = GMCreateGlobals(this);
	if (GGetPrefsMgr()->UsingPopServer())
		{
		itsPOPRetriever = new GPOPRetriever();
		assert(itsPOPRetriever != NULL);
		}

	if (!*displayAbout)
		{
		*prevVersStr = (GGetPrefsMgr())->GetArrowVersionStr();
		if (*prevVersStr == kArrowVersionNumberStr)
			{
			prevVersStr->Clear();
			}
		else
			{
			*displayAbout = kTrue;
			}
		}
	else
		{
		prevVersStr->Clear();
		}
	
	GMMDIServer* mdi = new GMMDIServer(this);
	assert(mdi != NULL);

	itsTableDirs = new JPtrArray<GMessageTableDir>;
	assert(itsTableDirs != NULL);
	
	itsEditDirs = new JPtrArray<GMessageEditDir>;
	assert(itsEditDirs != NULL);

	itsLockTasks = new JPtrArray<GLockFileTask>;
	assert(itsLockTasks != NULL);

	JBoolean ok = OpenSession();

	if (*argc > 1)
		{
		mdi->HandleCmdLineOptions(*argc, argv);
		}		
	else if (!ok)
		{
		OpenSystemMailbox();
		}

	ListenTo(GGetPrefsMgr());
}

/******************************************************************************
 Destructor

 *****************************************************************************/

GMApp::~GMApp()
{
	delete itsPOPRetriever;
	GMDeleteGlobals();
	delete itsTableDirs;
	delete itsEditDirs;
	itsLockTasks->DeleteAll();
	delete itsLockTasks;
}

/******************************************************************************
 Receive

 *****************************************************************************/

void
GMApp::Receive
	(
	JBroadcaster* 					sender,
	const JBroadcaster::Message& 	message
	)
{
	if (sender == GGetPrefsMgr() && message.Is(GPrefsMgr::kPopChanged))
		{
		if (GGetPrefsMgr()->UsingPopServer())
			{
			if (itsPOPRetriever == NULL)
				{
				itsPOPRetriever = new GPOPRetriever();
				assert(itsPOPRetriever != NULL);
				}
			}
		}
}

/******************************************************************************
 CheckMail

 *****************************************************************************/

void
GMApp::CheckMail()
{
	if (itsPOPRetriever != NULL)
		{
		itsPOPRetriever->CheckMail();
		}
	else
		{
		JString report = "In order to check mail, you must configure POP in the "
					 "Mail Preferences dialog. Would you like to do that now?";
		if (JGetUserNotification()->AskUserYes(report))
			{
			GGetPrefsMgr()->EditPrefs();
			}
		}
}

/******************************************************************************
 OpenMailbox

 *****************************************************************************/

void
GMApp::OpenMailbox
	(
	const JCharacter* filename
	)
{
	JString mailbox(filename);
	GMessageTableDir* dir;
	for (JSize i = 1; i <= itsTableDirs->GetElementCount(); i++)
		{
		if (itsTableDirs->NthElement(i)->GetMailFile() == mailbox)
			{
			itsTableDirs->NthElement(i)->Activate();
			return;
			}
		}
	JBoolean locked = FileLocked(mailbox, kFalse);

	if (!locked)
		{
		if (GMessageTableDir::Create(this, mailbox, &dir))
			{
			itsTableDirs->Append(dir);
			}
		}
}

/******************************************************************************
 OpenIconifiedMailbox

 *****************************************************************************/

void
GMApp::OpenIconifiedMailbox
	(
	const JCharacter* 	filename,
	const JBoolean 		beep
	)
{
	JString mailbox(filename);
	GMessageTableDir* dir;
	for (JSize i = 1; i <= itsTableDirs->GetElementCount(); i++)
		{
		JString mailfile = itsTableDirs->NthElement(i)->GetMailFile();
		if (mailfile == mailbox)
			{
			return;
			}
		}

	JBoolean locked = FileLocked(mailbox, kTrue);

	if (!locked)
		{
		if (GMessageTableDir::Create(this, mailbox, &dir, kTrue))
			{
			itsTableDirs->Append(dir);
			if (beep && GGetPrefsMgr()->IsBeepingOnNewMail())
				{
				GetCurrentDisplay()->Beep();
				}
			}
		}
}

/******************************************************************************
 OpenSystemMailbox

 *****************************************************************************/

void
GMApp::OpenSystemMailbox()
{
	JString user = JGetUserName();
	JString smbox = GGetPrefsMgr()->GetDefaultInbox();
/*	if (!(JFileExists(smbox) && JFileReadable(smbox)))
		{
		ofstream os(smbox);
		if (!os.good())
			{
			JGetUserNotification()->ReportError("Your default mailbox does not exist "
												"and I was unable to create it.");
			return;
			}
		os.close();
		}
*/
	OpenMailbox(smbox);
}

/******************************************************************************
 NewMailbox

 *****************************************************************************/

void
GMApp::NewMailbox
	(
	const JCharacter* 	filename,
	const JBoolean 		openFile
	)
{
	JString path;
	JString name;
	JSplitPathAndName(filename, &path, &name);
	if (path.IsEmpty())
		{
		path = JGetCurrentDirectory();
		}
	if (!(JDirectoryExists(path) && JDirectoryReadable(path)))
		{
		JString notice = "You do not have write permissions in directory \"" + path + "\"";
		JGetUserNotification()->ReportError(notice);
		return;
		}
	ofstream os(filename);
	if (!os.good())
		{
		JString notice = "Unable to create file \"" + path + name + "\"";
		JGetUserNotification()->ReportError(notice);
		return;
		}
	os.close();
	if (openFile)
		{
		OpenMailbox(filename);
		}
}

/******************************************************************************
 DirectorClosed

 *****************************************************************************/

void
GMApp::DirectorClosed
	(
	JXDirector* theDirector
	)
{
	GMessageTableDir* dir = dynamic_cast(GMessageTableDir*, theDirector);
	JIndex findex;
	if (itsTableDirs->Find(dir, &findex))
		{
		itsTableDirs->RemoveElement(findex);
		}

	GMessageEditDir* edir = dynamic_cast(GMessageEditDir*, theDirector);
	if (itsEditDirs->Find(edir, &findex))
		{
		itsEditDirs->RemoveElement(findex);
		}
		
	JXApplication::DirectorClosed(theDirector);
}

/******************************************************************************
 Close (virtual)

 ******************************************************************************/

JBoolean
GMApp::Close()
{
	GGetPrefsMgr()->WriteFindDialogPrefs(GetSearchDialog());
	SaveState();
	return JXApplication::Close();
}

/******************************************************************************
 SaveState

 *****************************************************************************/

void
GMApp::SaveState()
{
	JString homeDir;
	if (JGetHomeDirectory(&homeDir))
		{
		JAppendDirSeparator(&homeDir);
		JString session = homeDir + kStateFileName;
		if (JFileExists(session))
			{
			JRemoveFile(session);
			}
		JFileArray* fileArray; 
		JError err = JFileArray::Create(session, kStateFileSignature, &fileArray);
		if (!err.OK())
			{
			if (JFileExists(session))
				{
				JRemoveFile(session);
				}
			delete fileArray;
			return;
			}
		fileArray->SetVersion(kCurrentSessionVersion);

		JSize count;
		{
		ostrstream dataStream;
		count = itsTableDirs->GetElementCount();
		dataStream << count;
		fileArray->AppendElement(dataStream);
		}

		JFAID_t id = 2;
		for (JSize i = 1; i <= count; i++)
			{
			JFileArray* embed;
			err = JFileArray::Create(fileArray, id, &embed);
			itsTableDirs->NthElement(i)->Save();
			itsTableDirs->NthElement(i)->SaveState(*embed);
			delete embed;
			id ++;
			}
			
		for (JSize i = itsEditDirs->GetElementCount(); i >= 1; i--)
			{
			itsEditDirs->NthElement(i)->CheckForPendingMessage();
			}

		{
		ostrstream dataStream;
		count = itsEditDirs->GetElementCount();
		dataStream << count;
		fileArray->AppendElement(dataStream);
		}
		
		for (JSize i = 1; i <= count; i++)
			{
			ostrstream dataStream;
			itsEditDirs->NthElement(i)->SaveState(dataStream);
			fileArray->AppendElement(dataStream);
			}

		{
		ostrstream dataStream;
		GGetInboxMgr()->SaveState(dataStream);
		fileArray->AppendElement(dataStream);
		}

		delete fileArray;
		}
}

/******************************************************************************
 AppendNewMessage

 *****************************************************************************/

void
GMApp::AppendNewMessage
	(
	GMessageEditDir* dir
	)
{
	itsEditDirs->Append(dir);
}

/******************************************************************************
 OpenSession

 *****************************************************************************/

JBoolean
GMApp::OpenSession()
{
	JString homeDir;
	if (JGetHomeDirectory(&homeDir))
		{
		JAppendDirSeparator(&homeDir);
		JString session = homeDir + kStateFileName;
		if (JFileExists(session) && JFileReadable(session))
			{
			JFileArray* fileArray; 
			JError err = JFileArray::Create(session, kStateFileSignature, &fileArray);
			if (err.OK())
				{
				JFileVersion version = fileArray->GetVersion();
				if (version == kCurrentSessionVersion)
					{
					JFAID id = 1;
					JSize count;
					{
					JStaticBuffer data;
					fileArray->GetElement(id, &data);
					jistrstream(is, data.GetData(), strlen(data));
					is >> count;
					id.SetID(id.GetID() + 1);
					}

					if (count == 0)
						{
						delete fileArray;
						JRemoveFile(session);
						return kFalse;
						}
					for (JSize i = 1; i <= count; i++)
						{
						JFileArray* embed;
						err = JFileArray::Create(fileArray, id, &embed);
						GMessageTableDir* dir;
						if (GMessageTableDir::Create(this, *embed, &dir))
							{
							itsTableDirs->Append(dir);
							}
						id.SetID(id.GetID() + 1);
						delete embed;
						}
						
					{
					JStaticBuffer data;
					fileArray->GetElement(id, &data);
					jistrstream(is, data.GetData(), strlen(data));
					is >> count;
					id.SetID(id.GetID() + 1);
					}
					
					for (JSize i = 1; i <= count; i++)
						{
						JStaticBuffer data;
						fileArray->GetElement(id, &data);
						jistrstream(is, data.GetData(), strlen(data));
						GMessageEditDir* dir = new GMessageEditDir(JXGetApplication());
						assert(dir != NULL);
						dir->ReadState(is);
						dir->Activate();
						id.SetID(id.GetID() + 1);
						}

					{
					JStaticBuffer data;
					fileArray->GetElement(id, &data);
					jistrstream(is, data.GetData(), strlen(data));
					GGetInboxMgr()->ReadState(is);
					}
					}
				delete fileArray;
				JRemoveFile(session);
				return kTrue;
				}
			delete fileArray;
			JRemoveFile(session);
			}
		}
	return kFalse;
}

/******************************************************************************
 OpenMailbox

 *****************************************************************************/

void
GMApp::OpenMailbox()
{
	JString mbox;
	if (GMGetMailboxCSF()->ChooseFile("Select mailbox:", "Select the mailbox to open.", &mbox))
		{
		OpenMailbox(mbox);
		}
}

/******************************************************************************
 CleanUpBeforeSuddenDeath

 *****************************************************************************/

void
GMApp::CleanUpBeforeSuddenDeath
	(
	const JXDocumentManager::SafetySaveReason reason
	)
{
	GSetProgramDying();
	JSize count = itsTableDirs->GetElementCount();
	for (JSize i = 1; i <= count; i++)
		{
		itsTableDirs->NthElement(i)->Save();
		}
	SaveState();
	GMGetAddressBookDir()->SaveOnDeath();
}

/******************************************************************************
 FileLocked

 *****************************************************************************/

JBoolean
GMApp::FileLocked
	(
	const JCharacter*	filename,	
	const JBoolean 		iconify
	)
{
	JString lockfile = JString(filename) + ".lock";
	JBoolean locked = kFalse;
	if (JFileExists(lockfile))
		{
		locked = kTrue;
		JBoolean found = kFalse;
		for (JSize i = 1; i <= itsLockTasks->GetElementCount(); i++)
			{
			if (itsLockTasks->NthElement(i)->GetMailbox() == filename)
				{
				JString report;
				if (iconify)
					{
					report = "The mailox \"" + JString(filename) + "\" has new mail, but is locked. Do you want the lock to be retried in 30 seconds?";
					}
				else
					{
					report = "The mailox \"" + JString(filename) + "\" is locked. Do you want the lock to be retried in 30 seconds?";
					}
				JBoolean yes = JGetUserNotification()->AskUserYes(report);
				if (!yes)
					{
					report = "Do you want to forcibly remove the lock on mailbox \"" + JString(filename) + "\"?";
					yes = JGetUserNotification()->AskUserYes(report);
					if (yes)
						{
						GUnlockFile(filename);
						locked = kFalse;
						RemoveIdleTask(itsLockTasks->NthElement(i));
						itsLockTasks->DeleteElement(i);
						}
					else
						{
						RemoveIdleTask(itsLockTasks->NthElement(i));
						itsLockTasks->DeleteElement(i);
						}
					}
				found = kTrue;
				break;
				}
			}
		if (!found)
			{
			JString report;
			if (iconify)
				{
				report = "The mailox \"" + JString(filename) + "\" has new mail, but is locked. Do you want the lock to be retried in 30 seconds?";
				}
			else
				{
				report = "The mailox \"" + JString(filename) + "\" is locked. Do you want the lock to be retried in 30 seconds?";
				}
			JBoolean yes = JGetUserNotification()->AskUserYes(report);
			if (!yes)
				{
				report = "Do you want to forcibly remove the lock on mailbox \"" + JString(filename) + "\"?";
				yes = JGetUserNotification()->AskUserYes(report);
				if (yes)
					{
					GUnlockFile(filename);
					locked = kFalse;
					}
				}
			else
				{
				GLockFileTask* task = new GLockFileTask(filename, iconify);
				assert(task != NULL);
				itsLockTasks->Append(task);
				InstallIdleTask(task);
				}
			}
		}
	else
		{
		for (JSize i = 1; i <= itsLockTasks->GetElementCount(); i++)
			{
			if (itsLockTasks->NthElement(i)->GetMailbox() == filename)
				{
				RemoveIdleTask(itsLockTasks->NthElement(i));
				itsLockTasks->DeleteElement(i);
				break;
				}
			}
		}
	return locked;
}

/******************************************************************************
 DisplayAbout

	prevVersStr can be NULL.

 ******************************************************************************/

void
GMApp::DisplayAbout
	(
	const JCharacter* prevVersStr
	)
{
	GMAboutDialog* dlog = new GMAboutDialog(this, prevVersStr);
	assert( dlog != NULL );
	dlog->BeginDialog();
}

/******************************************************************************
 NewMessage (public)

 ******************************************************************************/

void
GMApp::NewMessage
	(
	const JCharacter* to
	)
{
	GMessageEditDir* dir = new GMessageEditDir(this);
	assert(dir != NULL);
	JString theTo(to);
	if (!theTo.IsEmpty())
		{
		dir->SetTo(theTo);
		}
	dir->Activate();
}

/******************************************************************************
 Suspend (virtual)

 ******************************************************************************/

void
GMApp::Suspend()
{
	JXApplication::Suspend();
	GMSuspend();
}

/******************************************************************************
 Resume (virtual)

 ******************************************************************************/

void
GMApp::Resume()
{
	JXApplication::Resume();
	GMResume();
}
