//
//  MPT_DO_Client.m
//  AUCoderDemo
//
//  Created by Andrew Salamon on 6/20/08.
//  Copyright 2008 Machine Perception Laboratory, University of California San Diego.. All rights reserved.
//

#import "MPT_DO_Client.h"
#import <DemoPlugin/MPT_RimageAdaptor.h>
#import <DemoPlugin/MPT_PluginProtocol.h>

NSString *MPT_DO_ErrorDomain = @"CERT DO";

@implementation MPT_DO_Client
+ (NSString *)shutdownNotificationName
{
	return @"CERT DO Client Shutdown";
}

- (id)initWithDelegate:(id)_delegate andPC:(id)_pluginController
{
	if( self = [super init] )
	{
		delegate = _delegate;
		pluginController = _pluginController;
		savedTasks = [[NSMutableDictionary alloc] init];
	}
	return self;
}

- (id)init
{
	return [self initWithDelegate:nil andPC:nil];
}

- (void)dealloc
{
	[serverProxy disconnectClient:self];
	[serverProxy release];
	[savedTasks release];
	[[NSNotificationCenter defaultCenter] removeObserver:self];
	[super dealloc];
}


- (id)delegate
{
    return delegate;
}
- (void)setDelegate:(id)value
{
    if( delegate != value )
    {
        delegate = value;
    }
}

- (id)pluginController
{
    return pluginController;
}
- (void)setPluginController:(id)value
{
    if( pluginController != value )
    {
		// We might need to re-call serverProxy setConfig:forClient:]
        pluginController = value;
    }
}

- (void)scheduleInput:(NSDictionary *)inputs
{
	NSNumber *frame = [inputs objectForKey:@"CERT.frame"];
	NSImage *image = [inputs objectForKey:@"CERT.image"];

	[savedTasks setObject:inputs forKey:frame];
	
	// We need to pull out just the bits we need.
	NSMutableDictionary *netInputs = [NSMutableDictionary dictionary];
	[netInputs setObject:frame forKey:@"CERT.frame"];
	if( image )
		[netInputs setObject:image forKey:@"CERT.image"];
	else
	{
		NSAssert( YES, @"No image to send to DO server." );
	}
//	[serverProxy processTask:netInputs];
//	NSLog( @"Sending image for frame %d", [frame intValue] );
	
	NSData *data = [NSKeyedArchiver archivedDataWithRootObject:netInputs];
//	[self performSelectorOnMainThread:@selector(sendDataToServer:) withObject:data waitUntilDone:YES];
	@try
	{
		[serverProxy processTaskData:data];
	}
	@catch( NSException *exception )
	{
		NSLog( @"Exception caught by Distributed Client: %@\n%@\n%@", [exception name], [exception reason], [[exception userInfo] description] );
	}
//	[serverProxy processImage:image asFrame:[frame intValue]];
}

- (void)sendDataToServer:(NSData *)data
{
	[serverProxy processTaskData:data];
}

- (void)cleanup
{
	if( serverProxy )
	{
		[serverProxy disconnectClient:self];
		[serverProxy release];
		serverProxy = nil;
	}
}

- (BOOL)isConnected
{
	return (serverProxy != nil);
}

//- (NSDictionary *)configForPrefs
//{
//	NSUserDefaults *prefs = [NSUserDefaults standardUserDefaults];
//	NSMutableDictionary *config = [NSMutableDictionary dictionary];
//	NSNumber *numPref;
//	
//	numPref = [prefs objectForKey:@"doFeatureAveraging"];
//	if( numPref )
//		[config setObject:numPref forKey:@"doFeatureAveraging"];
//	
//	numPref = [prefs objectForKey:@"windowMinScale"];
//	if( numPref )
//		[config setObject:numPref forKey:@"windowMinScale"];
//
//	numPref = [prefs objectForKey:@"windowMaxScale"];
//	if( numPref )
//		[config setObject:numPref forKey:@"windowMaxScale"];
//
//	numPref = [prefs objectForKey:@"alignmentMethod"];
//	if( numPref )
//		[config setObject:numPref forKey:@"alignmentMethod"];
//
////	NSNumber *yesNum = [NSNumber numberWithBool:YES];
//	NSMutableArray *plugins = [NSMutableArray array];
//	MPT_PluginController *pc = [MPT_PluginController defaultController];
//	NSEnumerator *pluginEnum = [[pc plugins] objectEnumerator];
//	id<MPT_PluginProtocol> plugin;
//	while( plugin = [pluginEnum nextObject] )
//	{
//		if( [plugin enabled] && (MPT_PluginStep_NoStep != [plugin step]) && (MPT_PluginStep_Results != [plugin step]) )
//		{
//			[plugins addObject:[plugin pluginID]];
////			[config setObject:yesNum forKey:[plugin pluginID]];
//		}
//	}
//	[config setObject:plugins forKey:@"CERT_DO_Plugins"];
//	return config;
//}

- (NSError *)connectToServer:(NSString *)name atHost:(NSString *)host port:(unsigned short)_port
{
//	serverProxy = [[NSConnection rootProxyForConnectionWithRegisteredName:name host:host] retain];
	// default port 5860
	if( (nil != host) && (0 == [host length]) )
		host = nil;
	
	NSSocketPort  *port = [[NSSocketPort alloc] initRemoteWithTCPPort:_port host:host];
	NSConnection  *connection = [NSConnection connectionWithReceivePort:nil sendPort:port];
	[connection enableMultipleThreads];
	serverProxy = [[connection rootProxy] retain];
	
	if( serverProxy )
	{
		[[NSNotificationCenter defaultCenter] addObserver:self
												 selector:@selector(connectionDidDie:)
													 name:NSConnectionDidDieNotification
												   object:nil];
		[serverProxy setProtocolForProxy:@protocol(CERTServer)];
		if( ![serverProxy available] )
		{
			return [NSError errorWithDomain:@"CERT DO" code:0 userInfo:nil];
		}
		[serverProxy setConfig:[pluginController configForPrefs] forClient:self];
	}
	else
	{
		return [NSError errorWithDomain:@"CERT DO" code:0 userInfo:nil];
	}
	return nil;
}

- (void)connectionDidDie:(NSNotification *)aNotification
{
	[self cleanup];
}

// Client methods
-(void)serverStatus:(NSError *)status
{
	if( [[status domain] isEqualTo:MPT_DO_ErrorDomain] )
	{
		switch( [status code] )
		{
			case MPT_DO_ServerShutDown: // server shut down
				[self cleanup];
				[[NSNotificationCenter defaultCenter] postNotificationName:[[self class] shutdownNotificationName] object:self];
				break;
			default:
				NSLog( @"Message/Error from DO server: %@", [status localizedDescription] );
		}
	}
}

- (id)deepCopyResults:(id)res
{
	if( [res isKindOfClass:[NSArray class]] )
	{
		NSMutableArray *newArray = [NSMutableArray array];
		NSEnumerator *resEnum = [res objectEnumerator];
		id item;
		while( item = [resEnum nextObject] )
		{
			id cp = [self deepCopyResults:item];
			[newArray addObject:cp];
		}
		return newArray;
	}
	else if( [res isKindOfClass:[NSDictionary class]] )
	{
		NSMutableDictionary *newDict = [NSMutableDictionary dictionary];
		NSEnumerator *resEnum = [[res allKeys] objectEnumerator];
		id key;
		while( key = [resEnum nextObject] )
		{
			id cp = [self deepCopyResults:[res objectForKey:key]];
			[newDict setObject:cp forKey:key];
		}
		return newDict;
	}

	return [[res copy] autorelease];
}

-(void)taskFinished:(NSDictionary *)task
{
	NSNumber *frame = [task objectForKey:@"CERT.frame"];
	// we need to merge this back into the original task dictionary so all the pieces are there.
	NSMutableDictionary *orig = [savedTasks objectForKey:frame];
	if( !orig )
	{
		NSLog( @"Unable to find original task information" ); //Uh oh!
	}
	else
	{
		orig = [[orig mutableCopy] autorelease];
		[savedTasks removeObjectForKey:frame];
//		NSLog( @"Incoming Faces for frame %d: %d", [frame intValue], [task objectForKey:@"CERT.faces"] );
		// Make a deep copy of the incoming results so they are all local (hopefully).
		//	task = [self deepCopyResults:task];
		//	NSLog( @"Incoming Faces Copy: %d", [task objectForKey:@"CERT.faces"] );
		[orig addEntriesFromDictionary:task];
		[delegate performSelectorOnMainThread:@selector(handleUnsortedResults:) withObject:orig waitUntilDone:NO];
	}
}
-(oneway void)taskFinishedData:(bycopy in NSData *)data
{
	NSDictionary *res = [NSKeyedUnarchiver unarchiveObjectWithData:data];
	[self taskFinished:res];
}
// End client methods
@end
