/******************************************************************************
 GMGlobals.cc

	Access to testjx global objects and factories.

	Written by Glenn W. Bach.

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

#include "GMGlobals.h"
#include "GMApp.h"
#include "GNetDebugDir.h"
#include "GAddressBookMgr.h"
#include "GPrefsMgr.h"
#include "GInboxMgr.h"
#include "GHelpText.h"
#include "ClipDir.h"
#include "JXSRFullDialog.h"
#include "GMHelpPrefObject.h"
#include "GMSpellCheckPrefObject.h"
#include "GMChooseSaveFile.h"
#include "GMPTPrinter.h"
#include "GMDirectorManager.h"
#include "GMailboxChooseSaveFile.h"
#include "GMailboxTreeDir.h"
#include "GAddressBookTreeDir.h"

#include <JXWindow.h>
#include <JXPTPrinter.h>

#include <JUserNotification.h>
#include <JUNIXDirEntry.h>
#include <JInPipeStream.h>
#include <JRegex.h>

#include <jProcessUtil.h>
#include <jStreamUtil.h>
#include <jUNIXUtil.h>
#include <jDirUtil.h>
#include <jFileUtil.h>
#include <jTime.h>

#include <unistd.h>
#include <stdlib.h>
#include <jAssert.h>

static GMApp*				theApplication 			= NULL;		// owns itself
static JXDirector*			thePermWindowOwner   	= NULL;
static GNetDebugDir*		itsSMTPDebugDir			= NULL;
static GNetDebugDir*		itsPOPDebugDir			= NULL;
static JBoolean 			itsPGPEnabled;
static JBoolean 			itsGPGEnabled;
static GAddressBookMgr* 	itsAddressBookMgr 		= NULL;
static GPrefsMgr*			itsPrefsMgr				= NULL;
static GInboxMgr*			itsInboxMgr				= NULL;
static JBoolean 			itsProgramIsDying		= kFalse;
static JBoolean				itsSpellCheckEnabled;
static JXSRTextDialogBase*	itsDialog				= NULL;
static JXPTPrinter*			itsPrinter				= NULL;
static GMHelpPrefObject*	itsHelpPrefsObj			= NULL;
static GMChooseSaveFile*	itsAltChooseSaveFile	= NULL;
static GMPTPrinter*			itsAltPTPrinter			= NULL;
static GMDirectorManager*	itsDirMgr				= NULL;
static GMailboxTreeDir*		itsMailboxTreeDir		= NULL;
static GAddressBookTreeDir*	itsAddressBookTreeDir	= NULL;

static GMSpellCheckPrefObject*	itsSpellCheckPrefsObj	= NULL;
static GMailboxChooseSaveFile*	itsMailboxCSF			= NULL;

static JBoolean				itsLockFilePrgExists = kFalse;

const JCharacter* smtpDebugTitle					= "SMTP Debug Window";
const JCharacter* popDebugTitle						= "POP Debug Window";

static const JCharacter* kGMWMInstance          	= "arrow";
static const JCharacter* kTableWindowClass       	= "Arrow_Table";
static const JCharacter* kViewWindowClass          	= "Arrow_View";
static const JCharacter* kEditorWindowClass        	= "Arrow_Editor";
static const JCharacter* kAddressBookWindowClass 	= "Arrow_Address_Book";
static const JCharacter* kMailboxWindowClass 		= "Arrow_Mailbox";

/******************************************************************************
 GMCreateGlobals

	server can be NULL

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

JBoolean
GMCreateGlobals
	(
	GMApp*		app
	)
{
	theApplication = app;

	itsDirMgr = new GMDirectorManager();
	assert(itsDirMgr != NULL);
	
	itsAddressBookMgr = new GAddressBookMgr();
	assert(itsAddressBookMgr != NULL);

	itsPGPEnabled = kTrue;

	JXInitHelp(kTOCHelpName, kHelpSectionCount,
			   kHelpSectionName, kHelpSectionTitle, kHelpSectionText,
			   JXMenu::kMacintoshStyle);

	JBoolean isNew;
	itsPrefsMgr = new GPrefsMgr(&isNew);
	assert(itsPrefsMgr != NULL);

	itsInboxMgr = new GInboxMgr();
	assert(itsInboxMgr != NULL);

	GCheckPGP();
	GGheckGPG();

	itsSpellCheckEnabled = JProgramOnPath("ispell");

	thePermWindowOwner = new JXDirector(NULL);
	assert( thePermWindowOwner != NULL );
	
	ClipDir* dir = new ClipDir(thePermWindowOwner);
	assert(dir != NULL);

	itsDialog = new JXSRFullDialog(thePermWindowOwner);
	assert(itsDialog != NULL);

	itsMailboxTreeDir = new GMailboxTreeDir(thePermWindowOwner);
	assert(itsMailboxTreeDir != NULL);

	itsAddressBookTreeDir = new GAddressBookTreeDir(thePermWindowOwner);
	assert(itsAddressBookTreeDir != NULL);

	itsSMTPDebugDir = new GNetDebugDir(thePermWindowOwner, smtpDebugTitle);
	assert(dir != NULL);

	itsPOPDebugDir = new GNetDebugDir(thePermWindowOwner, popDebugTitle);
	assert(dir != NULL);

	itsPrefsMgr->ReadFindDialogPrefs(itsDialog);
	itsPrefsMgr->ReadChooseSaveDialogPrefs();

	itsPrinter = new JXPTPrinter();
	assert(itsPrinter != NULL);

	itsPrefsMgr->ReadPrinterSetup(itsPrinter);

	itsHelpPrefsObj = new GMHelpPrefObject(itsPrefsMgr, kGHelpSetupID);
	assert(itsHelpPrefsObj != NULL);

	itsSpellCheckPrefsObj = new GMSpellCheckPrefObject(itsPrefsMgr, kGSpellCheckSetupID);
	assert(itsSpellCheckPrefsObj != NULL);

	itsAltChooseSaveFile = new GMChooseSaveFile(itsPrefsMgr, kGAltChooseSaveSetupID);
	assert(itsAltChooseSaveFile != NULL);

	itsAltPTPrinter = new GMPTPrinter(itsPrefsMgr, kGSpecialPTPrintSetupID);
	assert(itsAltPTPrinter != NULL);

	itsMailboxCSF = new GMailboxChooseSaveFile();
	assert(itsMailboxCSF != NULL);

	if (JProgramOnPath("lockfile"))
		{
		itsLockFilePrgExists = kTrue;
		}
		
	return isNew;	
}

/******************************************************************************
 GMDeleteGlobals

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

void
GMDeleteGlobals()
{	
	delete itsHelpPrefsObj;
	delete itsSpellCheckPrefsObj;
	delete itsAltChooseSaveFile;
	delete itsAltPTPrinter;
	delete itsMailboxCSF;
	itsPrefsMgr->WritePrinterSetup(itsPrinter);
	delete itsPrinter;
	itsPrefsMgr->WriteChooseSaveDialogPrefs();
	theApplication = NULL;
	delete itsInboxMgr;
	
	const JBoolean ok = thePermWindowOwner->Close();
	assert( ok );

	thePermWindowOwner   = NULL;
	itsDialog			 = NULL;

	delete itsPrefsMgr;
	delete itsAddressBookMgr;
	delete itsDirMgr;
}

/******************************************************************************
 GMSuspend

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

void
GMSuspend()
{
	thePermWindowOwner->Suspend();
}

/******************************************************************************
 GMResume

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

void
GMResume()
{
	thePermWindowOwner->Resume();
}

/******************************************************************************
 GMGetApplication

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

GMApp*
GMGetApplication()
{
	assert( theApplication != NULL );
	return theApplication;
}

/******************************************************************************
 GGetPrefsMgr

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

GPrefsMgr*
GGetPrefsMgr()
{
	assert(itsPrefsMgr != NULL);
	return itsPrefsMgr;
}

/******************************************************************************
 GGetInboxMgr

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

GInboxMgr*
GGetInboxMgr()
{
	assert(itsInboxMgr != NULL);
	return itsInboxMgr;
}

/******************************************************************************
 GMGetSMTPDebugDir

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

GNetDebugDir*
GMGetSMTPDebugDir()
{
	assert( itsSMTPDebugDir != NULL );
	return itsSMTPDebugDir;
}

/******************************************************************************
 GMGetPOPDebugDir

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

GNetDebugDir*
GMGetPOPDebugDir()
{
	assert( itsPOPDebugDir != NULL );
	return itsPOPDebugDir;
}

/******************************************************************************
 GGetAddressBookMgr


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

GAddressBookMgr* 
GGetAddressBookMgr()
{
	assert(itsAddressBookMgr != NULL);
	return itsAddressBookMgr;
}

/******************************************************************************
 GPGP_2_6_Enabled


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

JBoolean 
GPGP_2_6_Enabled()
{
	return itsPGPEnabled;
}

/******************************************************************************
 GGPG_1_0_Enabled


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

JBoolean 
GGPG_1_0_Enabled()
{
	return itsGPGEnabled;
}

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


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

JBoolean
GEncryptionEnabled()
{
	if (itsPrefsMgr->GetEncryptionType() == GPrefsMgr::kPGP2_6)
		{
		return itsPGPEnabled;
		}
	else
		{
		return itsGPGEnabled;
		}
}

/******************************************************************************
 GSpellCheckEnabled


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

JBoolean 
GSpellCheckEnabled()
{
	return itsSpellCheckEnabled;
}

/******************************************************************************
 GCheckPGP


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

void
GCheckPGP()
{
	if (!JProgramOnPath("pgp"))
		{
		itsPGPEnabled = kFalse;
		return;
		}
	JString sysCmd = "pgp -kv";
	int inFD;
	JError err = 
		JExecute(sysCmd, NULL,
			kJIgnoreConnection, NULL,
			kJCreatePipe, &inFD,
			kJTossOutput, NULL);
	if (err.OK())
		{
		JInPipeStream is(inFD, kTrue);
		if (is.peek())
			{
			JString buffer;
			JReadAll(is, &buffer);
			JRegex regex;
			JBoolean matched;
			JArray<JIndexRange> subList;
			err = regex.SetPattern("([0-9]+) matching key[s]* found");
			assert(err.OK());
			matched = regex.Match(buffer, &subList);
			if (matched)
				{
				JIndexRange sRange = subList.GetElement(2);
				JString number = buffer.GetSubstring(sRange);
				if (number == "0")
					{
					itsPGPEnabled = kTrue;
					}
				}
			else
				{
				itsPGPEnabled = kFalse;
				}
			}
		else
			{
			itsPGPEnabled = kFalse;
			}
		}
	else
		{
		itsPGPEnabled = kFalse;
		}
}

/******************************************************************************
 GGheckGPG


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

void
GGheckGPG()
{
	if (!JProgramOnPath("gpg"))
		{
		itsGPGEnabled = kFalse;
		return;
		}
	JString sysCmd = "gpg --list-key --no-secmem-warning";
	int fromFD;
	JError err = 
		JExecute(sysCmd, 		NULL,
			kJIgnoreConnection,	NULL,
			kJCreatePipe, 		&fromFD,
			kJTossOutput, 		NULL);
	if (err.OK())
		{
		JInPipeStream is(fromFD, kTrue);
		JString buffer;
		JReadAll(is, &buffer);
		buffer.TrimWhitespace();
		if (buffer.IsEmpty())
			{
			itsGPGEnabled = kFalse;
			}
		}
	else
		{
		itsGPGEnabled = kFalse;
		}		
	itsGPGEnabled = kTrue;
}

/******************************************************************************
 GBlockUntilFileUnlocked


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

void
GBlockUntilFileUnlocked
	(
	const JCharacter* filename
	)
{
	GMGetApplication()->DisplayBusyCursor();
	JString lockfile = JString(filename) + ".lock";
	if (!JFileExists(lockfile))
		{
		return;
		}
	for (JSize count = 1; count <= 6; count++)
		{
		JWait(0.5);
		if (!JFileExists(lockfile))
			{
			return;
			}
		}
	JString report = "The file \"" + JString(filename) + "\" is locked. Waiting for the file to be unlocked.";
	if (!itsProgramIsDying)
		{
		JGetUserNotification()->ReportError(report);
		}
	for (JSize count = 1; count <= 60; count++)
		{
		JWait(0.5);
		if (!JFileExists(lockfile))
			{
			return;
			}
		}
	GUnlockFile(filename);
}

/******************************************************************************
 GLockFile


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

JBoolean
GLockFile
	(
	const JCharacter* filename
	)
{
	JString lockfile = JString(filename) + ".lock";
	ofstream os(lockfile, ios::out, 0444);
	if (!os.good())
		{
		JString username;
		char* loginname = getlogin();
		if (loginname != NULL)
			{
			username = loginname;
			}
		else
			{
			username = JGetUserName();
			}
		
		JString inbox = "/var/spool/mail/" + username;
		if ((inbox == filename) && itsLockFilePrgExists)
			{
			JString cmd = "lockfile -ml";
			JString errStr;
			const JError err = JRunProgram(cmd, &errStr);
			if (!err.OK())
				{
				err.ReportError();
				JString report = "I was unable to lock the mailbox \"" + 
								 JString(filename) +
								 "\", so I won't be able to save your changes.";
				JGetUserNotification()->ReportError(report);
				}
			return kTrue;
			}
		else
			{
			JString report = "I was unable to lock the mailbox \"" + 
							 JString(filename) +
							 "\", so I won't be able to save your changes.";
			JGetUserNotification()->ReportError(report);
			return kFalse;
			}
		}
	return kTrue;
}

/******************************************************************************
 GFileLocked


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

JBoolean
GFileLocked
	(
	const JCharacter* filename
	)
{
	JString lockfile = JString(filename) + ".lock";
	if (JFileExists(lockfile))
		{
		return kTrue;
		}
	return kFalse;
}

/******************************************************************************
 GUnlockFile


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

void
GUnlockFile
	(
	const JCharacter* filename
	)
{
	JString lockfile = JString(filename) + ".lock";
	if (!JFileExists(lockfile))
		{
		return;
		}
	const JError err = JRemoveFile(lockfile);
	if (err.OK())
		{
		return;
		}
	if (!JFileExists(lockfile))
		{
		return;
		}
	JString username;
	char* loginname = getlogin();
	if (loginname != NULL)
		{
		username = loginname;
		}
	else
		{
		username = JGetUserName();
		}
	
	JString inbox = "/var/spool/mail/" + username;
	if ((inbox == filename) && itsLockFilePrgExists)
		{
		JString cmd = "lockfile -mu";
		JString errStr;
		const JError err = JRunProgram(cmd, &errStr);
		if (!err.OK())
			{
			JGetUserNotification()->ReportError(errStr);
			}
		}
}

/******************************************************************************
 GProgramIsDying


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

JBoolean 
GProgramIsDying()
{
	return itsProgramIsDying;
}

/******************************************************************************
 GSetProgramDying


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

void 
GSetProgramDying()
{
	itsProgramIsDying = kTrue;
}

/******************************************************************************
 GQuoteString

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

void
GQuoteString
	(
	JString* 		text,
	const JBoolean 	quoteFirst
	)
{
	if (quoteFirst)
		{
		if (text->IndexValid(1))
			{
			if (text->GetCharacter(1) == '>')
				{
				text->InsertSubstring(">", 1);
				}
			else
				{
				text->InsertSubstring("> ", 1);
				}
			}
		}
	JIndex index = 1;
	while (text->IndexValid(index) && text->LocateNextSubstring("\n", &index))
		{
		if (text->IndexValid(index + 1))
			{
			if (text->GetCharacter(index+1) == '>')
				{
				text->InsertSubstring(">", index+1);
				}
			else if (text->GetCharacter(index+1) == '\n')
				{
				text->InsertSubstring(">", index+1);
				}
			else
				{
				text->InsertSubstring("> ", index+1);
				}
			}
		index ++;
		}

}

/******************************************************************************
 GCheckMail

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

void
GCheckMail()
{
	theApplication->CheckMail();
}

/******************************************************************************
 GCheckMail

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

JXSRTextDialogBase*
GetSearchDialog()
{
	assert(itsDialog != NULL);
	return itsDialog;
}

/******************************************************************************
 GDBGetPTPrinter

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

JXPTPrinter*
GMGetPTPrinter()
{
	return itsPrinter;
}

/******************************************************************************
 GMReplaceFileWithTemp

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

JBoolean
GMReplaceFileWithTemp
	(
	const JCharacter* tempname, 
	const JCharacter* filename
	)
{
	JString tempStr(tempname);
	JString fileStr(filename);
	JString cmd = "mv -f \"" + tempStr + "\" \"" + fileStr + "\"";
	JString errStr;
	JError err = JRunProgram(cmd, &errStr);
	if (err.OK())
		{
		return kTrue;
		}
	cmd = "cp -f \"" + tempStr + "\" \"" + fileStr + "\"";
	err = JRunProgram(cmd, &errStr);
	if (err.OK())
		{
		JRemoveFile(tempStr);
		return kTrue;
		}
	// do it by hand...
	JSize size;
	err = JGetFileLength(tempname, &size);
	if (!err.OK())
		{
		return kFalse;
		}
	ifstream is(tempname);
	if (!is.good())
		{
		return kFalse;
		}
	ofstream os(filename);
	if (!os.good())
		{
		return kFalse;
		}
	JCopyBinaryData(is, os, size);
	JRemoveFile(tempStr);
	return kTrue;
}

/******************************************************************************
 GMGetAltChooseSaveFile

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

GMChooseSaveFile*	
GMGetAltChooseSaveFile()
{
	return itsAltChooseSaveFile;
}

/******************************************************************************
 GMGetAltPTPrinter

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

GMPTPrinter*		
GMGetAltPTPrinter()
{
	return itsAltPTPrinter;
}

/******************************************************************************
 GGetDirMgr

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

GMDirectorManager*
GGetDirMgr()
{
	return itsDirMgr;
}

/******************************************************************************
 GMGetMailboxCSF

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

GMailboxChooseSaveFile*	
GMGetMailboxCSF()
{
	return itsMailboxCSF;
}

/******************************************************************************
 GMGetAddressBookDir

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

GAddressBookTreeDir*
GMGetAddressBookDir()
{
	assert(itsAddressBookTreeDir != NULL);
	return itsAddressBookTreeDir;
}

/******************************************************************************
 GMGetWMClassInstance

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

const JCharacter*
GMGetWMClassInstance()
{
	return kGMWMInstance;
}

/******************************************************************************
 GMGetTableWindowClass

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

const JCharacter*
GMGetTableWindowClass()
{
	return kTableWindowClass;
}

/******************************************************************************
 GMGetViewWindowClass

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

const JCharacter*
GMGetViewWindowClass()
{
	return kViewWindowClass;
}

/******************************************************************************
 GMGetEditorWindowClass

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

const JCharacter*
GMGetEditorWindowClass()
{
	return kEditorWindowClass;
}

/******************************************************************************
 GMGetAddressBookWindowClass

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

const JCharacter*
GMGetAddressBookWindowClass()
{
	return kAddressBookWindowClass;
}

/******************************************************************************
 GMGetMailboxWindowClass

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

const JCharacter*
GMGetMailboxWindowClass()
{
	return kMailboxWindowClass;
}
