/* CEAppManager.m				 
 *
 * This is the basic controller of the App. It manages all big abstraction
 * tasks for the panels, documents and connections to the outside.
 *
 * For interface-info see the header file. The comments in this file mostly
 * cover only the real implementation details.
 *
 * Written by: 		Thomas Engel
 * Created:    		23.10.1993 (Copyleft)
 * Last modified: 	07.05.1994
 */

#import "CEAppManager.h"
#import "CEClassEditor.h"
#import "CEListener.h"
#import "Text_MiscExtensions.h"
#import "MiscCompletionText.h"

#import <misckit/MiscString.h>


@implementation CEAppManager

- appWillInit:sender
{
	// This is the init part we need to pass before we get the messages that
	// we have to open some files.
	// To be able to init a class we need the have the Preferences,

	// preferences = [CEPreferencesManager new];
	
	// Now lets load the docu templates
	
	NXStream 	* aStream;
	id			aPath;
	id			ourListener;

	// Setup out own Listener...this is really nasty..anyway.
	
	[[NXApp appListener] free];

	ourListener = [[CEListener alloc] init];
	[ourListener setDelegate:self];
	[ourListener checkInAs:[NXApp appName]];
	[ourListener addPort];

	[NXApp setAppListener:ourListener];	
	
	// Well now this is a litle rough....maybe we should search for those files
	// inside the ~/AppInfo or even *Library/ClassEditor sections..anyway.	
	
	aPath = [MiscString newWithString:
				[(NXBundle *)[NXBundle mainBundle] directory]];
	[aPath cat:"/CEClassDocuTemplate.rtf"];
	if( [aPath doesExistInFileSystem] )
	{
		aStream = NXMapFile( [aPath stringValue], NX_READONLY );
		[classDocTemplateView readRichText:aStream];
		NXCloseMemory( aStream, NX_FREEBUFFER );
	}
	[aPath free];

	aPath = [MiscString newWithString:
				[(NXBundle *)[NXBundle mainBundle] directory]];
	[aPath cat:"/CEMethodDocuTemplate.rtf"];
	if( [aPath doesExistInFileSystem] )
	{
		aStream = NXMapFile( [aPath stringValue], NX_READONLY );
		[methodDocuTemplateView readRichText:aStream];
		NXCloseMemory( aStream, NX_FREEBUFFER );
	}
	[aPath free];
	
	// Here comes teh hack...

	[MiscCompletionText poseAs:[Text class]];

	return self;
}

- appDidInit:sender
{
	// This is some kind of late init. These object are need for working but
	// not for reading the classes or having a class initialized.

	
	return self;
}

- (int)app:sender openFile:(const char *)path type:(const char *)type
{
	// This method is performed whenever a user opens a document from the
	// Workspace Manager.
	// We use our default file open method with some default params.
	
	return [self openFile:path onHost:"*" atTrueLine:0];
}

- (BOOL)appAcceptsAnotherFile:sender
{
	// Inform the workspace that we can open multiple files.
	return YES;
}

- appDidBecomeActive:sender
{
	return self;
}

- appWillTerminate:sender
{
	// Now lets see what we have...ask every window what is going on.

	BOOL	ready;
	int 	i;
	id	windowList;
	int	count;
	int	result;

	windowList = [NXApp windowList];
	count = 0;

	for( i=0; i<[windowList count]; i++ )
		if( [[windowList objectAt:i] isDocEdited] ) count++;

	// Well if there are windows with unsaved contents we should tell the
	// user about it.
	// We won't leave until the user has decided what to do on the top level.

	if( count == 0 ) return self;

	ready = NO;
	while( !ready )
	{
		result = NXRunAlertPanel( "Quit", "There are edited windows.", 
								"Review Unsaved", "Quit Anyway", "Cancel" );

		if( result == NX_ALERTOTHER )
			return nil;
	
		else if( result == NX_ALERTDEFAULT )
			ready = [self _closeAllWindows];

		else ready = YES;
	}
	return self;
}

- (BOOL)_closeAllWindows
{
	// We will try to close all windows and if we succeed we will let the app
	// know. YES = all closed
	// NOTE: Well this is kind of ugly because the app should maintain a list
	// 		of all the controllers that are responsible for some data. But I
	//		don't want to hack down another list access stuff. So we do it
	// 		this way. Maybe in the future I will change that.

	id	delegateList;
	id	aDelegate;
	int	i;
	id	windowList;
	BOOL	success;

	windowList = [NXApp windowList];
	delegateList = [List new];

	// Lets find all the delegates first.

	for( i=0; i<[windowList count]; i++ )
	{
		aDelegate = [[windowList objectAt:i] delegate];
		if( aDelegate != nil &&
			[aDelegate isKindOf:[CEClassEditor class]] )
			[delegateList addObjectIfAbsent:aDelegate];
	}
	success = YES;

	for( i=0; i<[delegateList count]; i++ )
	{
		if( [[delegateList objectAt:i] close:self] == nil )
		{
			success = NO;
			break;
		}
	}

	return success;
}

- addToReleasePool:anObject
{
	return [NXApp delayedFree:anObject];
}

- (int)openFile:(char *)path onHost:(char *)host atTrueLine:(int *)line;
{
	// This method is performed whenever a user opens a document.
	// It takes care that every file is only opened once.
	
	id	newEditor;
	id	aString;
	id	basename;
	id	filetype;
	id	thePath;
	id	windowList;
	id	aDelegate;
	int	i;
	BOOL	loadIt;

	// First we have to check if we already know this file !
	// Some window delegate should have the same filename (without .h or .m)
	// Using the window list might not be the safest way..but I guess it
	// will work.
	
	aString = [MiscString newWithString:path];
	filetype = [aString fileExtension];
	
	if( [filetype cmp:"m"] != 0 &&
		[filetype cmp:"h"] != 0 &&
		[filetype cmp:"rtf"] != 0 )
	{
		// Looks like this is no valid file !
		
		[aString free];
		[filetype free];
		return -1;
	}
	// Now that it is clear that this is a valid file...
	
	[NXApp activateSelf:NO];

	windowList = [NXApp windowList];
	basename = [aString fileBasename];
	thePath = [aString pathName];
	[thePath addChar:[MiscString pathSeparator]];
	[thePath concatenate:basename];
	[basename free];
	[aString free];
	loadIt = YES;

	for( i=0; i<[windowList count]; i++ )
	{
		aDelegate = [[windowList objectAt:i] delegate];

		if( [aDelegate respondsTo:@selector(filename)] &&
			aDelegate != nil )
		{
			if( [thePath cmp:[aDelegate filename]] == 0 )
			{
				// Looks like we have found a delegate that belongs to exactly
				// the same path.
				// We must to ask this delegate now what window we should top.
				
				[[aDelegate window] makeKeyAndOrderFront:self];
				loadIt = NO;
			}
		}
	}
	[thePath free];

  	// If we really have a new editor...let the world know about it.
  	
	if( loadIt )
	{
		newEditor = [[CEClassEditor alloc] initFromFile:path];
 		if( newEditor ) return YES;
	}
	return 0;
}

- showInfo:sender
{
//	if( !info ) info = [BBInfo new];
		
//	[info makeKeyAndOrderFront:self];
	return self;
}

- showPreferences:sender
{
//	if( preferences ) [preferences makeKeyAndOrderFront:self];
	return self;
}

- preferences
{
	return self;
}

- open:sender
{
	id 		openPanel;
	char 	fullName[MAXPATHLEN];
	const char *const *files;
	const char *const fileType[6] = { "m",
									  "h",
									   NULL};

 	openPanel = [[OpenPanel new] allowMultipleFiles:YES];

 	// Run the open panel, filtering for our type of documents
 	// We try to open every file the panel returns.
  
  	if( [openPanel runModalForTypes:fileType] )
  	{
  		files = [openPanel filenames];
		for( files = [openPanel filenames]; files && *files; files++)
		{
			// Now lets merge the fullFilename from the directory and the
			// current filename and try to init a editor from this file.
			// We use the default open method with some default params.
      		
			sprintf( fullName, "%s/%s", 
					[(OpenPanel *)openPanel directory], *files );
      		[self openFile:fullName onHost:"*" atTrueLine:0];
    		}
	}
  	return self;
}

- new:sender
{
	[CEClassEditor new];
	return self;
}

- save:sender
{
	// [[NXApp keyWindow] save:sender];
	//
	// This is a posible solution
	// But we currently work with a firstResponder solution
	
	return self;
}

- saveAs:sender
{
	return self;
}

- saveTo:sender
{
	return self;
}

- saveAll:sender
{
	return self;
}

- revert:sender
{
	return self;
}

- close:sender
{
	return self;
}

- print:sender
{
	return self;
}

- setTextFontStyle:sender
{
	[self _setSelectionFont:textFontField andColor:textFontColorWell];
	return self;
}

- setMethodNameFontStyle:sender
{
	[self _setSelectionFont:methodNameField andColor:methodNameColorWell];
	return self;
}

- setParameterFontStyle:sender
{
	id firstResponder = [[NXApp mainWindow] firstResponder];
	
	if( [firstResponder respondsTo:@selector(setSelFont:)] )
		[firstResponder setSelFont:[Font newFont:"Times-Italic" size:14]];
	return self;
}

- setMathSymbolFontStyle:sender
{
	id firstResponder = [[NXApp mainWindow] firstResponder];
	
	if( [firstResponder respondsTo:@selector(setSelFont:)] )
		[firstResponder setSelFont:[Font newFont:"Symbol" size:14]];
	return self;
}

- setHeadlinesFontStyle:sender
{
	[self _setSelectionFont:headlinesFontField
				   andColor:headlinesFontColorWell];
	return self;
}

- setExampleCodeFontStyle:sender
{
	[self _setSelectionFont:exampleCodeFontField
				   andColor:exampleCodeFontColorWell];
	return self;
}

- setBlankLinesFontStyle:sender
{
	[self _setSelectionFont:blankLinesFontField
				   andColor:blankLinesFontColorWell];
	return self;
}

- setSourceFontStyle:sender
{
	[self _setSelectionFont:sourceFontField andColor:sourceFontColorWell];
	return self;
}

- setKeywordsFontStyle:sender
{
	[self _setSelectionFont:keywordsFontField andColor:keywordsFontColorWell];
	return self;
}

- setTypecastFontStyle:sender
{
	[self _setSelectionFont:typecastFontField andColor:typecastFontColorWell];
	return self;
}

- setRemarkFontStyle:sender
{
	[self _setSelectionFont:remarkFontField andColor:remarkFontColorWell];
	return self;
}

- setBugNoteFontStyle:sender
{
	[self _setSelectionFont:bugNoteFontField andColor:bugNoteFontColorWell];
	return self;
}

- _setSelectionFont:aFont andColor:aColor
{
	// Well this is a lie...we don't get the font but the TextField that has
	// the right font set !!
	
	id firstResponder = [[NXApp mainWindow] firstResponder];
	
	if( [firstResponder respondsTo:@selector(setSelFont:)] &&
		aFont != nil )
		[firstResponder setSelFont:[aFont font]];
	if( [firstResponder respondsTo:@selector(setSelColor:)] &&
		aColor != nil )
		[firstResponder setSelColor:[aColor color]];

	// This is for letting the delegates know about the changes to
	// be able to track document modification.
	// The first responder must know about the window because the
	// textDidChange might require it.
	
	if( [firstResponder respondsTo:@selector(window)] )
		[[[NXApp mainWindow] delegate] textDidChange:firstResponder];

	return self;
}

- copyClassDocuTemplate
{
	// All we do here is to copy the docu template to the pasteboard.
	
	[classDocTemplateView selectAll:self];
	[classDocTemplateView copyTo:[Pasteboard newName:"CETempPb"]];
	return self;
}

- copyMethodDocuTemplate
{
	// All we do here is to copy the method template to the pasteboard.
	
	[methodDocuTemplateView selectAll:self];
	[methodDocuTemplateView copyTo:[Pasteboard newName:"CETempPb"]];
	return self;
}

- windowDidBecomeKey:sender
{
	// now if our Docu window will become key...we should load the text into
	// it if we havn't yet.

	NXStream 	* aStream;
	id			aPath;
	
	if( sender != docuTemplateWindow ) return self;
	
	if( [docuTemplateTextView textLength] < 10 )
	{
		aPath = [MiscString newWithString:
						[(NXBundle *)[NXBundle mainBundle] directory]];
		[aPath cat:"/DocuTemplate.rtf"];
		if( [aPath doesExistInFileSystem] )
		{
			aStream = NXMapFile( [aPath stringValue], NX_READONLY );
			[docuTemplateTextView readRichText:aStream];
			NXCloseMemory( aStream, NX_FREEBUFFER );
		}
		[aPath free];
	}
	return self;
}

@end

/*
 * History: 13.01.95 Bla.
 *
 *
 * Bugs: No bugs and birds seen....
 */