//
//  MyDocument.m
//  ScaleAndPadImages
//
//  Created by Frank Mcpherson on 2/28/07.
//  Copyright Frank H. McPherson IV, 2007. All rights reserved.
//

#import "MyDocument.h"
#import "ImageFilter.h"
#import "BWSnippetKeeper.h"
#import "BWThumbnailHTMLAccumulator.h"

/* This is also acting as the controller, mostly out of laziness.
 *
 */
@interface MyDocument (private) 
- (NSString *)chooseDirectoryFromPanel:(NSOpenPanel *)panel;
- (void)loadImagesForPath:(NSString *)path;
- (void)processImagesForRect:(CGRect)targetRect 
					 withTag:(NSString *)fileTag 
		 withBackgroundColor:(NSColor *)color;	
@end

@implementation MyDocument


- (id)init
{
    self = [super init];
    if (self) {
		snippets = [[BWSnippetKeeper alloc] init];
		
		inPath = nil;
		outPath = nil;
		[self setDefaults];
		sharedDefaults = [[NSUserDefaultsController sharedUserDefaultsController] values];
    }
    return self;
}

- (void)dealloc {
	[snippets release];
	if (imageArray) [imageArray release];
	if (pathArray) [pathArray release];
	
	[super dealloc];
}

- (NSString *)windowNibName
{
    // Override returning the nib file name of the document
    // If you need to use a subclass of NSWindowController or if your document supports multiple NSWindowControllers, you should remove this method and override -makeWindowControllers instead.
    return @"MyDocument";
}

- (void)windowControllerDidLoadNib:(NSWindowController *) aController
{
    [super windowControllerDidLoadNib:aController];
	
}

- (NSData *)dataRepresentationOfType:(NSString *)aType
{
    // Insert code here to write your document from the given data.  You can also choose to override -fileWrapperRepresentationOfType: or -writeToFile:ofType: instead.
    
    // For applications targeted for Tiger or later systems, you should use the new Tiger API -dataOfType:error:.  In this case you can also choose to override -writeToURL:ofType:error:, -fileWrapperOfType:error:, or -writeToURL:ofType:forSaveOperation:originalContentsURL:error: instead.

    return nil;
}

- (BOOL)loadDataRepresentation:(NSData *)data ofType:(NSString *)aType
{
    // Insert code here to read your document from the given data.  You can also choose to override -loadFileWrapperRepresentation:ofType: or -readFromFile:ofType: instead.
    
    // For applications targeted for Tiger or later systems, you should use the new Tiger API readFromData:ofType:error:.  In this case you can also choose to override -readFromURL:ofType:error: or -readFromFileWrapper:ofType:error: instead.
    
    return YES;
}


- (void) setDefaults {
	NSMutableDictionary *dictionary = [NSMutableDictionary dictionary];
	[dictionary setObject:[NSNumber numberWithInt:512] forKey:@"imageDimension"];
	[dictionary setObject:[NSNumber numberWithInt:146] forKey:@"thumbnailDimension"];
	[dictionary setObject:[NSArchiver archivedDataWithRootObject:[NSColor blackColor]] 
				   forKey:@"imageBackgroundColor"];
	[dictionary setObject:[NSArchiver archivedDataWithRootObject:[NSColor redColor]] 
				   forKey:@"thumbnailBackgroundColor"];
	
	[[NSUserDefaultsController sharedUserDefaultsController] setInitialValues:dictionary];
}

// given a key into the user defaults, get an int value from there
- (int) intFromDefaultsFor: (NSString *) string
{
	NSNumber *number;
    number = [sharedDefaults valueForKey: string];
	
    return ([number intValue]);
	
} // intFromDefaults

- (NSColor *)colorForKey:(NSString *)aKey
{
    NSColor *theColor=nil;
    NSData *theData=[sharedDefaults valueForKey:aKey];
    if (theData != nil)
        theColor=(NSColor *)[NSUnarchiver unarchiveObjectWithData:theData];
    return theColor;
}
- (NSColor *)thumbnailBackgroundColor {
	return [self colorForKey:@"thumbnailBackgroundColor"];
}

- (NSColor *)imageBackgroundColor {
	return [self colorForKey:@"imageBackgroundColor"];
}
// Accessors for getting values from user defaults
- (int) imageDimension {
	return ([self intFromDefaultsFor:@"imageDimension"]);
}

- (int) thumbnailDimension {
	return ([self intFromDefaultsFor:@"thumbnailDimension"]);
}

// This should actually be called Paths Set and Valid
- (bool)pathsSet {
	if ((inPath != nil) && (outPath != nil)) return YES;
	else return NO;
}

- (IBAction)chooseDirectory:(id)sender
{	
	NSOpenPanel *panel = [NSOpenPanel openPanel];
	[self willChangeValueForKey:@"pathsSet"];
	NSString *newPath;
	// Minor evil - I set the tag for the buttons in IB, so I know 
	// 0 is the input directory button tag and 1 is the output 
	// directory button tag
	switch ([sender tag]) {
		case 0:	newPath = [self chooseDirectoryFromPanel:panel];
			if (newPath != nil) {
				[inPath autorelease];
				[newPath retain];
				inPath = newPath;
				[sender setTitle:newPath];
			}
			break;
			// allow the output directory to be created 
		case 1: [panel setCanCreateDirectories:YES];
			newPath = [self chooseDirectoryFromPanel:panel];
			if (newPath != nil) {
				[outPath autorelease];
				[newPath retain];
				outPath = newPath;
				[sender setTitle:newPath];
			}
			break;
		default:
			NSLog(@"Ooops, unable to set directory");
	}
	[self didChangeValueForKey:@"pathsSet"];
}


// This is called when the Process Images button is pressed in the UI, 
// bound via AController
- (void)processImages {
	// Make sure the user really wants to overwrite the source images if the paths are the same
	if ([inPath isEqualToString:outPath]) {
		NSAlert *pathsEqual = [NSAlert alertWithMessageText:@"Input and output directories are identical"
											  defaultButton:@"Keep source images" 
											alternateButton:@"Over-write source images"
												otherButton:nil
								  informativeTextWithFormat:@"This action will replace the source images with the scaled and padded images."];
		int result = [pathsEqual runModal];
		// If the user chooses not to over-write, skip the processImages step
		if (result == NSAlertDefaultReturn) return;
	}
	
	// First display a progress sheet
	[NSApp beginSheet:sheet modalForWindow:[self windowForSheet]
		modalDelegate:self didEndSelector:NULL contextInfo:nil];
	
	// Now detach a new thread to perform the image filtering
	[NSThread detachNewThreadSelector:@selector(processImagesWorker) toTarget:self withObject:nil];
}


// User wants to edit the html. Show the editing window in a sheet
// Put in a live preview for this one based on webkit
- (IBAction) showHTMLSnippets: (id) sender
{
    [snippets showSnippetsAsSheetInWindow: [self windowForSheet]];
	
} // showHTMLSnippets

@end

@implementation MyDocument (private) 

// Loop through the contents of inPath, and if we find a jpg file, filter the image 
// t
- (void)processImagesForRect:(CGRect)targetRect 
					 withTag:(NSString *)fileTag 
		 withBackgroundColor:(NSColor *)color;
{
	// Prepare the image filter
	ImageFilter *filter = [[ImageFilter alloc] initWithTargetSize:targetRect backgroundColor:color];
	
	CIImage *inImage;
	NSString *outFile;
	
	int i;
	
	[self willChangeValueForKey:@"imageIndex"];
	for (i=0;i<[imageArray count];i++,imageIndex++) {
		[self didChangeValueForKey:@"imageIndex"];
		inImage = [imageArray objectAtIndex:i];
		outFile = [pathArray objectAtIndex:i];
		[filter saveImage:[filter filterImage2:inImage]
				   toFile:[NSString stringWithFormat:@"%@/%@%@", outPath, fileTag, outFile]];
		[self willChangeValueForKey:@"imageIndex"];
	}	
	// Release the filter
	[filter release];
}

// This runs on a separate thread from the app event loop so we don't show
// the pizza wheel. Call processImages:withTag:withBackgroundColor: for
// each of our image sizes. 
- (void)processImagesWorker {
	NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
	
	// Load the images
	[self loadImagesForPath:inPath];
	
	// Notify bindings we've changed the image count so the progress bar can work
	[self willChangeValueForKey:@"imageCount"];
	imageCount = [imageArray count] * 2;
	[self didChangeValueForKey:@"imageCount"];
	[self willChangeValueForKey:@"imageIndex"];
	imageIndex = 0;
	[self didChangeValueForKey:@"imageIndex"];
	
	// Add the color well by passing in the color here
	[self processImagesForRect:CGRectMake(0.0f, 0.0f, [self thumbnailDimension], [self thumbnailDimension])
					   withTag:@"thumb_"
		   withBackgroundColor:[self thumbnailBackgroundColor]];
	[self processImagesForRect:CGRectMake(0.0f, 0.0f, [self imageDimension], [self imageDimension])
					   withTag:@""
		   withBackgroundColor:[self imageBackgroundColor]];
	
	if ([self intFromDefaultsFor:@"generateHTMLGallery"]) {
		// If the user wants to generate HTML, make an accumulator to do it. 
		BWThumbnailHTMLAccumulator *html = [BWThumbnailHTMLAccumulator accumulatorWithSnippets:snippets];
		[html addHeader];
		[html addBeforeThumbnails];
		NSEnumerator *e = [pathArray objectEnumerator];
		NSString *imageName;
		
		NSSize size;
		size.height = size.width = [self thumbnailDimension];
		// Right now I'm just adding the thumbnails
		while (imageName = [e nextObject]) {
			[html addImageNamed:nil 
				 thumbnailNamed:[NSString stringWithFormat:@"%@%@", @"thumb_", imageName] 
						   size:size];
		}
		[html addAfterThumbnails];
		[html addFooter];
		[html writeToFile:[outPath stringByAppendingString:@"/index.html"]];
	}
	
	[sheet orderOut:nil];
	[NSApp endSheet:sheet];
	// Release the arrays since we're done with them		
	[imageArray release];
	imageArray = nil;
	[pathArray release];
	pathArray = nil;
	[pool release];

}


// Return a shallow list of all the image files we can process at inPath
- (void)loadImagesForPath:(NSString *)path; {
	NSFileManager *fileManager = [NSFileManager defaultManager];
	NSString *file, *inFullPath;
	NSArray *directoryContents = [fileManager directoryContentsAtPath:inPath];
	if (imageArray) [imageArray release];
	imageArray = [[NSMutableArray alloc] initWithCapacity:[directoryContents count]];
	if (pathArray) [pathArray release];
	pathArray = [[NSMutableArray alloc] initWithCapacity:[directoryContents count]];
	NSEnumerator *e = [directoryContents objectEnumerator];
	CIImage *inImage;
	
	while (file = [e nextObject]) {
		inFullPath = [NSString stringWithFormat:@"%@/%@", path, file];
		inImage = [CIImage imageWithContentsOfURL:[NSURL fileURLWithPath:inFullPath]];
		if (inImage == nil) {
			NSLog(@"Can't make a CIImage out of %@", file);
		} else {
			[imageArray addObject:inImage];
			[pathArray addObject:file];
		}
			
	}
}


// A helper to make sure the right options are set on the panel
- (NSString *)chooseDirectoryFromPanel:(NSOpenPanel *)panel {
	NSString *path = nil;
	[panel setCanChooseFiles:NO];
	[panel setCanChooseDirectories:YES];
	[panel setAllowsMultipleSelection:NO];
	if ([panel runModalForTypes:nil] == NSOKButton) {
		if ([[panel filenames] count] == 1) {
			path = [[panel filenames] lastObject];
		} else {
			NSLog(@"Open panel returned too many dirs (%d)", [[panel filenames] count]);
		}
	} else {
		NSLog(@"User canceled");
	} 	
	return path;
}

@end
