#import "ApplicationDelegate.h"
#import "OpenPanelDelegate.h"
#import "RKDocumentController.h"
#import "InfoWindowController.h"
#import "PasteboardWindowController.h"
#import "PrefsWindowController.h"
#import "CreateResourceSheetController.h"
#import "ResourceDocument.h"
#import "ResourceDataSource.h"
#import "RKEditorRegistry.h"

#import "ResKnifePluginProtocol.h"
#import "RKSupportResourceRegistry.h"


@implementation ApplicationDelegate

- (id)init
{
	self = [super init];
	[NSApp registerServicesMenuSendTypes:[NSArray arrayWithObject:NSStringPboardType] returnTypes:[NSArray arrayWithObject:NSStringPboardType]];
	return self;
}

- (void)applicationWillFinishLaunching:(NSNotification *)notification
{
	// instanciate my own subclass of NSDocumentController so I can override the open dialog
	[[RKDocumentController alloc] init];
	[RKSupportResourceRegistry scanForSupportResources];
}

/*!
@method		awakeFromNib
@change		2003-10-24 NGS: moved icon caching into method called by timer (to speed up app launch time)
*/

- (void)awakeFromNib
{
	// Part of my EvilPlanŠ to find out how many people use ResKnife and how often!
	int launchCount = [[NSUserDefaults standardUserDefaults] integerForKey:@"LaunchCount"];
	[[NSUserDefaults standardUserDefaults] setInteger:launchCount + 1 forKey:@"LaunchCount"];
	
	// initalise an empty icon cache and create timer used to pre-cache a number of common icons
	_icons = [[NSMutableDictionary alloc] init];
	[NSTimer scheduledTimerWithTimeInterval:5.0 target:self selector:@selector(precacheIcons:) userInfo:nil repeats:NO];
	
	// set default preferences
    [self initUserDefaults];
}

- (void)dealloc
{
	[_icons release];
	[super dealloc];
}

/*!
@method			precacheIcons:
@author			Nicholas Shanks
@created		2003-10-24
@abstract		Pre-caches the icons for a number of common resource types.
@description	Icon pre-caching now uses the more sophisticated iconForResourceType: instead of obtaining the images directly from the file system (otherwise pre-cached icons would not be overridable by plug-ins). In addition it has been moved from the awakeFromNib: method into one called by a timer. This method should not be called until after the editor registry has been built.
*/

- (void)precacheIcons:(NSTimer *)timer
{
	// pre-cache a number of common icons (ignores return value, relies on iconForResourceType: to do the actual caching)
	[self iconForResourceType:@"    "];
	[self iconForResourceType:@"????"];
	[self iconForResourceType:@"CODE"];
	[self iconForResourceType:@"icns"];
	[self iconForResourceType:@"PICT"];
	[self iconForResourceType:@"plst"];
	[self iconForResourceType:@"snd "];
	[self iconForResourceType:@"TEXT"];
}

- (NSArray *)forksForFile:(FSRef *)fileRef
{
	if(!fileRef) return nil;
	
	FSCatalogInfo		catalogInfo;
	FSCatalogInfoBitmap whichInfo = kFSCatInfoNodeFlags;
	CatPositionRec		forkIterator = { 0 };
	NSMutableArray *forks = [NSMutableArray array];
	
	// check we have a file, not a folder
	OSErr error = FSGetCatalogInfo(fileRef, whichInfo, &catalogInfo, NULL, NULL, NULL);
	if(!error && !(catalogInfo.nodeFlags & kFSNodeIsDirectoryMask))
	{
		// iterate over file and populate forks array
		while(error == noErr)
		{
			HFSUniStr255 forkName;
			SInt64 forkSize;
			UInt64 forkPhysicalSize;	// used if opening selected fork fails to find empty forks
			
			error = FSIterateForks(fileRef, &forkIterator, &forkName, &forkSize, &forkPhysicalSize);
			if(!error)
			{
				NSString *fName = [NSString stringWithCharacters:forkName.unicode length:forkName.length];
				NSNumber *fSize = [NSNumber numberWithLongLong:forkSize];
				NSNumber *fAlloc = [NSNumber numberWithUnsignedLongLong:forkPhysicalSize];
				[forks addObject:[NSDictionary dictionaryWithObjectsAndKeys:fName, @"forkname", fSize, @"forksize", fAlloc, @"forkallocation", nil]];
			}
			else if(error != errFSNoMoreItems)
			{
				NSLog(@"FSIterateForks() error: %d", error);
			}
		}
	}
	else if(error)
	{
		NSLog(@"FSGetCatalogInfo() error: %d", error);
	}
	return forks;
}

- (BOOL)applicationShouldOpenUntitledFile:(NSApplication *)sender
{
#pragma unused(sender)
	NSString *launchAction = [[NSUserDefaults standardUserDefaults] stringForKey:@"LaunchAction"];
	if([launchAction isEqualToString:@"OpenUntitledFile"])
		return YES;
	else if([launchAction isEqualToString:@"DisplayOpenPanel"])
	{
		[[NSDocumentController sharedDocumentController] openDocument:sender];
		return NO;
	}
	else return NO;	// should be @"None", but we shall return NO for any other value
}

- (BOOL)application:(NSApplication *)application openFile:(NSString *)file
{
#pragma unused(application)
	// bug: check if application was an external editor (e.g. Iconographer) and update existing open file instead
	[[NSDocumentController sharedDocumentController] openDocumentWithContentsOfFile:file display:YES];
	return YES;
}

- (BOOL)applicationShouldHandleReopen:(NSApplication *)sender hasVisibleWindows:(BOOL)flag
{
#pragma unused(sender)
	return !flag;
}

- (IBAction)showAbout:(id)sender
{
	// could do with a better about box
/*	NSWindowController *wc = [[NSWindowController alloc] initWithWindowNibName:@"AboutPanel"];
	if([(NSTextView *)[[wc window] initialFirstResponder] readRTFDFromFile:[[NSBundle mainBundle] pathForResource:@"Credits" ofType:@"rtf"]])
	{
		[[wc window] center];
		[[wc window] orderFront:nil];
	}
	else*/ [NSApp orderFrontStandardAboutPanel:sender];
}

- (IBAction)visitWebsite:(id)sender
{
	[[NSWorkspace sharedWorkspace] openURL:[NSURL URLWithString:@"http://web.nickshanks.com/resknife/"]];
}

- (IBAction)visitSourceforge:(id)sender
{
	[[NSWorkspace sharedWorkspace] openURL:[NSURL URLWithString:@"http://resknife.sourceforge.net/"]];
}

- (IBAction)emailDeveloper:(id)sender
{
	[[NSWorkspace sharedWorkspace] openURL:[NSURL URLWithString:@"mailto:resknife@nickshanks.com?Subject=Comments,%20Suggestions%20and%20Bug%20Reports"]];
}

- (IBAction)showInfo:(id)sender
{
	[[InfoWindowController sharedInfoWindowController] showWindow:sender];
}

- (IBAction)showPasteboard:(id)sender
{
	[[PasteboardWindowController sharedPasteboardWindowController] showWindow:sender];
}

- (IBAction)showPrefs:(id)sender
{
	[[PrefsWindowController sharedPrefsWindowController] showWindow:sender];
}

- (void)initUserDefaults
{
	// This should probably be added to NSUserDefaults as a category,
	//	since its universally useful.  It loads a defaults.plist file
	//	from the app wrapper, and then sets the defaults if they don't
	//	already exist.
	
	NSUserDefaults *defaults;
	NSDictionary *defaultsPlist;
	NSEnumerator *overDefaults;
	id eachDefault;
	
	// this isn't required, but saves us a few method calls
	defaults = [NSUserDefaults standardUserDefaults];
	
	// load the defaults.plist from the app wrapper.  This makes it
	//	easy to add new defaults just using a text editor instead of
	//	hard-coding them into the application
	defaultsPlist = [NSDictionary dictionaryWithContentsOfFile:[[NSBundle mainBundle] pathForResource:@"defaults" ofType:@"plist"]];
	
	// enumerate over all the keys in the dictionary
	overDefaults = [[defaultsPlist allKeys] objectEnumerator];
	while(eachDefault = [overDefaults nextObject])
	{
		// for each key in the dictionary
		//	check if there is a value already registered for it
		//	and if there isn't, then register the value that was in the file
		if(![defaults stringForKey:eachDefault])
		{
			[defaults setObject:[defaultsPlist objectForKey:eachDefault] forKey:eachDefault];
		}
	}
	
	// force the defaults to save to the disk
	[defaults synchronize];
}

- (OpenPanelDelegate *)openPanelDelegate
{
	return openPanelDelegate;
}

/*!
@method			iconForResourceType:
@author			Nicholas Shanks
@created		2003-10-24
@abstract		Manages the cache of icons used for representing resource types.
@description	This method loads icons for each resource type from a variety of places and caches them for faster access. Your plug-in may be asked to return an icon for any resource type it declares it can edit. To implement this, your plug should respond to the iconForResourceType: selector with the same method signature as this method. The icons can be in any format recognised by NSImage. Alternativly, just leave your icons in "Your.plugin/Contents/Resources/Resource Type Icons/" (or any equivalent localised directory) with a name like "TYPE.tiff" and ResKnife will retrieve them automatically.
@pending		I don't like the name I chose here for the resource type icons directory. Can anyone think of something better?
*/

- (NSImage *)iconForResourceType:(NSString *)resourceType
{
	NSImage *icon = nil;
	if([resourceType isEqualToString:@""])
		resourceType = nil;
	
	if(resourceType)
	{
		// check if we have image in cache already
		icon = [[self _icons] objectForKey:resourceType];		// valueForKey: raises when the resourceType begins with '@' (e.g. the @GN4 owner resource from Gene!)
		
		if(!icon)
		{
			NSString *iconPath = nil;
			
			// try to load icon from the default editor for that type
			Class editor = [[RKEditorRegistry defaultRegistry] editorForType:resourceType];
			if(editor)
			{
				// ask politly for icon
				if([editor respondsToSelector:@selector(iconForResourceType:)])
					icon = [editor iconForResourceType:resourceType];
				
				// try getting it myself
				if(!icon)
				{
					iconPath = [[NSBundle bundleForClass:editor] pathForResource:resourceType ofType:nil inDirectory:@"Resource Type Icons"];
					if(iconPath)
						icon = [[[NSImage alloc] initWithContentsOfFile:iconPath] autorelease];
				}
			}
			
			// try to load icon from the ResKnife app bundle itself
			if(!icon)
			{
				iconPath = [[NSBundle mainBundle] pathForResource:resourceType ofType:nil inDirectory:@"Resource Type Icons"];
				if(iconPath)
					icon = [[[NSImage alloc] initWithContentsOfFile:iconPath] autorelease];
			}
			
			// try to retrieve from file system using our resource type to file name extension/bundle identifier code
			if(!icon)
			{
				NSString *fileType = [[NSBundle mainBundle] localizedStringForKey:resourceType value:@"" table:@"Resource Type Mappings"];
				NSRange range = [fileType rangeOfString:@"."];
				if(range.location == NSNotFound)
					icon = [[NSWorkspace sharedWorkspace] iconForFileType:fileType];
				else	// a '.' character in a file type means ResKnife should look for a bundle icon with fileType as the bundle's identifier
				{
					NSString *bundlePath = [[NSBundle bundleWithIdentifier:fileType] bundlePath];
					if(bundlePath)
						icon = [[NSWorkspace sharedWorkspace] iconForFile:bundlePath];
				}
			}
			
			// TODO: convert to a UTI and try that 
			
			// try to retrieve from file system as an OSType code
			if(!icon)
				icon = [[NSWorkspace sharedWorkspace] iconForFileType:[NSString stringWithFormat:@"'%@'", resourceType]];
			
			// save the newly retrieved icon in the cache
			if(icon)
				[[self _icons] setObject:icon forKey:resourceType];
		}
	}
	else
	{
		// we have no resource type, try to get a generic icon - this is what icon represented forks get
//		if(!icon)	icon = [NSImage imageNamed:@"NSMysteryDocument"];
//		if(!icon)	icon = [[NSWorkspace sharedWorkspace] iconForFileType:@"'    '"];
		if(!icon)	icon = [[NSWorkspace sharedWorkspace] iconForFileType:[NSString stringWithFormat:@"'%@'", @"????"]];
	}
	
	// return the cached icon, or nil if none was found
	return icon;
}

- (NSMutableDictionary *)_icons
{
	return _icons;
}

- (NSDictionary *)icons
{
	return [NSDictionary dictionaryWithDictionary:[self _icons]];
}

@end