// FileRetriever.m
// By Charles G. Fleming and Judy D. Halchin, 
// Educational Computing Services, Allegheny College.
// Copyright 1992, 1993 Allegheny College
// You may freely copy, distribute and reuse this code. 
// Allegheny College and the authors disclaim any warranty of any kind, 
// expressed or implied, as to its fitness for any particular use.
// This work was partially supported by a grant from the Pew Charitable Trusts
// and by an NSF Teacher Preparation Grant.

#import "SaverAndRetriever.h"

#import <objc/error.h>

#import <appkit/OpenPanel.h>
#import <appkit/Application.h>

#import <stdlib.h>
#import <strings.h>

@implementation FileRetriever

- (const char *)getInspectorClassName
{
	return "FileSaverAndRetrieverInspector";
}

// Initially the filename is the empty string, and the delegate and 
// dataDelegate are nil.
- init
{
	self = [super init];

	filename = (char *)malloc(MAXPATHLEN + 1);
	directory = (char *)malloc(MAXPATHLEN + 1);
	strcpy(filename, "");
	strcpy(directory, NXHomeDirectory());
	return self;
}

- setDelegate:anObject
{
	delegate = anObject;
	return self;
}

- setDataDelegate:anObject
{
	dataDelegate = anObject;
	return self;
}	
	 
- setFileSaver:anObject
{
	fileSaver = anObject;
	return self;
}	
	 
// Returns the file saver's delegate.
- delegate
{
	return delegate;
}

// Returns the file saver's dataDelegate.
- dataDelegate
{
	return dataDelegate;
}
	
// Returns the fileSaver with which this fileRetriever works
- fileSaver
{
	return fileSaver;
}
	
- setFilename:(char *)name
{
	strcpy(filename, name);
	return self;
}

- setDirectory:(char *)path
{
	strcpy(directory, path);
	return self;
}

- (char *)filename
{
	return filename;
}

- (char *)directory
{
	return directory;
}

// Run an open panel.  If the cancel button is not pushed, call open.	
- open:sender
{
	OpenPanel *openPanel = [OpenPanel new];
	char tempFilename[MAXPATHLEN], *lastSlash;
	int count;

	// Try to run an open panel.  If not possible, raise an exception.
	[openPanel allowMultipleFiles:NO];
	if([openPanel runModalForDirectory:directory  file:filename])
	{
		strcpy(tempFilename, [openPanel filename]);
		// Split the filename into directory and file
		lastSlash = strrchr(tempFilename, '/');
		strcpy(filename, lastSlash + 1);
		if(lastSlash == tempFilename)
		{
			count = 1;
			directory[1] = '\0';
		}	
		else 
		{
			count = lastSlash - tempFilename;	
			directory[lastSlash - tempFilename] = '\0';
		}	
		strncpy(directory, tempFilename, count);
		
		[self open];
	}
	else
		if([delegate respondsTo:
				@selector(dataWasRead:success:errorCode:)])
			[delegate dataWasRead:self success:NO
					errorCode:cancelButtonPushed];	
	return self;
}

// Open and read the file specified by the filename and directory instance
// variables.  The dataDelegate must read the data from the stream provided.
// Any errors that occur will be handled by an exception handler.
- (BOOL)open
{
	NXStream *stream = NULL;
	char fullPath[MAXPATHLEN];
	BOOL fileOpened = NO;

	NX_DURING;    // begin exception handling zone
	
	if(strcmp(filename, "") != 0)
	{
		// Raise an exception if we don't have a dataDelegate or if it 
		// doesn't respond to ReadDataFromStream;
		if(dataDelegate == nil)
			NX_RAISE(noDataDelegate, self, delegate);
		if(![dataDelegate respondsTo:@selector(readDataFromStream:)])
			NX_RAISE(doesNotRespondToMethod, self, delegate);

		// If the directory variable is empty, we'll use the user's 
		// home directory.	
		if(strcmp(directory, "") == 0)
			strcpy(directory, NXHomeDirectory());
		
		// Try to open the file.  Raise exception if not possible.
		sprintf(fullPath, "%s/%s", directory, filename);		
		stream = NXMapFile(fullPath, NX_READONLY);
		if (stream == NULL)
			NX_RAISE(noStream, self, delegate);

		// Send the dataDelegate a readDataFromStream: message.
		if (![dataDelegate readDataFromStream:stream])
			NX_RAISE(dataNotRead, self, delegate);

		// If all went well, tell the delegate that the data has been read
		if([delegate respondsTo:@selector(dataWasRead:success:errorCode:)])
			[delegate dataWasRead:self success:YES errorCode:noError];
		if([fileSaver respondsTo:@selector(setFilename:)])
			[fileSaver setFilename:filename];
		if([fileSaver respondsTo:@selector(setDirectory:)])
			[(FileSaver *)fileSaver setDirectory:directory];
		fileOpened = YES;
	}
	else
		if([delegate respondsTo: 
				@selector(dataWasRead:success:errorCode:)])
			[delegate dataWasRead:self success:NO errorCode:noFile];

	// Here's the error handler.
	NX_HANDLER
		switch(NXLocalHandler.code)	
		{
			case noStream: 
				if([(id)NXLocalHandler.data2 
						respondsTo: 	
						@selector(dataWasRead:success:errorCode:)])
					[(id)NXLocalHandler.data2 	
							dataWasRead:(id)NXLocalHandler.data1 
							success:NO
							errorCode:noStream];	
				break;
			case noDataDelegate: 
				if([(id)NXLocalHandler.data2 
						respondsTo: 	
						@selector(dataWasRead:success:errorCode:)])
					[(id)NXLocalHandler.data2 	
							dataWasRead:(id)NXLocalHandler.data1 
							success:NO
							errorCode:noDataDelegate];	
				break;
			case doesNotRespondToMethod: 
				if([(id)NXLocalHandler.data2 
						respondsTo: 	
						@selector(dataWasRead:success:errorCode:)])
					[(id)NXLocalHandler.data2 	
							dataWasRead:(id)NXLocalHandler.data1 
							success:NO
							errorCode:doesNotRespondToMethod];	
				break;
			case dataNotRead: 
				if([(id)NXLocalHandler.data2 
						respondsTo: 	
						@selector(dataWasRead:success:errorCode:)])
					[(id)NXLocalHandler.data2 	
							dataWasRead:(id)NXLocalHandler.data1 
							success:NO
							errorCode:dataNotRead];	
				break;
			default:
				NX_RERAISE();
				break;
		}
	NX_ENDHANDLER;

	// Close the stream.
	if(stream)
		NXCloseMemory(stream, NX_FREEBUFFER);				
	return fileOpened;
}

- write:(NXTypedStream *)typedStream
{
	[super write:typedStream];
	NXWriteTypes(typedStream, "**", &directory, &filename);
	return self;
}
	
- read:(NXTypedStream *)typedStream
{
	char *tempDirectory, *tempFilename;
	
	filename = (char *)malloc(MAXPATHLEN + 1);
	directory = (char *)malloc(MAXPATHLEN + 1);

	[super read:typedStream];
	NXReadTypes(typedStream, "**", &tempDirectory, &tempFilename);
	
	strcpy(filename, tempFilename);
	strcpy(directory, tempDirectory);
	return self;
}	
@end

