//
//  MPT_DataRecorder.m
//  AUCoderDemo
//
//  Created by Andrew Salamon on 11/8/06.
//  Copyright 2006 Machine Perception Laboratory, University of California San Diego. All rights reserved.
//

#import "MPT_DataRecorder.h"

#import "MPT_PluginController.h"
#import "MPT_FaceView.h"
#import "MPT_PluginProtocolResults.h"
#import "MPT_Demo.h"
#import "MPT_DocumentController.h"

@interface MPT_DataRecorder(PrivateMethods)
- (void)recordDataFieldNames;
- (void)recordTabbedDataPoint:(NSDictionary *)pluginResults;
- (void)recordFullDataPoint:(NSDictionary *)pluginResults;
- (void)setDataPathFilename:(NSString *)ignored;
- (void)warnUserAboutFile:(NSString *)path;
- (void)appendResults:(id)resNum withLabel:(NSString *)label toString:(NSMutableString *)pluginString showLabel:(BOOL)showLabel;
@end

@implementation MPT_DataRecorder
- (id)init
{
	if( self = [super init] )
	{
		NSMutableParagraphStyle *parStyle = [[NSParagraphStyle defaultParagraphStyle] mutableCopy];
		[parStyle setTabStops:[NSArray array]];
		[parStyle setDefaultTabInterval:72.0];

		dataViewAttributes = [[NSDictionary dictionaryWithObject:parStyle forKey:NSParagraphStyleAttributeName] retain];
		[parStyle release];

		pluginController = [MPT_PluginController defaultController];
		
		int dec = [[NSUserDefaults standardUserDefaults] integerForKey:@"decimalPlaces"];
		if( dec < 0 ) dec = 0;
		[self setDecimalPlaces:dec];
        
        resultsArray = [[NSMutableArray alloc] init];
        resultsToRemove = [[NSArray alloc] initWithObjects:@"CERT.rimage", @"CERT.patch", @"CERT.image", nil];
	}

	return self;
}

- (void)awakeFromNib
{
	[self clearDataWindow:self];
#ifdef MPT_ASAP
    [self setRecordTabbedData:YES];
    [self toggleRecording:self];
    dataWindow = NULL;
#endif
}

- (void)dealloc
{
	[dataViewAttributes release];
	[dataPath release];
	[dataFileHandle release];
	[doubleFormat release];
    [resultsArray release];
    [resultsToRemove release];
	[super dealloc];
}

- (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];
	}
}

- (BOOL)recordTabbedData
{
    return recordTabbedData;
}

- (void)setRecordTabbedData:(BOOL)value
{
	if( recordTabbedData != value )
	{
		recordTabbedData = value;

		// If we're switching to tab delimited and we're already recording data, then record the field names
		if( value && [self recording] )
			[self recordDataFieldNames];
	}
}

- (BOOL)viewHasData
{
	NSString *data = [dataTextView string];
	if( !data || (0 == [data length]) ) return NO;
	return ![data isEqualToString:@" "];
}
- (void)setViewHasData:(BOOL)_hasData
{
	// Do nothing. This is here so that Bindings will update properly
}


- (NSString *)dataPath
{
    return dataPath;
}

- (void)setDataPath:(NSString *)value
{
    if( dataPath != value )
    {
        [dataPath release];
        dataPath = [value copy];

		[dataFileHandle release];
		dataFileHandle = nil;
		if( dataPath )
		{
			NSFileManager *fileManager = [NSFileManager defaultManager];
			BOOL fileExists = YES;
			BOOL isDir = NO;

			if( ![fileManager fileExistsAtPath:dataPath isDirectory:&isDir] )
				fileExists = [fileManager createFileAtPath:dataPath contents:nil attributes:nil];

			if( !isDir && fileExists )
				dataFileHandle = [[NSFileHandle fileHandleForWritingAtPath:dataPath] retain];

			if( !dataFileHandle )
			{
				// Probably ought to provide an error message
				[dataPath release];
				dataPath = nil;
			}
		}
		else
		{
			dataFileHandle = nil;
		}
		[self setDataPathFilename:[dataPath lastPathComponent]];
	}
}

- (NSString *)dataPathFilename
{
	return [dataPath lastPathComponent];
}
- (void)setDataPathFilename:(NSString *)ignored
{

}

- (unsigned int)decimalPlaces
{
    return decimalPlaces;
}

- (void)setDecimalPlaces:(unsigned int)value
{
	if( decimalPlaces != value || (nil == doubleFormat) )
	{
		decimalPlaces = value;
		
		[doubleFormat release];
		
		if( 0 == decimalPlaces )
			doubleFormat = @"\t%@%lf"; // Use the default
		else
			doubleFormat = [[NSString alloc] initWithFormat:@"\t%%@%%.%dlf", decimalPlaces];
		
	}
}

- (void)startRecording
{
	[self setRecording:YES];
	[recordedData release];
	recordedData = [[NSMutableArray alloc] init];
    [resultsArray removeAllObjects];
	if( [self recordTabbedData] )
		[self recordDataFieldNames];
}
- (void)stopRecording
{
	[self setRecording:NO];

	if( dataFileHandle )
	{
		[dataFileHandle closeFile];
		[dataFileHandle release];
		dataFileHandle = nil;
		[self setDataPath:nil];
	}
	else
	{
		NSString *dataString = [recordedData componentsJoinedByString:@"\n"];

		NSString *data = [dataTextView string];
		[dataTextView replaceCharactersInRange:NSMakeRange([data length],0) withString:[NSString stringWithFormat:@"\n%@", dataString]];
		[self setViewHasData:YES];
	}
	//	[dataTextView setString:dataString];
	//	[dataWindow makeKeyAndOrderFront:self];
	//	[dataWindow orderWindow:NSWindowBelow relativeTo:0];
	//	[[self window] makeKeyAndOrderFront:self];
}

- (void)addData:(NSString *)dataItem
{
	if( dataFileHandle )
	{
		[dataFileHandle writeData:[[dataItem stringByAppendingString:@"\n"] dataUsingEncoding:NSUTF8StringEncoding]];
	}
	else
	{
		[recordedData addObject:dataItem];
	}
}

#ifdef MPT_ASAP
- (void)recordDataFieldNames
{
	NSMutableString *pluginString = [[NSMutableString alloc] initWithString:@"File"];
    [pluginString appendString:@"\tEnjoyment\tJoyQuotient\tCutoff\tJoy/No Joy\tPercentile\tCutoff\tOdds\tCutoff"];
    
	if( ([pluginString length] > 0) && ('\t' == [pluginString characterAtIndex:0]) )
		[pluginString deleteCharactersInRange:NSMakeRange(0,1)];
    
	[self addData:pluginString];
}

- (void)recordDataPoint:(NSDictionary *)pluginResults
{
	if( [self recording] )
	{
		if( [self recordTabbedData] )
			[self recordTabbedDataPoint:pluginResults];
		else
			[self recordFullDataPoint:pluginResults];
        
        NSMutableDictionary *res = [pluginResults mutableCopy];
        [res removeObjectsForKeys:resultsToRemove];
        [resultsArray addObject:res];
	}
}

- (void)recordTabbedDataPoint:(NSDictionary *)pluginResults
{
	// Walk through all plugins and output results if they are enabled
	NSMutableString *pluginString = [NSMutableString string];
	
	NSString *file = [pluginResults objectForKey:@"CERT.path"];
	if( [file length] > 0 )
		[pluginString appendString:file];
    
	id<MPT_PluginProtocol> plugin = [pluginController pluginForPluginID:@"com.mpt4u.plugins.Enjoyment"];
	id<MPT_PluginProtocol> percentPlugin = [pluginController pluginForPluginID:@"com.mpt4u.plugins.JoyPercentile"];
	id<MPT_PluginProtocol> oddsPlugin = [pluginController pluginForPluginID:@"com.mpt4u.plugins.Odds"];

    if( [plugin enabled] && [percentPlugin enabled] && [oddsPlugin enabled] )
    {
        id resNum = [pluginResults objectForKey:[plugin pluginID]];
        
		NSNumber *enj = [resNum objectForKey:@"Enjoyment"];
		NSNumber *quot = [resNum objectForKey:@"JoyQuotient"];
		NSNumber *cut = [resNum objectForKey:@"cutoff"];
		NSString *joyNoJoy = @"No Joy";
		if( quot && cut && ([quot floatValue] > [cut floatValue]) )
			joyNoJoy = @"Joy";
		
		[self appendResults:enj withLabel:@"Enjoyment" toString:pluginString showLabel:NO];
		[self appendResults:quot withLabel:@"JoyQuotient" toString:pluginString showLabel:NO];
		[self appendResults:cut withLabel:@"Cutoff" toString:pluginString showLabel:NO];
		[self appendResults:joyNoJoy withLabel:@"Joy/No Joy" toString:pluginString showLabel:NO];

        resNum = [pluginResults objectForKey:[percentPlugin pluginID]];
        
		quot = [resNum objectForKey:@"Percentile"];
		cut = [resNum objectForKey:@"cutoff"];
		
		[self appendResults:quot withLabel:@"Percentile" toString:pluginString showLabel:NO];
		[self appendResults:cut withLabel:@"Cutoff" toString:pluginString showLabel:NO];

        resNum = [pluginResults objectForKey:[oddsPlugin pluginID]];
        
		quot = [resNum objectForKey:@"Odds"];
		cut = [resNum objectForKey:@"cutoff"];
		
		[self appendResults:quot withLabel:@"Odds" toString:pluginString showLabel:NO];
		[self appendResults:cut withLabel:@"Cutoff" toString:pluginString showLabel:NO];
    }
    else
    {
        NSLog( @"One or more of the Joy plugins are not enabled." );
    }
	
    
	if( ([pluginString length] > 0) && ('\t' == [pluginString characterAtIndex:0]) )
		[pluginString deleteCharactersInRange:NSMakeRange(0,1)];
    
    //	NSString *file = @"";
    //	if( [imageView lastDropPath] )
    //		file = [NSString stringWithFormat:@"\tFile: %@", [imageView lastDropPath]];
    
    //	if( (0 == [recordedData count]) && [self recordTabbedData] )
    //		[self recordDataFieldNames];
    
	//	NSString *res = [NSString stringWithFormat:@"%@\t%@", pluginString, file];
	NSString *res = pluginString;
	[self addData:res];
}
#else
- (void)recordDataFieldNames
{
	NSMutableString *pluginString = [[NSMutableString alloc] initWithString:@"File"];
	NSEnumerator *pluginEnum = [[pluginController sortedPlugins] objectEnumerator];
	id<MPT_PluginProtocol> plugin;
	while( plugin = [pluginEnum nextObject] )
	{
		if( [plugin enabled] )
		{
			if( [[plugin labels] count] > 0 )
			{
				NSString *label;
				NSEnumerator *labelEnum = [[plugin labels] objectEnumerator];

				while( label = [labelEnum nextObject] )
				{
					[pluginString appendFormat:@"\t%@", label];
				}
			}
			else
			{
				[pluginString appendFormat:@"\t%@", [plugin localizedName]];
			}
		}
	}
	if( ([pluginString length] > 0) && ('\t' == [pluginString characterAtIndex:0]) )
		[pluginString deleteCharactersInRange:NSMakeRange(0,1)];

	[self addData:pluginString];
}

- (void)recordDataPoint:(NSDictionary *)pluginResults
{
	if( [self recording] )
	{
		if( [self recordTabbedData] )
			[self recordTabbedDataPoint:pluginResults];
		else
			[self recordFullDataPoint:pluginResults];
	}
}

- (void)recordTabbedDataPoint:(NSDictionary *)pluginResults
{
	// Walk through all plugins and output results if they are enabled
	NSMutableString *pluginString = [NSMutableString string];
	
	NSString *file = [pluginResults objectForKey:@"CERT.path"];
	if( [file length] > 0 )
		[pluginString appendString:file];
		
	NSEnumerator *pluginEnum = [[pluginController sortedPlugins] objectEnumerator];
	id<MPT_PluginProtocol> plugin;
	while( plugin = [pluginEnum nextObject] )
	{
		if( [plugin enabled] )
		{
			id resNum = [pluginResults objectForKey:[plugin pluginID]];

			if( [[plugin labels] count] > 0 )
			{
				NSString *label;
				NSEnumerator *labelEnum = [[plugin labels] objectEnumerator];
				while( label = [labelEnum nextObject] )
				{
					[self appendResults:resNum withLabel:label toString:pluginString showLabel:NO];
				}
			}
			else
			{
				// No labels, so we just use the plugin's localized name
				[self appendResults:resNum withLabel:[plugin localizedName] toString:pluginString showLabel:NO];
			}
		}
	}
	if( ([pluginString length] > 0) && ('\t' == [pluginString characterAtIndex:0]) )
		[pluginString deleteCharactersInRange:NSMakeRange(0,1)];

//	NSString *file = @"";
//	if( [imageView lastDropPath] )
//		file = [NSString stringWithFormat:@"\tFile: %@", [imageView lastDropPath]];

//	if( (0 == [recordedData count]) && [self recordTabbedData] )
//		[self recordDataFieldNames];

	//	NSString *res = [NSString stringWithFormat:@"%@\t%@", pluginString, file];
	NSString *res = pluginString;
	[self addData:res];
}
#endif

- (void)recordFullDataPoint:(NSDictionary *)pluginResults
{
	// Walk through all plugins and output results if they are enabled
	NSMutableString *pluginString = [NSMutableString string];
	NSEnumerator *pluginEnum = [[pluginController sortedPlugins] objectEnumerator];
	id<MPT_PluginProtocol> plugin;
	while( plugin = [pluginEnum nextObject] )
	{
		if( [plugin enabled] )
		{
			id resNum = [pluginResults objectForKey:[plugin pluginID]];

			if( [[plugin labels] count] > 0 )
			{
				NSString *label;
				NSEnumerator *labelEnum = [[plugin labels] objectEnumerator];

				while( label = [labelEnum nextObject] )
				{
					[self appendResults:resNum withLabel:label toString:pluginString showLabel:YES];
				}
			}
			else
			{
				// No labels, so we just use the plugin's localized name
				[self appendResults:resNum withLabel:[plugin localizedName] toString:pluginString showLabel:YES];
			}
		}
	}
	if( ([pluginString length] > 0) && ('\t' == [pluginString characterAtIndex:0]) )
		[pluginString deleteCharactersInRange:NSMakeRange(0,1)];

	NSString *file = [pluginResults objectForKey:@"CERT.path"];
	if( [file length] > 0 )
		file = [NSString stringWithFormat:@"\tFile: %@", file];
	else
		file = @"";

	NSString *res = [NSString stringWithFormat:@"%@%@", pluginString, file];
	[self addData: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:@" "];
	[[dataTextView textStorage] setAttributes:dataViewAttributes range:NSMakeRange(0,1)];
	[self setViewHasData:NO];
}

- (IBAction)saveDataWindow:(id)sender
{
    [self saveDataModalToWindow:dataWindow];
}

- (void)saveDataModalToWindow:(NSWindow *)window
{
	if( ![[MPT_Demo sharedDemo] isEvaluation] )
	{
        MPT_DocumentController *docs = [MPT_DocumentController sharedDocumentController];
		[dataWindow makeKeyAndOrderFront:self];
        [docs saveDataModalToWindow:window withDelegate:self];
        
//		NSSavePanel *panel = [NSSavePanel savePanel];
//		[panel setPrompt:@"Save Data"];
//		[panel setTitle:@"Save Data"];
//        MPT_DocumentController *docs = [MPT_DocumentController sharedDocumentController];
//        [panel setDelegate:docs];
//		[panel setAccessoryView:[docs popupForWritingDocTypesWithRect:NSMakeRect( 16.0, 16.0, 200.0, 32.0 )]];
//        
//		[dataWindow makeKeyAndOrderFront:self];
//		[panel beginSheetForDirectory:nil
//								 file:@"AU Data.txt"
//					   modalForWindow:window
//						modalDelegate:self
//					   didEndSelector:@selector(savePanelDidEnd:returnCode:contextInfo:)
//						  contextInfo:nil];
	}
}

- (void)savePanelDidEnd:(NSSavePanel *)panel returnCode:(int)returnCode  contextInfo:(void  *)contextInfo
{
	if( returnCode == NSOKButton )
	{
        NSString *path = [panel filename];
        if( [path length] > 0 )
        {
            if( [self recording] )
            {
                [self stopRecording];
            }
            
            NSPopUpButton *popup = (NSPopUpButton *)contextInfo;
            if( [popup isKindOfClass:[NSPopUpButton class]] )
            {
                id<MPT_DocHandler> docHandler = [[popup selectedItem] representedObject];
                if( docHandler && [[docHandler class] respondsToSelector:@selector(preferredExtension)]
                   && ![[[docHandler class] preferredExtension] isEqualToString:@"txt"])
                {
                    // have docHandler write out the data
                    if( ![docHandler writeResults:resultsArray toFile:path] )
                    {
                        NSString *dataString = [dataTextView string];
                        [dataString writeToFile:path atomically:NO];
                    }
                    [self clearDataWindow:self];
                    return;
                }
            }
            
            
            NSString *dataString = [dataTextView string];
            [dataString writeToFile:path atomically:NO];
            [self clearDataWindow:self];
        }
	}
}

- (IBAction)chooseDataFile:(id)sender
{
	if( ![[MPT_Demo sharedDemo] isEvaluation] )
	{
		NSSavePanel *panel = [NSSavePanel savePanel];
		[panel setPrompt:@"Choose File"];
		[panel setTitle:@"Choose a file for recording AU data"];
		
		[dataWindow makeKeyAndOrderFront:self];
		[panel beginSheetForDirectory:nil
								 file:@"AU Data.txt"
					   modalForWindow:dataWindow
						modalDelegate:self
					   didEndSelector:@selector(chooseFilePanelDidEnd:returnCode:contextInfo:)
						  contextInfo:nil];
	}
}
- (void)chooseFilePanelDidEnd:(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 )
			{
				[self setDataPath:path];
				if( !dataFileHandle )
					[self performSelector:@selector(warnUserAboutFile:) withObject:path afterDelay:0.01];
			}
		}
	}
	else
	{
		[self setDataPath:nil];
	}
}
- (void)warnUserAboutFile:(NSString *)path
{
	NSString *msg = @"Unable to open file for reading.";
	[[NSAlert alertWithMessageText:msg
					 defaultButton:@"OK"
				   alternateButton:nil
					   otherButton:nil
		 informativeTextWithFormat:@"You may not have the correct file or folder permissions to write to it: %@.", path] runModal];	
}

- (void)appendResults:(id)resNum withLabel:(NSString *)label toString:(NSMutableString *)pluginString showLabel:(BOOL)showLabel
{
	NSString *labelString = @"";
	if( showLabel )
		labelString = [NSString stringWithFormat:@"%@: ", label];
	
	if( [resNum isKindOfClass:[NSDictionary class]] )
	{
		resNum = [(NSDictionary *)resNum objectForKey:label];
	}

	if( !resNum )
	{
		[pluginString appendFormat:@"\t%@NA", labelString];
	}
	else if( [resNum isKindOfClass:[NSNumber class]] )
	{
		[pluginString appendFormat:doubleFormat, labelString, [resNum doubleValue]];
	}
	else if( [resNum isKindOfClass:[NSString class]] )
	{
		[pluginString appendFormat:@"\t%@%@", labelString, resNum];
	}				
	else
	{
		[pluginString appendFormat:@"\t%@ERR", labelString];
	}
}
@end
