
#import "STAppController.h"
#import "STDocumentClass.h"
#import "STMenuCategory.h"
#import "STSwapBoxManager.h"
#import "STDefines.h"
#import "STUtil.h"
#import <foundation/NSAutoreleasePool.h>
#import <foundation/NSException.h>

void MyTopLevelErrorHandler(NXHandler *errorState)
{
char buf[BUFSIZE]="";
	if (errorState->code >= NSExceptionBase &&
	     errorState->code <= NSLastException) {
	     NSException *exception = (id) errorState->data1;

	     NSLog(@"%@: %@\n", [exception exceptionName], 
		 	[exception exceptionReason]);
		 sprintf(buf,"%s: %s\n", [[exception exceptionName] cString], 
		  	[[exception exceptionReason] cString]);
		 NXRunAlertPanel("Exception","%s",NULL,NULL,NULL,buf);

	 }
}

@implementation STAppController

/* ==== init section ==== */

- init
{	[super init];
	controllerZone=NXCreateZone(vm_page_size, vm_page_size, YES);
	return self;
}

- appWillInit:sender
{
	NXSetTopLevelErrorHandler(MyTopLevelErrorHandler);
	return self;
}

- appDidInit:sender
{
id w;

	docArray=[[NSMutableArray allocWithZone:(NSZone *)controllerZone] init];
	docArray=[[NSMutableArray alloc] init];
	
	[self setupMenus:self];
	
	w=[accessoryView window];
	[accessoryView removeFromSuperview];
	[w free];

    if(BoolValueForDefault("NewEmptyDocument"))
		[self newDocument:nil];
	
	return self;
}

/* ==== document management section ==== */

- openFile:sender
{
char *filetypes[50];  
NSString *str;
NSEnumerator *enm=[[self docExtensions] objectEnumerator];
NSString *name=NULL;
int i;

	bzero(filetypes, sizeof filetypes);
	for(i=0;((str=[enm nextObject])!=nil) && i<50;i++)
	 { 	filetypes[i]=(char *)[str cString];
	 }


	if(sender)
	 {	if ([[OpenPanel new] runModalForTypes: filetypes]==NO) 
		 return (self);
		else 
		 { 	const char *p=[[OpenPanel new] filename];
		 	if(p)	name=[NSString stringWithCString:p];
		 	else
			 { 	fprintf(stderr,"Failed to open file");
				return self;
			 }
		 }
	 }

	if(name) [self openDocument:name];
	
	return self;
}

- documentClass
{	return [STDocumentClass class];
}

- (NSArray *)docExtensions
{	return [NSArray arrayWithObject:@"starter"];
}

- createDocumentObject:sender
{	
NSZone *zone;
id doc;
static int documentTag=0;

	zone=NSCreateZone(vm_page_size*1024, vm_page_size, YES);
	
	/* allocate a new document object */
	doc=[[self documentClass] newDocument];
	
	if(!doc)
	 {	internalError("Could not allocate document object");
	 	return nil;
	 }

	/* this section opens the document file */
	[doc setTag:documentTag++];
	[NXApp perform:@selector(updateWindows) with:nil
	   afterDelay:1 cancelPrevious:YES];

	/* this will retain the object */
	[docArray addObject:doc];
	return doc;
}

- openDocument:(NSString *)aPath
{
id doc;
	
	if(!aPath) return nil;

	if((doc=[self findDocWithPath:aPath])!=nil)
	 {	[[doc window] makeKeyAndOrderFront:nil];
		return nil;
	 }
	
	doc=[self createDocumentObject:self];

	if(![doc openDocumentFile:aPath])
	 {	[[NXApp delegate] perform:@selector(freeDoc:) with:doc
	     afterDelay:1 cancelPrevious:YES];
		return nil;
	 }
	[[doc window] makeKeyAndOrderFront:nil];
	return doc;
}

- newDocument:sender
{	
NSString *str;
id new;
static int untitledCount=0;

	/* if UNTITLED already exist, the new name is UNTITLEDn */
	
	do { str=untitledCount
		 	? [NSString stringWithFormat:@"%s/UNTITLED%d",
		  					NXHomeDirectory(), untitledCount]
		 	: [NSString stringWithFormat:@"%s/UNTITLED",NXHomeDirectory()];
			
		 untitledCount++;
	   } while([self findDocWithPath:str]);

	new=[self createDocumentObject:self];
	[new setDocumentPath:str];
	[new setDocumentName:[str lastPathComponent]];
	[[new window] makeKeyAndOrderFront:nil];
	[NXApp perform:@selector(updateWindows) with:nil
	 afterDelay:1 cancelPrevious:YES];

	return new;
}
	
- save:sender;
{	
NSString *name=[currentDoc documentName];
NSRange r;
	if(!name) return self;

	r=[name rangeOfString:@"UNTITLED"];
	if(r.location==0)
	  		[self saveAs:sender];
	else    [currentDoc saveDocument];
	return self;
}

- saveAs:sender
{
NSString *str;
id savePanel;

	if(!currentDoc) return self;
    savePanel=[[[SavePanel new] setDelegate:self] setRequiredFileType:NULL];
	[savePanel setAccessoryView:nil];
	if ([savePanel runModal]) 
	   str=[NSString stringWithCString:[savePanel filename]];
	else return self;   

    if(str) 
	 { 	str=insertExt(str, [[[self docExtensions] objectAtIndex:0] cString]);
		[currentDoc saveDocumentAs:str];
	 }
    
    return self;
}

- saveTo:sender;
{
	[self saveAs:sender];
    return self;
}

- saveAll:sender
{
int i;
id obj;
NSEnumerator *enm=[docArray objectEnumerator];

	for(i=0;(obj=[enm nextObject])!=nil;i++)
	 {	[obj saveDocument];
	 }
	return nil;
}

- revert:sender
{
id doc=currentDoc;
NSString *path=[[doc documentPath] copy];
	if(!doc) return self;

	if([[doc documentName] isEqual:@"UNTITLED"])
	 { 	NXRunAlertPanel("Revert",
	     "Document does not have a name, cannot revert...",NULL,NULL,NULL);
	   	return self;
	 }

	[doc retain];
	[docArray removeObject:doc];
	
	/* clear the inspector */
	currentDoc=nil;
	[NXApp updateWindows];
	
	/* this will set the new doc to be the currentdoc */
	currentDoc=[self openDocument:path];
	[doc release];
	return self;
}

- close:sender
{	if(!currentDoc) return self;
	return [[currentDoc window] performClose:nil];
}

- freeDoc:aDoc
{
	if(!aDoc) return nil;
	
	if(currentDoc==aDoc)  currentDoc=nil;
	
	/* this will release the object */
	[docArray removeObject:aDoc];

	[NXApp perform:@selector(updateWindows) with:nil
	   afterDelay:100 cancelPrevious:YES];

	return self;
}

- findDocWithPath:(NSString *)aPath
{
int i;
id obj;
NSEnumerator *enm=[docArray objectEnumerator];

	for(i=0;(obj=[enm nextObject])!=nil;i++)
	 {	if([aPath isEqual:[obj documentPath]]) return obj;
	 }
	return nil;
}

- findDocWithTag:(int)aTag
{
int i;
id obj;
NSEnumerator *enm=[docArray objectEnumerator];

	for(i=0;(obj=[enm nextObject])!=nil;i++)
	 {	if(aTag==[obj tag]) return obj;
	 }
	return nil;
}

/* ==== panels ==== */

- getPrefsPanel:sender
{
	if(!prefManager)
	  [NXApp  loadNibSection:"PrefPanel.nib" owner:self
	   withNames:NO fromZone:(NXZone *)controllerZone];
	[prefManager getPanelWithScreen:0];
	return self;
}

- getInspectorPanel:sender
{
	/* called from the menu */
	[self getInspectorPanelWithScreen:0];
	return self;
}

- (BOOL)getInspectorPanelWithScreen:(int)aScreen
{
	/* called from the keyboard */
	if(aScreen<0 || aScreen>1) return NO;
	if(!inspectorManager)
	  [NXApp  loadNibSection:"InspectorPanel.nib" owner:self
	   withNames:NO fromZone:(NXZone *)controllerZone];
	[inspectorManager getPanelWithScreen:aScreen];
	return YES;
}

- getInfoPanel:sender
{
	if(!infoPanel)
	  [NXApp  loadNibSection:"InfoPanel.nib" owner:self
	   withNames:NO fromZone:(NXZone *)controllerZone];
	[infoPanel makeKeyAndOrderFront:nil];
	return self;
}

/* ==== access to instance variables ==== */

- setCurrentDoc:aDoc
{	currentDoc=aDoc;
	return self;
}

- currentDoc
{	return currentDoc;
}

	
/* ==== application delegate methods ==== */

- (BOOL)appAcceptsAnotherFile:sender
{
    return YES;
}

- (int)appOpenFile:(const char *)path type:(const char *)type
{
int i;
NSString *str, *typeString=[NSString stringWithCString:type];
NSEnumerator *enm=[[self docExtensions] objectEnumerator];
BOOL found=NO;

	for(i=0;(str=[enm nextObject])!=nil;i++)
	 { if([str isEqual:typeString]) found=YES;
	 }

	if(!found) return NO;

    [self perform:@selector(openDocument:) 
		with:[[NSString stringWithCString:path] retain] afterDelay:1
	    cancelPrevious: NO];

	return YES;
}

- appWillTerminate:sender
{
BOOL needsWarning=NO;
id doc, obj;
int i;
NSEnumerator *enm=[docArray objectEnumerator];


TRY_AGAIN:
	doc=currentDoc;
	for(i=0;(obj=[enm nextObject])!=nil;i++)
	 { if([[obj window] isDocEdited]) needsWarning=YES;
	 }
	if(needsWarning)
	 { switch(NXRunAlertPanel("Quit",
	      "There are edited documents",
	      "Review Unsaved","Quit Anyway","Cancel"))
		   { case NX_ALERTDEFAULT: // Review Unsaved
	          
	         for(i=0;(obj=[enm nextObject])!=nil;i++)
	           { if([[obj window] isDocEdited])
			      { switch(NXRunAlertPanel("Review Unsaved",
	                "Save changes to %s","Save","Don't save",
					"Cancel",[obj documentPath]))
				     { case NX_ALERTDEFAULT: //Save
					    currentDoc=obj;
						[[obj window] orderFront:nil];
						[self save:self];
						currentDoc=doc;
						break;
					   case NX_ALERTALTERNATE: //Don't save
					        break;
					   case NX_ALERTOTHER: //Cancel
					        goto TRY_AGAIN;
					 }
				  }
	           }
		     case NX_ALERTALTERNATE: //Quit Anyway
			                         break;
			 case NX_ALERTOTHER: //Cancel
			                      return nil;
		   }
	 }

	return self;
}

@end

