/******************************************************************************
 GNBApp.cc

	<Description>

	BASE CLASS = public JXApplication

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

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

#include <GNBApp.h>
#include <GNBMDIServer.h>
#include <GNBPrefsMgr.h>
#include <GNBNoteDir.h>
#include <GNBTreeDir.h>
#include <GNBTrashDir.h>
#include <GNBArrayFlushTask.h>
#include <GNBAboutDialog.h>

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

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

#include <GNBGlobals.h>

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

#include <fstream.h>

#include <jAssert.h>

extern const JCharacter* kNotebookVersionNumberStr;

const JCharacter* kGNBFileName		= ".GNBNotes";
const JCharacter* kGNBFileSignature	= "GDBNotebook database file";
const JIndex kSavedIDListIndex		= 3;
const JFileVersion kIDListVersion	= 1;

const JFileVersion kFileVersionNumber	= 1;

const JCharacter* GNBApp::kDataChanged = "DataChanged::GNBApp";

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

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

GNBApp::GNBApp
	(
	int* 		argc, 
	char* 		argv[],
	JBoolean*	displayAbout,
	JString*	prevVersStr
	)
	:
	JXApplication(argc, argv),
	itsFileArray(NULL),
	itsTreeDir(NULL),
	itsWaitingForFlush(kFalse),
	itsIsStarting(kTrue)
{
	*displayAbout = GNBCreateGlobals(this);

	if (!*displayAbout)
		{
		*prevVersStr = (GNBGetPrefsMgr())->GetNotebookVersionStr();
		if (*prevVersStr == kNotebookVersionNumberStr)
			{
			prevVersStr->Clear();
			}
		else
			{
			*displayAbout = kTrue;
			}
		}
	else
		{
		prevVersStr->Clear();
		}
	
	GNBMDIServer* mdi = new GNBMDIServer(this);
	assert(mdi != NULL);

	itsNoteDirs = new JPtrArray<GNBNoteDir>;
	assert(itsNoteDirs != NULL);

	if (GNBGetPrefsMgr()->GetNotesDatabaseFile(&itsFileName))
		{
		JError err = JFileArray::Create(itsFileName, kGNBFileSignature, &itsFileArray);
		if (!err.OK() && err.Is(JFileArray::kFileAlreadyOpen))
			{
			itsFileArray = NULL;
			JString backupFileName 	= itsFileName + "~";
			JString restoreFileName	= itsFileName + ".Recoved";
			JBoolean backupFound = JFileExists(backupFileName);
			if (backupFound)
				{
				JString message = "Your notebook is either open already, "
								  "or it was not closed properly. "
								  "Would you like me to try to open "
								  "the backup notebook?";
				if (JGetUserNotification()->AskUserYes(message))
					{
					JString output;
					JString cmd = "mv \"" + itsFileName + "\" \"" + restoreFileName + "\"";
					JError cmdErr = JRunProgram(cmd, &output);
					if (cmdErr.OK())
						{
						JString report = "Your old notebook has been saved as \"" + restoreFileName + "\"";
						JGetUserNotification()->DisplayMessage(report);
						}
					else
						{
						JGetUserNotification()->ReportError(output);
						}
					cmd = "cp \"" + backupFileName + "\" \"" + itsFileName + "\"";
					cmdErr = JRunProgram(cmd, &output);
					if (cmdErr.OK())
						{
						err = JFileArray::Create(itsFileName, kGNBFileSignature, &itsFileArray);
						}
					else
						{
						JGetUserNotification()->ReportError(output);
						}
					}
				else
					{
					message = "Would you like me to create a new notebook?";
					if (JGetUserNotification()->AskUserYes(message))
						{
						JError remErr = JRemoveFile(itsFileName);
						if (remErr.OK())
							{
							err = JFileArray::Create(itsFileName, kGNBFileSignature, &itsFileArray);
							}
						else
							{
							JGetUserNotification()->ReportError(remErr.GetMessage());
							}
						}
					}
				}
			else
				{
				JString message = "Your notebook is either open already, "
								  "or it was not closed properly. "
								  "Would you like me to try to create "
								  "a new notebook?";
				if (JGetUserNotification()->AskUserYes(message))
					{
					err = JFileArray::Create(itsFileName, kGNBFileSignature, &itsFileArray);		
					if (!err.OK())
						{
						JGetUserNotification()->ReportError("Sorry, I was unable to create a new notebook.");
						}
					}
				}
			}
		if (err.OK())
			{
			if (itsFileArray->GetElementCount() == 0)
				{
				itsFileArray->AppendElement("");
				itsFileArray->AppendElement("");
				itsFileArray->AppendElement("");
				itsFileArray->AppendElement("");
				itsFileArray->SetVersion(kFileVersionNumber);
				}
			else
				{
				// check for new version
				}
			GNBTrashDir* dir = new GNBTrashDir(this);
			assert(dir != NULL);
			GNBSetTrashDir(dir);
			JXGetDocumentManager()->DocumentMustStayOpen(dir, kTrue);

			mdi->HandleCmdLineOptions(*argc, argv);
			
			}
		else if (err.Is(JFileArray::kWrongSignature))
			{
			itsFileArray = NULL;
			JGetUserNotification()->ReportError("The file specified is not a notebook.");
			}
		}
	else
		{
		JGetUserNotification()->ReportError("Unable to locate notebook!");
		}
}

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

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

GNBApp::~GNBApp()
{
	GNBDeleteGlobals();
	delete itsNoteDirs;
	Flush(kFalse);
}

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

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

void
GNBApp::DirectorClosed
	(
	JXDirector* theDirector
	)
{
	GNBNoteDir* dir = dynamic_cast(GNBNoteDir*, theDirector);
	JIndex findex;
	if (itsNoteDirs->Find(dir, &findex))
		{
		itsNoteDirs->RemoveElement(findex);
		}
		
	JXApplication::DirectorClosed(theDirector);
}

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

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

void
GNBApp::CleanUpBeforeSuddenDeath
	(
	const JXDocumentManager::SafetySaveReason reason
	)
{
	JXGetDocumentManager()->SafetySave(reason);
	Flush(kFalse);
}

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

	prevVersStr can be NULL.

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

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

/******************************************************************************
 ShowNoteTree (public)

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

void
GNBApp::ShowNoteTree()
{
	if (itsTreeDir == NULL)
		{
		itsTreeDir = new GNBTreeDir(this);
		assert(itsTreeDir != NULL);
		}
	itsTreeDir->Activate();
	if (itsTreeDir->GetWindow()->IsIconified())
		{
		itsTreeDir->GetWindow()->Deiconify();
		}
	if (itsIsStarting)
		{
		OpenWindows();
		itsIsStarting = kFalse;
		}
}

/******************************************************************************
 OpenNote (public)

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

void
GNBApp::OpenNote
	(
	const JFAID_t id
	)
{
	const JSize count = itsNoteDirs->GetElementCount();
	for (JSize i = 1; i <= count; i++)
		{
		if (itsNoteDirs->NthElement(i)->GetID() == id)
			{
			itsNoteDirs->NthElement(i)->Activate();
			return;
			}
		}
		
	GNBNoteDir* dir = new GNBNoteDir(this, id);
	assert(dir != NULL);
	itsNoteDirs->Append(dir);
	dir->Activate();
}

/******************************************************************************
 CloseNote (public)

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

void
GNBApp::CloseNote
	(
	const JFAID_t id
	)
{
	const JSize count = itsNoteDirs->GetElementCount();
	for (JSize i = 1; i <= count; i++)
		{
		if (itsNoteDirs->NthElement(i)->GetID() == id)
			{
			itsNoteDirs->NthElement(i)->Close();
			return;
			}
		}
}

/******************************************************************************
 NewNote (public)

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

JFAID_t
GNBApp::NewNote()
{
	itsFileArray->AppendElement("");
	JSize count = itsFileArray->GetElementCount();
	JFAID id; 
	JBoolean ok = itsFileArray->IndexToID(count, &id);
	assert(ok);
	return id.GetID();
}

/******************************************************************************
 DeleteFileElement (public)

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

void
GNBApp::DeleteFileElement
	(
	const JFAID_t id
	)
{
	const JSize count = itsNoteDirs->GetElementCount();
	for (JSize i = 1; i <= count; i++)
		{
		if (itsNoteDirs->NthElement(i)->GetID() == id)
			{
			itsNoteDirs->NthElement(i)->Close();
			return;
			}
		}
	itsFileArray->RemoveElement(JFAID(id));
}

/******************************************************************************
 Flush (public)

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

void
GNBApp::Flush
	(
	const JBoolean reopen
	)
{
	if (itsFileArray == NULL)
		{
		return;
		}
	delete itsFileArray;
	itsFileArray = NULL;
	JString bname = itsFileName + "~";
	JString cmd = "cp " + itsFileName + " " + bname;
	JExecute(cmd, NULL);
	if (reopen)
		{
		JError err = JFileArray::Create(itsFileName, kGNBFileSignature, &itsFileArray);
		assert(err.OK());
		}
	itsWaitingForFlush = kFalse;
}

/******************************************************************************
 NeedsSave (public)

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

void
GNBApp::NeedsSave()
{
	if (!itsWaitingForFlush)
		{
		GNBArrayFlushTask* task = new GNBArrayFlushTask(this);
		assert(task != NULL);
		InstallIdleTask(task);
		itsWaitingForFlush = kTrue;
		}
}

/******************************************************************************
 SaveOpenWindows (private)

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

void
GNBApp::SaveOpenWindows()
{
	ostrstream os;
	os << kIDListVersion << ' ';
	const JSize count = itsNoteDirs->GetElementCount();
	os << count << ' ';
	for (JSize i = 1; i <= count; i++)
		{
		JFAID_t id = itsNoteDirs->NthElement(i)->GetID();
		os << id << ' ';
		}

	os << GNBGetTrashDir()->IsActive() << ' ';
		
	assert(itsFileArray->IndexValid(JFAIndex(kSavedIDListIndex)));
	itsFileArray->SetElement(JFAIndex(kSavedIDListIndex), os);
}

/******************************************************************************
 OpenWindows (private)

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

void
GNBApp::OpenWindows()
{
	JStaticBuffer data;
	itsFileArray->GetElement(JFAIndex(kSavedIDListIndex), &data);
	if (!data.GetLength() == 0)
		{
		jistrstream(is, data.GetData(), strlen(data));
		JFileVersion version;
		is >> version;
		JSize count;
		is >> count;
		for (JSize i = 1; i <= count; i++)
			{
			JFAID_t id;
			is >> id;
			OpenNote(id);
			}
		JBoolean active;
		is >> active;
		if (active)
			{
			GNBGetTrashDir()->Activate();
			}
		}
}

/******************************************************************************
 Quit (public)

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

void
GNBApp::Quit()
{
	SaveOpenWindows();
	JXApplication::Quit();
}

/******************************************************************************
 NoteNameChanged (public)

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

void
GNBApp::NoteNameChanged
	(
	const JFAID_t  id, 
	const JString& title
	)
{
	Broadcast(DataChanged(id, title));
}
