#import <Carbon/Carbon.h>

#import "MPT_AUAppDelegate.h"
#import "MPT_CERT.h"
#import "MPT_Pair.h"
#import "MPT_FaceView.h"
#import "MPT_Face.h"
#import "MPT_AUView.h"
#import "MPT_ResolveAlias.h"

#import "TestController.h"

@interface MPT_AUAppDelegate(PrivateMethods)
- (void)handleResults:(NSImage *)image;
- (BOOL)loading;
- (void)setLoading:(BOOL)value;
- (BOOL)recording;
- (void)setRecording:(BOOL)value;
- (void)startRecording;
- (void)stopRecording;
- (void)recordDataPoint;
- (NSString *)recordDataPoint_NRL;
- (void)captureFace:(NSRect)face inImage:(NSImage *)image;
@end

@implementation MPT_AUAppDelegate
- (void)awakeFromNib
{
	cert = [[MPT_CERT alloc] init];
	videoController = [[TestController alloc] init];
	[imageView resetFaceAndEyes];
	
	[AU4 setAU:4];
	[AU12 setAU:12];
	[AU20 setAU:20];
	[smileView retain];
	[smileView setAUString:@"Smile"];
//#define NRL_DEMO
#ifdef NRL_DEMO
	[AU20 setFrameOrigin:[AU4 frame].origin];
	[AU4 setFrameOrigin:[AU1 frame].origin];
	[AU12 setFrameOrigin:[AU2 frame].origin];
	[smileView setFrameOrigin:[AU5 frame].origin];
	
	[AU1 removeFromSuperview];
	[AU2 removeFromSuperview];
	[AU5 removeFromSuperview];
	[AU10 removeFromSuperview];
	[AU14 removeFromSuperview];
	AU1 = nil;
	AU2 = nil;
	AU5 = nil;
	AU10 = nil;
	AU14 = nil;
#else
	[AU1 setAU:1];
	[AU2 setAU:2];
	[AU5 setAU:5];
	[AU10 setAU:10];
	[AU14 setAU:14];
#endif
	
	[self setDoCERT:[cert doCERT]];
	[self setFindFaces:[cert findFaces]];
	[self setLoading:YES];
	[self setDisplayGray:[cert displayGray]];
	[self setDoSmile:[cert doSmile]];
	[self setDoFilters:[cert doFilters]];
	[self setVideoWidth:[videoController videoWidth]];
	[self setVideoHeight:[videoController videoHeight]];
	[self setCaptureFaces:NO];
	nextFace = 1;
	[self setLastCapture:[NSDate distantPast]];
	
    [self setEyefinderOffset:[cert eyefinderOffset]];
    [self setEyefinderDropoff:[cert eyefinderDropoff]];
}

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

- (void)applicationDidFinishLaunching:(NSNotification *)aNotification
{
	BOOL optKeyDown = ((GetCurrentKeyModifiers() & (optionKey |  rightOptionKey)) != 0) ? YES : NO;

	[[self window] makeKeyAndOrderFront:self];
	
	// Don't load the kernels if the option key is down.
	if( !optKeyDown )
	{
		[cert loadAllKernelsWithProgress:dataLoading];
		[self setDoCERT:[cert doCERT]]; // If any file failed to load, CERT will be turned off so we need to reset our UI.
	}
	
	[self setLoading:NO];
}

- (NSWindow *)window
{
	return window;
}

- (IBAction)chooseFolder:(id)sender
{
	NSOpenPanel *panel = [NSOpenPanel openPanel];
	[panel setCanChooseDirectories:YES];
	[panel setAllowsMultipleSelection:NO];
	[panel setCanChooseFiles:NO];
	
	[panel beginSheetForDirectory:nil
							 file:nil
							types:nil
				   modalForWindow:[self window]
					modalDelegate:self
				   didEndSelector:@selector(openFolderPanelDidEnd:returnCode:contextInfo:)
					  contextInfo:nil];
}

- (void)openFolderPanelDidEnd:(NSOpenPanel *)panel returnCode:(int)returnCode  contextInfo:(void  *)contextInfo
{
	if( returnCode == NSOKButton )
	{
		NSArray *filenames = [panel filenames];
		if( [filenames count] > 0 )
		{
			NSString *path = [filenames objectAtIndex:0];
			if( [path length] > 0 )
			{
				[NSTimer scheduledTimerWithTimeInterval:0.0 target:self selector:@selector(folderTimerFired:) userInfo:path repeats:NO];
			}
		}
	}
}

- (void)folderTimerFired:(NSTimer *)timer
{
	NSFileManager *fileManager = [NSFileManager defaultManager];
	NSString *path = [timer userInfo];
	NSArray *images = [fileManager directoryContentsAtPath:path];
	NSEnumerator *imageEnum = [images objectEnumerator];
	NSString *imageName;

	while( imageName = [imageEnum nextObject] )
	{
		NSString *imagePath = [[[[path stringByAppendingPathComponent:imageName] stringByStandardizingPath] stringByResolvingSymlinksInPath] stringByResolvingAliases];
		NSDictionary *fileAttributes = [fileManager fileAttributesAtPath:imagePath traverseLink:YES];

		if( [[fileAttributes objectForKey:NSFileType] isEqualToString:NSFileTypeRegular]
			|| [[fileAttributes objectForKey:NSFileType] isEqualToString:NSFileTypeSymbolicLink] )
		{
			NSImage *image = [[NSImage alloc] initWithContentsOfFile:imagePath];
			if( image )
			{
				MPT_Pair *pair = [MPT_Pair pairWith:image and:imagePath];
				[NSTimer scheduledTimerWithTimeInterval:0.03 target:self selector:@selector(lastImageChangedTimer:) userInfo:pair repeats:NO];
			}
			else
			{
				if( [[imagePath pathExtension] isEqualToString:@"rimage"] )
				{
					[NSTimer scheduledTimerWithTimeInterval:0.03 target:self selector:@selector(lastImageChangedTimer:) userInfo:imagePath repeats:NO];
				}
			}			
		}
	}
}

- (IBAction)chooseImage:(id)sender
{
	NSArray *rimageTypes = [NSArray arrayWithObjects:@"rimage", @"rimage_bin", @"rimage_xml", nil];
	NSOpenPanel *panel = [NSOpenPanel openPanel];
	[panel setCanChooseDirectories:NO];
	[panel setAllowsMultipleSelection:NO];
	[panel setCanChooseFiles:YES];
	
	[panel beginSheetForDirectory:nil
							 file:nil
							types:[[NSImage imageFileTypes] arrayByAddingObjectsFromArray:rimageTypes]
				   modalForWindow:[self window]
					modalDelegate:self
				   didEndSelector:@selector(openPanelDidEnd:returnCode:contextInfo:)
					  contextInfo:nil];

	
}

- (BOOL)doCERT
{
	return [cert doCERT];
}
- (void)setDoCERT:(BOOL)value
{
	[cert setDoCERT:value];
}
- (BOOL)displayGray
{
    return [cert displayGray];
}
- (void)setDisplayGray:(BOOL)value
{
	[cert setDisplayGray:value];
}

- (BOOL)doSmile
{
    return [cert doSmile];
}
- (void)setDoSmile:(BOOL)value
{
	[cert setDoSmile:value];
	if( value )
	{
		[[[self window] contentView] addSubview:smileView];
	}
	else
	{
		[smileView removeFromSuperview];
	}
}

- (BOOL)doFilters
{
    return [cert doFilters];
}

- (void)setDoFilters:(BOOL)value
{
    [cert setDoFilters:value];
	if( value )
	{
		[AU1 setMin:0.0]; [AU1 setMax:1.0];
		[AU2 setMin:0.0]; [AU2 setMax:1.0];
		[AU4 setMin:0.0]; [AU4 setMax:1.0];
		[AU5 setMin:0.0]; [AU5 setMax:1.0];
		[AU10 setMin:0.0]; [AU10 setMax:1.0];
		[AU12 setMin:0.0]; [AU12 setMax:1.0];
		[AU14 setMin:0.0]; [AU14 setMax:1.0];
		[AU20 setMin:0.0]; [AU20 setMax:1.0];
		[smileView setMin:0.0]; [smileView setMax:1.0];
	}
	else
	{
		[AU1 setMin:-0.5]; [AU1 setMax:0.5];
		[AU2 setMin:-0.5]; [AU2 setMax:0.5];
		[AU4 setMin:-0.5]; [AU4 setMax:0.5];
		[AU5 setMin:-0.5]; [AU5 setMax:0.5];
		[AU10 setMin:-0.5]; [AU10 setMax:0.5];
		[AU12 setMin:-0.5]; [AU12 setMax:0.5];
		[AU14 setMin:-0.5]; [AU14 setMax:0.5];
		[AU20 setMin:-0.5]; [AU20 setMax:0.5];
		[smileView setMin:-0.5]; [smileView setMax:0.5];
	}
}

- (BOOL)captureFaces
{
    return captureFaces;
}
- (void)setCaptureFaces:(BOOL)value
{
    captureFaces = value;
}

- (NSDate *)lastCapture
{
    return lastCapture;
}

- (void)setLastCapture:(NSDate *)value
{
    if( lastCapture != value )
    {
        [lastCapture release];
        lastCapture = [value retain];
    }
}

- (BOOL)findFaces
{
	return [cert findFaces];
}
- (void)setFindFaces:(BOOL)value
{
	[cert setFindFaces:value];
	if( !value )
		[imageView resetFaceAndEyes];
}

- (float)videoWidth {
    return [videoController videoWidth];
}

- (void)setVideoWidth:(float)value {
	[videoController setVideoWidth:value];
}

- (float)videoHeight {
	return [videoController videoHeight];
}

- (void)setVideoHeight:(float)value {
	[videoController setVideoHeight:value];
}

- (BOOL)loading
{
    return loading;
}

- (void)setLoading:(BOOL)value
{
    if (loading != value) {
        loading = value;
    }
}

- (float)eyefinderOffset
{
    return [cert eyefinderOffset];
}

- (void)setEyefinderOffset:(float)value
{
    [cert setEyefinderOffset:value];
}

- (float)eyefinderDropoff
{
    return [cert eyefinderDropoff];
}

- (void)setEyefinderDropoff:(float)value
{
    [cert setEyefinderDropoff:value];
}

- (IBAction)restoreEyefinderDefaults:(id)sender
{
	[self setEyefinderOffset:[cert defaultEyefinderOffset]];
	[self setEyefinderDropoff:[cert defaultEyefinderDropoff]];
}

- (MPT_CERT *)cert
{
    return cert;
}

- (BOOL)recording
{
    return recording;
}
- (void)setRecording:(BOOL)value
{
    recording = value;
	if( recording )
	{
		[recordButton setTitle:@"Stop Recording"];
	}
	else
	{
		[recordButton setTitle:@"Start Recording"];
	}
}
- (void)toggleRecording:(id)sender
{
	if( [self recording] )
	{
		[self stopRecording];
	}
	else
	{
		[self startRecording];
	}
}

- (void)startRecording
{
	[self setRecording:YES];
	[recordedData release];
	recordedData = [[NSMutableArray alloc] init];
}
- (void)stopRecording
{
	[self setRecording:NO];
	NSString *dataString = [recordedData componentsJoinedByString:@"\n"];
	
	NSString *data = [dataTextView string];
	[dataTextView replaceCharactersInRange:NSMakeRange([data length],0) withString:[NSString stringWithFormat:@"\n%@", dataString]];
		
//	[dataTextView setString:dataString];
//	[dataWindow makeKeyAndOrderFront:self];
//	[dataWindow orderWindow:NSWindowBelow relativeTo:0];
//	[[self window] makeKeyAndOrderFront:self];
}
- (void)recordDataPoint
{
	NSMutableString *resString = [NSMutableString string];
	if( [self doCERT] )
	{
		[resString appendString:@"AUs: "]; 
		NSEnumerator *resEnum = [[cert results] objectEnumerator];
		NSNumber *num;
		while( num = [resEnum nextObject] )
		{
			[resString appendFormat:@"%.6lf ", [num doubleValue]];
		}
	}
	
	NSString *smile = @"";
	if( [self doSmile] )
		smile = [NSString stringWithFormat:@"\tSmile: %lf", [cert smile]];
	
	NSString *tilt = @"";
//	if( [[cert faces] count] > 0 )
//	{
//		float headTilt = [[cert largestFace] headTilt];
//		tilt = [NSString stringWithFormat:@"\tTilt: %f", headTilt ];
//		
//	}
	
	NSString *file = @"";
	if( [imageView lastDropPath] )
		file = [NSString stringWithFormat:@"\tFile: %@", [imageView lastDropPath]];
	
	NSString *res = [NSString stringWithFormat:@"%@%@%@\tLeft: %@\tRight: %@%@",
		resString, smile, tilt, NSStringFromPoint( [cert left] ), NSStringFromPoint( [cert right] ), file];
	[recordedData addObject:res];
}

- (NSString *)recordDataPoint_NRL
{
	NSString *filePath = [imageView lastDropPath]; // D006_S014_face_9.jpg
	NSArray *nameParts = [[[filePath lastPathComponent] stringByDeletingPathExtension] componentsSeparatedByString:@"_"];
	NSString *frame = @"UNK";
	
	if( [nameParts count] >= 4 )
		frame = [nameParts objectAtIndex:3];
		
	NSMutableString *resString = [NSMutableString string];
	[resString appendFormat:@"%@, ", frame];
	
	NSEnumerator *resEnum = [[cert results] objectEnumerator];
	NSNumber *num;
	while( num = [resEnum nextObject] )
	{
		[resString appendFormat:@"%.6lf, ", [num doubleValue]];
	}
	
	[resString deleteCharactersInRange:NSMakeRange( [resString length]-2, 2 )];
	
	return resString;
}

- (IBAction)clearDataWindow:(id)sender
{
	[dataTextView setString:@""];
}
- (void)saveDataWindow:(id)sender
{
	NSSavePanel *panel = [NSSavePanel savePanel];
	[panel setPrompt:@"Save Data"];
	[panel setTitle:@"Save Data"];
		
	[dataWindow makeKeyAndOrderFront:self];
	[panel beginSheetForDirectory:nil
							 file:@"AU Data.txt"
				   modalForWindow:dataWindow
					modalDelegate:self
				   didEndSelector:@selector(savePanelDidEnd:returnCode:contextInfo:)
					  contextInfo:nil];
}
- (void)savePanelDidEnd:(NSOpenPanel *)panel returnCode:(int)returnCode  contextInfo:(void  *)contextInfo
{
	if( returnCode == NSOKButton )
	{
		NSArray *filenames = [panel filenames];
		if( [filenames count] > 0 )
		{
			NSString *path = [filenames objectAtIndex:0];
			if( [path length] > 0 )
			{
				NSString *dataString = [dataTextView string];
				[dataString writeToFile:path atomically:NO];
				[dataTextView setString:@""];
			}
		}
	}
}

- (void)openPanelDidEnd:(NSOpenPanel *)panel returnCode:(int)returnCode  contextInfo:(void  *)contextInfo
{
	if( returnCode == NSOKButton )
	{
		NSEnumerator *fileEnum = [[panel filenames] objectEnumerator];
		NSString *path;
		while( path = [fileEnum nextObject] )
		{
			if( [path length] > 0 )
			{
				NSImage *image = [[NSImage alloc] initWithContentsOfFile:path];
				if( image )
				{
//					[fileNameField setStringValue:path];
					[imageView setLastDropPath:path];
//					[imageView setImage:image];
//					[self startRecording];
					MPT_Pair *pair = [MPT_Pair pairWith:image and:path];
					[NSTimer scheduledTimerWithTimeInterval:0.0 target:self selector:@selector(lastImageChangedTimer:) userInfo:pair repeats:NO];
//					[self imageChanged:image];
				}
				else
				{
					if( [[path pathExtension] hasPrefix:@"rimage"] )
					{
						[NSTimer scheduledTimerWithTimeInterval:0.0 target:self selector:@selector(lastImageChangedTimer:) userInfo:path repeats:NO];
					}
				}
			}
		}
	}
}

- (void)lastImageChangedTimer:(NSTimer *)timer
{
	[self imageChanged:[timer userInfo]];
}

- (IBAction)imageChanged:(id)sender
{
	BOOL haveRImage = NO;
	NSImage *image = NULL;
	NSString *imagePath = NULL;
	if( [sender isKindOfClass:[NSImage class]] )
	{
		image = (NSImage *)sender;
	}
	else if( [sender isKindOfClass:[MPT_Pair class]] )
	{
		MPT_Pair *pair = (MPT_Pair *)sender; 
		image = [pair first];
		imagePath = [pair second];
	}
	else if( [sender isKindOfClass:[MPT_FaceView class]] )
	{	
		image = [imageView image];
		imagePath = [sender lastDropPath];
	}
	else if( [sender isKindOfClass:[NSString class]] )
	{
		haveRImage = YES;
		image = NULL;
		imagePath = sender;
	}
	else
		return;
	
	if( imagePath )
	{
		[imageView setLastDropPath:imagePath];
		[fileNameField setStringValue:imagePath];
	}
	
	[leftField setStringValue:@""];
	[rightField setStringValue:@""];
	[imageView resetFaceAndEyes];
	
	[progress startAnimation:self];

	[cert setDoingSequence:NO];
	if( haveRImage )
		[cert processRImageFile:imagePath];
	else
		[cert processImage:image];
	
	if( [self displayGray] || haveRImage )
		[self handleResults:[cert grayImage]];
	else
		[self handleResults:image];
	[progress stopAnimation:self];
}

- (void)handleResults:(NSImage *)image
{
	NSArray *results = [cert results];

	[AU1 clearValue];
	[AU2 clearValue];
	[AU4 clearValue];
	[AU5 clearValue];
	[AU10 clearValue];
	[AU12 clearValue];
	[AU14 clearValue];
	[AU20 clearValue];
	
	if( [results count] > 7 )
	{
		[AU1 addValue:[[results objectAtIndex:0] doubleValue]];
		[AU2 addValue:[[results objectAtIndex:1] doubleValue]];
		[AU4 addValue:[[results objectAtIndex:2] doubleValue]];
		[AU5 addValue:[[results objectAtIndex:3] doubleValue]];
		[AU10 addValue:[[results objectAtIndex:4] doubleValue]];
		[AU12 addValue:[[results objectAtIndex:5] doubleValue]];
		[AU14 addValue:[[results objectAtIndex:6] doubleValue]];
		[AU20 addValue:[[results objectAtIndex:7] doubleValue]];
	}
	else
	{
		[AU1 addNoValue];
		[AU2 addNoValue];
		[AU4 addNoValue];
		[AU5 addNoValue];
		[AU10 addNoValue];
		[AU12 addNoValue];
		[AU14 addNoValue];
		[AU20 addNoValue];
	}

	if( [self doSmile] )
		[smileView addValue:[cert smile]];
	else
		[smileView addNoValue];

	if( [cert left].x > -1 )
		[leftField setStringValue:NSStringFromPoint( [cert left] )];
	if( [cert right].x > -1 )
		[rightField setStringValue:NSStringFromPoint( [cert right] )];
	[imageView setLastFace:[cert face]];
	[imageView setLastLeftEye:[cert left]];
	[imageView setLastRightEye:[cert right]];
	[imageView setFaces:[cert faces]];
	
	NSImage *gray = NULL; // [cert grayImage];
	if( gray )
		[imageView setImage:gray];
	else
		[imageView setImage:image];	
	
	if( [self recording] )
		[self recordDataPoint];
	
	if( [self captureFaces] )
	{
		NSRect face = [cert face];
		if( face.origin.x != -1 )
		{
			// Capture images at most once per second
			NSTimeInterval diff = [[self lastCapture] timeIntervalSinceNow];
			if( diff < -1.0 )
			{
				[self captureFace:face inImage:image];
				[self setLastCapture:[NSDate date]];
			}
		}
	}
}

- (IBAction)startVideo:(id)sender
{
	if( videoTimer )
	{
		[progress stopAnimation:self];
		[videoController stop];
		videoTimer = NO;
		[cert setDoingSequence:NO];
	}
	else
	{
		videoTimer = YES;
		[fileNameField setStringValue:@""];
		[progress startAnimation:self];
		[imageView resetFaceAndEyes];
		[videoController start];
		[cert setDoingSequence:YES];
		if( ![videoController cameraStarted] )
		{
			[progress stopAnimation:self];
			[videoController stop];
			videoTimer = NO;
			NSRunAlertPanel( @"Video Error", @"No video source was found, or an error was encountered while trying to access the video.", @"OK", nil, nil );
		}
		else
		{
			[NSTimer scheduledTimerWithTimeInterval:0.0 target:self selector:@selector(videoTimerFired:) userInfo:NULL repeats:NO];
		}
	}
}

- (void)videoTimerFired:(NSTimer *)timer
{
	if( videoTimer )
	{
		NSImage *vImage = (NSImage *)[videoController lastImage];
		if( vImage )
		{
			[vImage retain];
			if( [cert findFaces] )
			{
				[cert processImage:vImage];

				if( [self displayGray] )
					[self handleResults:[cert grayImage]];
				else
					[self handleResults:vImage];
			}
			else
			{
				[imageView setImage:vImage];	
			}
			[frameRateField setFloatValue:[videoController frameRate]];
			[vImage release];
		}
		[NSTimer scheduledTimerWithTimeInterval:0.0 target:self selector:@selector(videoTimerFired:) userInfo:NULL repeats:NO];
	}
	else
	{
		[progress stopAnimation:self];
	}
}

- (void)captureFace:(NSRect)face inImage:(NSImage *)image
{
	unsigned int captureNumFaces = 20;
	NSString *directory = @"~/Sites/Faces"; // preference
	NSString *filename = [NSString stringWithFormat:@"CapturedFace%d.jpg", nextFace];
	NSString *dest = [[directory stringByAppendingPathComponent:filename] stringByStandardizingPath];
	
	NSBitmapImageRep *imageRep = NULL;
	
	image = [[image copy] autorelease];
	NSSize size = [image size]; 
	[image lockFocus];
	[[NSColor blueColor] setFill];
	[[NSColor blueColor] setStroke];
	face.origin.y = size.height - face.origin.y - face.size.height;
	NSFrameRect(face);
	imageRep = [[[NSBitmapImageRep alloc] initWithFocusedViewRect:
		NSMakeRect(0,0,size.width,size.height)] autorelease];
	[image unlockFocus];
	
	if( imageRep )
	{
		// save as JPEG
		NSDictionary *properties =
        [NSDictionary dictionaryWithObjectsAndKeys:
			[NSNumber numberWithFloat:0.85],
			NSImageCompressionFactor, NULL];    
		
		NSData *bitmapData = [imageRep representationUsingType:NSJPEGFileType properties:properties];
		
		if( bitmapData )
		{
			if( [bitmapData writeToFile:dest atomically:YES] )
			{
				++nextFace;
				if( nextFace > captureNumFaces )
					nextFace = 1;
			}
		}
	}
}

- (void)displayAboutPanel:(id)sender
{
	NSBundle *certBundle = [NSBundle bundleWithIdentifier:@"edu.ucsd.mplab.CERTFramework"];
	NSString *mptVersion = [certBundle objectForInfoDictionaryKey:@"MPTVersion"];
	NSString *certVersion = [certBundle objectForInfoDictionaryKey:@"CERTVersion"];

//	int rev = getCertRevision();
////	char *revDate = getCertRevisionDate();
//	const char *mptRev = getMPTRevisionString();
	NSDictionary *options = [NSDictionary dictionaryWithObjectsAndKeys:[NSString stringWithFormat:@"build %@::%@", mptVersion, certVersion], @"Version", nil]; // , @"1.0", @"ApplicationVersion", nil
	[NSApp orderFrontStandardAboutPanelWithOptions:options];
//	[NSApp orderFrontStandardAboutPanel:sender];
}
@end
