#import <appkit/appkit.h>
#import "Document.h"
#import "TextController.h"

#define SAVE NX_ALERTDEFAULT
#define CLOSE NX_ALERTALTERNATE
#define CANCEL NX_ALERTOTHER

#define SAVE_ERROR "Can't save file"
#define OPEN_ERROR "Can't open file"

@implementation Document

// initialize a new document
- init
{
	[super init];
	[NXApp loadNibSection:"Document.nib"
		owner:self withNames:NO];
	// make the document the delegate of the window
	// allows us to determine the current document
	// in showSavePanel:
	[window setDelegate:self];
	// make the document the delegate of the text
	// every time a key is pressed, the delegate
	// of the text object will receive a
	// textDidGetKeys: isEmpty: message
	// this allows us to update the window from
	// a clean state to a dirty state
	[theText setDelegate:self];
	return self;
}

// load a document from a file
- initDocumentFromFile:
	(const char *)fullPathName
{
	NXStream *stream;
    
	// obtain a stream for an existing file
	if (stream =
		NXMapFile(fullPathName, NX_READONLY))
		{
		[self init];
		// read text from stream
		[theText readText:stream];
		//update the window's title
		[window setTitle:fullPathName];
		// display the document
		[self showDocument];
		// free memory associated with stream
		NXCloseMemory(stream, NX_FREEBUFFER);
		return self;
		}
	else
		{
		[self showError:OPEN_ERROR];
		return nil;
		}
}

// display the document -- one improvement
// is to stagger the window rather than display
// each one on top of each other
- showDocument
{
	// set the text selection to 1st char
	[theText setSel:0 :0];
	[window makeKeyAndOrderFront:self];
	return self;
}

// write the document to file
- saveDocumentToFile:(const char *)fullPathName
{
	NXStream *stream = NULL;
	const char* title = [window title];

	// get a stream first
	if (stream =
		(NXOpenMemory(NULL, 0, NX_WRITEONLY)))
		{
		// write text into stream
		[theText writeText:stream];
		// write the stream to the file
		// and check for error code
		if (NXSaveToFile(stream, fullPathName) != 0)
			{
			[self showError:SAVE_ERROR];
			// free stream
			NXCloseMemory(stream, NX_FREEBUFFER);
			return nil;
			}
		else
			// save succeeded: update misc info
			{
			// update the title of the window
			[window setTitle:fullPathName];
			// update window to clean state
			[window setDocEdited:NO];
			// free stream
			NXCloseMemory(stream, NX_FREEBUFFER);
			}
		}
	else
		// Couldn't get stream
		{
		[self showError:SAVE_ERROR];
		return nil;
		}
	return self;
}

// use an Alert panel to display error messages
- showError:(const char *)errorMessage
{
	NXRunAlertPanel(NULL,
		errorMessage, "OK", NULL, NULL);
	return self;
}

// called every time a key is pressed
// if the window is marked dirty, we don't do
// anything -- if it's not dirty, we set
// it to dirty
- textDidGetKeys:sender isEmpty:(BOOL)flag
{
	// if window is not dirty, then set
	// it to dirty
	if ([window isDocEdited] == NO)
		[window setDocEdited:YES];
	return self;
}

- free
{
	// free the window since it's
	// dynamically allocated
	[window free];
	// free everything allocated
	// by the superclasses
	return [super free];
}

// since the document object is the delegate of
// the window, the window will, before closing,
// send a windowWillClose: message to
// the document
- windowWillClose:sender
{
	int result;
	
	// display alert panel only if window
	// is dirty
	if ([window isDocEdited])
		{
		result = NXRunAlertPanel
			([NXApp appName],
			"Unsaved changes. Close Anyway?\n",
			"Save",
			"Close anyway",
			"Cancel");

		switch(result)
			{
			case SAVE:
				// display savepanel;
				// showSavePanel: returns
				// nil if user presses Cancel
				// this prevents window from
				// closing
				return [textController
					showSavePanel:sender];
				break;
			case CLOSE:
				// delay freeing the object until
				// we are done with the current
				// event (closing the window)
				return [NXApp delayedFree:self];
			case CANCEL:
				// returning nil prevents the window
				// from closing
				return nil;
			}
		}
	return self;
}

- window
{
	return window;
}

@end
