//
//  MPT_Rubios.m
//  MPT_Rubios
//
//  Created by Andrew Salamon on 12/18/06.
//  Copyright 2007 Machine Perception Laboratory, University of California San Diego. All rights reserved.
//

#import "MPT_Rubios.h"

#include <sys/socket.h>
#include <netinet/in.h>
#include <netdb.h>

#import <sys/time.h>

#import "MPT_StatusView.h"

@interface MPT_Rubios(PrivateMethods)
- (void)resetTimer;
- (void)pingTimerFired:(NSTimer*)theTimer;
@end

@implementation MPT_Rubios
- (id)initWithBundle:(NSBundle *)_bundle
{
	if( self = [super initWithBundle:_bundle] )
	{
		okToSend = NO;
		messageBuffer = [[NSMutableString alloc] init];
		
		if( ![NSBundle loadNibNamed:@"RubiosPrefPane" owner:self] )
			NSLog( @"Unable to load pref pane for MPT_Rubios plugin." );
		
		if( ![NSBundle loadNibNamed:@"StatusView" owner:self] )
			NSLog( @"Unable to load Status View for MPT_Rubios plugin." );

		[[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(applicationWillTerminate:) 
													 name:NSApplicationWillTerminateNotification
												   object:nil];

		[self setStatus:NSLocalizedStringFromTableInBundle(@"Not Connected", nil, [NSBundle bundleForClass:[self class]], @"")];

		shouldBeConnected = NO;
		[self restoreFromPrefs];
		if( shouldBeConnected )
		{
			[self connect:self];
			shouldBeConnected = NO;
		}
	}
	return self;
}

- (void)awakeFromNib
{
	[self initViews];
}

- (void)dealloc
{
	okToSend = NO;
	[messageBuffer release];
	[self disconnect:self];
	[super dealloc];
}

// It's not OK to copy this plugin, so return nil.
- (id)copyWithZone:(NSZone *)zone
{
	return nil;
}

- (void)applicationWillTerminate:(NSNotification *)notification
{
	[self updatePrefs];
	[self sendMessageToRUBIOS:@"RUBIOS Quit"];
	[self disconnect:self];
}

- (void)initViews
{
	NSString *label = [labels objectAtIndex:0];
	
	if( label )
	{
		MPT_AUView *view = [self viewForLabel:label];
		if( !view )
		{
			view = statusView;
			[view setAUString:label];
			[self setView:view forLabel:label];
			[view release];
		}
	}	
}

- (NSString *)step
{
	return MPT_PluginStep_Results;
}

- (NSString *)localizedName
{
	return NSLocalizedStringFromTableInBundle(@"Rubios", nil, [NSBundle bundleForClass:[self class]], @"");
}

- (NSString *)internalName
{
	return @"rubios";
}

- (NSView *)prefPane
{
	return prefView;
}

- (void)setEnabled:(BOOL)value
{
	[super setEnabled:value];
	if( !value )
	{
		[self disconnect:nil]; // don't use 'self' as the sender because this is the same as the user clicking the 'Disconnect' button.
	}
}

- (NSString *)labelForPlugin:(NSString *)pluginID usingController:(id<MPT_PluginControllerProtocol>)controller
{
	return [controller localizedNameForPluginID:pluginID];
}

- (void)generateLabelOrder:(NSDictionary *)results usingController:(id<MPT_PluginControllerProtocol>)controller
{
	// we don't want the order changing mid-stream
	if( [pluginIDOrder count] > 0 )
		return;
	
	NSArray *faces = [results objectForKey:@"CERT.faces"];
	if( 0 == [faces count] )
		return;
	
	NSMutableDictionary *tmpLabels = [NSMutableDictionary dictionary];
	pluginIDOrder = [[results allKeys] copy];
	 
	NSEnumerator *pluginEnum = [pluginIDOrder objectEnumerator];
	NSString *pluginID;
	while( pluginID = [pluginEnum nextObject] )
	{
		id value =  [results objectForKey:pluginID];
		if( [value isKindOfClass:[NSNumber class]]
			|| [value isKindOfClass:[NSString class]] )
		{
			id plugin = [self labelForPlugin:pluginID usingController:controller];
			if( plugin )
				[tmpLabels setObject:plugin forKey:pluginID];
		}
		else if( [value isKindOfClass:[NSDictionary class]] )
		{
			[tmpLabels setObject:[value allKeys] forKey:pluginID];
		}
		else
		{
			// error
		}
	}
	pluginLabels = [tmpLabels copy];
}

- (NSString *)stripLabels:(NSString *)label
{
	NSMutableString *tmp = [NSMutableString stringWithString:label];
	if( [tmp hasPrefix:@"(AU "] )
	{
		NSRange range = [tmp rangeOfString:@")"];
		range.length = [tmp length] - range.location;
		[tmp deleteCharactersInRange:range];
		[tmp deleteCharactersInRange:NSMakeRange(0,1)];
	}
	[tmp replaceOccurrencesOfString:@"Right Eye" withString:@"REye" options:NSLiteralSearch range:NSMakeRange(0,[tmp length])];
	[tmp replaceOccurrencesOfString:@"Left Eye" withString:@"LEye" options:NSLiteralSearch range:NSMakeRange(0,[tmp length])];
	[tmp replaceOccurrencesOfString:@"Smile Detector" withString:@"Smile" options:NSLiteralSearch range:NSMakeRange(0,[tmp length])];
	[tmp replaceOccurrencesOfString:@" " withString:@"" options:NSLiteralSearch range:NSMakeRange(0,[tmp length])];
	[tmp replaceOccurrencesOfString:@":" withString:@"_" options:NSLiteralSearch range:NSMakeRange(0,[tmp length])];
	return tmp;
}

- (NSMutableDictionary *)processResults:(NSDictionary *)results usingController:(id<MPT_PluginControllerProtocol>)controller
{
	if( !okToSend )
		return nil;
	
	if( [self includeLabels] )
	{
		if( 0 == [results count] )
			return nil;
		
		NSArray *faces = [results objectForKey:@"CERT.faces"];
		if( 0 == [faces count] )
			return nil;
	}
	
	if( [self connected] )
	{
		if( ![pluginIDOrder count] )
			[self generateLabelOrder:results usingController:controller];
		
		// if we couldn't build the plugin order, then just return.
		if( ![pluginIDOrder count] )
			return nil;
		
		/*
		 foreach pluginID in array
		 get value from label dictinary
		 if string, then
		   get value from results for this pluginID
		   send label (if flag is set) and value
		 if array, then
		   get results dict for this plugin
		   foreach label in new array get value from dict for this plugin
		     send label (if flag is set) and value
		 */
		
		NSMutableString *res = [NSMutableString stringWithString:@"CERT"];
		
		long long milliTime = [self currentTimeMillis];
		[res appendFormat:@" %lld", milliTime];
			
		NSEnumerator *pluginEnum = [pluginIDOrder objectEnumerator];
		NSString *pluginID;
		while( pluginID = [pluginEnum nextObject] )
		{
			id labelValue = [pluginLabels objectForKey:pluginID];
			
			id plugin = [controller pluginForPluginID:pluginID];
			if( [[plugin step] isEqualToString:MPT_PluginStep_Features] )
			{
				id oneRes = [results objectForKey:pluginID];
				NSPoint loc = NSPointFromString( oneRes );
				if( [self includeLabels] )
				{
					labelValue = [self stripLabels:labelValue];
					[res appendFormat:@" %@X %0.2f", labelValue, loc.x];
					[res appendFormat:@" %@Y %0.2f", labelValue, loc.y];
				}
				else
				{
					[res appendFormat:@" %0.2f %0.2f", loc.x, loc.y];
				}
			}
			else if( [pluginID isEqualToString:@"edu.ucsd.mplab.plugins.FaceFinderStub"] )
			{
				// Face finder results
//				@"{x:%.0lf, y:%.0lf, width:%.0lf, height:%.0lf, scale:%.0lf}"
				NSString *faceRes = [results objectForKey:pluginID];
				if( !faceRes || (0 == [faceRes length]) )
					continue;
				NSScanner *scanner = [NSScanner scannerWithString:faceRes];
				float x, y, width, height;
				if( ![scanner scanString:@"{x:" intoString:nil] )
					continue;
				if( ![scanner scanFloat:&x] )
					continue;
				if( ![scanner scanString:@", y:" intoString:nil] )
					continue;
				if( ![scanner scanFloat:&y] )
					continue;
				if( ![scanner scanString:@", width:" intoString:nil] )
					continue;
				if( ![scanner scanFloat:&width] )
					continue;
				if( ![scanner scanString:@", height:" intoString:nil] )
					continue;
				if( ![scanner scanFloat:&height] )
					continue;
				if( [self includeLabels] )
				{
					labelValue = [self stripLabels:labelValue];
					[res appendFormat:@" %@X %0.2f", labelValue, x];
					[res appendFormat:@" %@Y %0.2f", labelValue, y];
					[res appendFormat:@" %@Width %0.2f", labelValue, width];
					[res appendFormat:@" %@Height %0.2f", labelValue, height];
				}
				else
				{
					[res appendFormat:@" %0.2f %0.2f %0.2f %0.2f", x, y, width, height];
				}
			}
			else if( [labelValue isKindOfClass:[NSString class]] )
			{
				id oneRes = [results objectForKey:pluginID];
				labelValue = [self stripLabels:labelValue];
				if( [self includeLabels] )
				{
					if( oneRes )
					{
						if( [oneRes isKindOfClass:[NSNumber class]] )
						{
							float rf = [oneRes floatValue];
							[res appendFormat:@" %@ %0.2f", labelValue, rf];
						}
						else
						{
							[res appendFormat:@" '%@' = %@;", labelValue, oneRes];
						}
					}
				}
				else
				{
					if( oneRes && [oneRes isKindOfClass:[NSNumber class]] )
					{
						float rf = [oneRes floatValue];
						[res appendFormat:@" %0.2f", rf];
					}
					else
					{
						[res appendFormat:@" %@", oneRes];
					}
				}
			}
			else if( [labelValue isKindOfClass:[NSArray class]] )
			{
				NSDictionary *auResults = [results objectForKey:pluginID];
				NSEnumerator *labelEnum = [labelValue objectEnumerator];
				NSString *auLabel;
				while( auLabel = [labelEnum nextObject] )
				{
					id oneRes = [auResults objectForKey:auLabel];
					if( [self includeLabels] )
					{
						auLabel = [self stripLabels:auLabel];
						if( oneRes )
						{
							if( [oneRes isKindOfClass:[NSNumber class]] )
							{
								float rf = [oneRes floatValue];
								[res appendFormat:@" %@ %0.2f", auLabel, rf];
							}
							else
							{
								[res appendFormat:@" %@ %@", auLabel, oneRes];
							}
						}
					}
					else
					{
						if( oneRes && [oneRes isKindOfClass:[NSNumber class]] )
						{
							float rf = [oneRes floatValue];
							[res appendFormat:@" %0.2f", rf];
						}
						else
						{
							[res appendFormat:@" %@", oneRes];
						}
					}
				}
			}
			else
			{
				if( labelValue )
					NSLog( @"Unknown label class: %@\n%@", [labelValue class], labelValue );
			}
		}
//		NSMutableString *res = [NSMutableString stringWithString:[results descriptionInStringsFileFormat]];
//		[res replaceOccurrencesOfString:@"\n" withString:@" " options:NSLiteralSearch  range:NSMakeRange(0,[res length])];
//		[res replaceCharactersInRange:NSMakeRange(0,0) withString:@"CERT "];
		[self sendMessageToRUBIOS:res];
		
		return nil;
	}
	
	return nil;
}

- (BOOL)connected
{
	return (socketHandle != nil);
}
- (void)setConnected:(BOOL)value
{
	// do nothing
}

- (NSString *)server
{
    return server;
}
- (void)setServer:(NSString *)value
{
    if( server != value )
    {
        [server release];
        server = [value retain];
		[self updatePrefs];
    }
}


- (unsigned short)port
{
    return port;
}
- (void)setPort:(unsigned short)value
{
	if( port != value )
	{
		port = value;
		[self updatePrefs];
	}
}
- (NSNumber *)portNumber
{
	return [NSNumber numberWithUnsignedShort:[self port]];
}
- (void)setPortNumber:(NSNumber *)value
{
	[self setPort:[value unsignedShortValue]];
}

- (NSString *)nodeName
{
    return nodeName;
}
- (void)setNodeName:(NSString *)value
{
    if( nodeName != value )
    {
        [nodeName release];
        nodeName = [value retain];
		[self updatePrefs];
    }
}

- (NSString *)password
{
    return password;
}
- (void)setPassword:(NSString *)value
{
    if( password != value )
    {
        [password release];
        password = [value retain];
		[self updatePrefs];
    }
}

- (NSString *)status
{
    return status;
}
- (void)setStatus:(NSString *)value
{
    if( status != value )
    {
        [status release];
        status = [value retain];
		
		NSEnumerator *labelEnum = [labels objectEnumerator];
		NSString *label;
		
		while( label = [labelEnum nextObject] )
		{
			MPT_StatusView *view = (MPT_StatusView *)[self viewForLabel:label];
			[view setStatus:value];
		}			
    }
}


- (BOOL)includeLabels
{
    return includeLabels;
}

- (void)setIncludeLabels:(BOOL)value
{
	if( value != includeLabels )
	{
		includeLabels = value;
		[self updatePrefs];
	}
}

- (NSString *)robotName
{
    return robotName;
}
- (void)setRobotName:(NSString *)value
{
    if( robotName != value )
    {
        [robotName release];
        robotName = [value retain];
		[self updatePrefs];
    }
}

- (NSString *)outputNodes
{
	if( [outputNodes length] > 0 )
		return outputNodes;
	return @"AllNodes";
}
- (void)setOutputNodes:(NSString *)value
{
    if( outputNodes != value )
    {
        [outputNodes release];
        outputNodes = [value retain];
		[self updatePrefs];
    }
}
- (void)updatePrefs
{
	NSMutableDictionary *myPrefs = [[[NSUserDefaults standardUserDefaults] objectForKey:[self pluginID]] mutableCopy];
	if( !myPrefs )
		myPrefs = [[NSMutableDictionary alloc] init];
	
	[myPrefs setObject:([self server]?[self server]:@"") forKey:@"server"];
	[myPrefs setObject:[self portNumber] forKey:@"port"];
	[myPrefs setObject:([self nodeName]?[self nodeName]:@"") forKey:@"nodeName"];
	[myPrefs setObject:([self password]?[self password]:@"") forKey:@"password"];
	[myPrefs setObject:([self outputNodes]?[self outputNodes]:@"") forKey:@"outputNodes"];
	[myPrefs setObject:([self robotName]?[self robotName]:@"") forKey:@"robotName"];
	[myPrefs setObject:[NSNumber numberWithBool:[self includeLabels]] forKey:@"labels"];
	[myPrefs setObject:[NSNumber numberWithBool:[self connected]] forKey:@"connected"];

	[[NSUserDefaults standardUserDefaults] setObject:myPrefs forKey:[self pluginID]];
	[myPrefs release];
}

- (void)restoreFromPrefs
{
	NSDictionary *myPrefs = [[NSUserDefaults standardUserDefaults] objectForKey:[self pluginID]];
	if( !myPrefs )
		return;
	
	[self setServer:[myPrefs objectForKey:@"server"]];
	[self setPortNumber:[myPrefs objectForKey:@"port"]];
	[self setNodeName:[myPrefs objectForKey:@"nodeName"]];
	[self setPassword:[myPrefs objectForKey:@"password"]];
	[self setOutputNodes:[myPrefs objectForKey:@"outputNodes"]];
	[self setRobotName:[myPrefs objectForKey:@"robotName"]];
	[self setIncludeLabels:[[myPrefs objectForKey:@"labels"] boolValue]];
	
	shouldBeConnected = [[myPrefs objectForKey:@"connected"] boolValue];
}

- (IBAction)connect:(id)sender
{
	if( serverSocket || socketHandle )
	{
		// error of some kind
		return;
	}
	
	[pluginIDOrder release];
	pluginIDOrder = nil;
	
	okToSend = NO;

	NSResponder *prev = [[prefView window] firstResponder];
	[[prefView window] makeFirstResponder:[prefView window]];
	[[prefView window] makeFirstResponder:prev];

	// verify that we have all the data we need
	if( [[self server] length] <= 0 )
	{
		[self setStatus:@"Missing RUBIOS server address."];
		return;
	}
	if( [self port] <= 0 )
	{
		[self setStatus:@"Missing RUBIOS port."];
		return;
	}
	if( [[self nodeName] length] <= 0 )
	{
		[self setStatus:@"Missing RUBIOS node name."];
		return;
	}
	if( [[self password] length] <= 0 )
	{
		[self setStatus:@"Missing RUBIOS password."];
		return;
	}
//	if( [[self outputNodes] length] <= 0 )
//	{
//		[self setStatus:@"Missing list of output nodes."];
//		return;
//	}
	if( [[self robotName] length] <= 0 )
	{
		[self setStatus:@"Missing robot name."];
		return;
	}
	
	//serverSocket = [[NSSocketPort alloc] initRemoteWithTCPPort:[self port] host:[self server]];
	//write( [serverSocket socket], " ", 1 );
	//int res = send( [serverSocket socket], " ", 1, 0 );
	
	/* Assume servername is a string that contains the address */
	/* e.g:  irc.gate.net */
	/* Assume port is a string that contains the port number */
	/* eg: 6666 */
	struct sockaddr_in serveradd;
	//char port[8];
	int csock;
	//int sockaddr_size;
	struct hostent *hp; 
	
	csock = socket( AF_INET, SOCK_STREAM, 0 );
	if( csock < 0 )
	{
		NSLog( @"Error creating a socket for RUBIOS." );
		[self setStatus:@"Error creating a socket for RUBIOS."];
		return;
	}
	
	hp = gethostbyname( [[self server] UTF8String] );
	if( hp == 0 )
	{
		NSLog( @"Unknown host for RUBIOS." );
		[self setStatus:@"Unknown host for RUBIOS."];
		return;
	} 

	serveradd.sin_family = AF_INET;
	serveradd.sin_port = htons( [self port] ); 
	memcpy( (char *)&serveradd.sin_addr, (char*)hp->h_addr, hp->h_length );
	//serveradd.sin_addr.s_addr = inet_addr("132.239.147.100"); 
//	if( !inet_aton( "132.239.147.100", &(serveradd.sin_addr) ) )
//	{
//		NSLog( @"Unable to convert RUBIOS host address." );
//		[self setStatus:@"Unable to convert RUBIOS host address."];
//		return;
//	}
	memset( (char *)&serveradd.sin_zero, 0, sizeof(serveradd.sin_zero) );

	//sendto(s,msg,sizeof(msg),0,(struct sockaddr*)&addr,sizeof(addr)); 
	if( connect( csock, (struct sockaddr *)&serveradd, sizeof(serveradd) ) < 0 )
	{
		NSLog( @"Error connecting to RUBIOS. errno = %d", errno );
		[self setStatus:[NSString stringWithFormat:@"Error connecting to RUBIOS. errno = %d", errno]];
		return;
	}
		  
	socketHandle = [[NSFileHandle alloc] initWithFileDescriptor:csock closeOnDealloc:YES];
		  
	[[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(dataAvailableNotification:) 
												 name:NSFileHandleReadCompletionNotification
											   object:socketHandle];
	[self setStatus:@"Connecting..."];
	[self setConnected:YES];
	[socketHandle readInBackgroundAndNotify];
}

- (IBAction)disconnect:(id)sender
{
	if( sender != self )
	{
		[self sendMessageToRUBIOS:@"RUBIOS Quit"];
		[self setStatus:@"Disconnected"];
	}
	
	okToSend = NO;

	[[NSNotificationCenter defaultCenter] removeObserver:self name:nil object:socketHandle];
	//[socketHandle closeFile];
	[socketHandle release];
	socketHandle = nil;
	[serverSocket release];
	serverSocket = nil;
	[self setConnected:NO];
	
	if( sender != self )
	{
		[self updatePrefs];
	}
}

/*
 Rubi registration example:
 >telnet mplab.ucsd.edu 1357
 <Trying 132.239.147.100...
 <Connected to mplab.ucsd.edu.
 <Escape character is '^]'.
 <RUBIOSMasterServer
 
 >RUBIOS2 ytttm
 <Correct RUBIOSPassword. Proceed Proceed with Registration.
 
 >NodeName CERT
 
 >OutputNodes AllNodes
 <RUBIOS Registration Successful
 <RUBIOS Registration Successful
 */

- (void)dataAvailableNotification:(NSNotification *)notification
{
	NSData *data = [[notification userInfo] objectForKey:NSFileHandleNotificationDataItem];
	
	if( data && [data length] )
	{
		NSMutableString *dataStr = [[[NSMutableString alloc] initWithData:data encoding:NSASCIIStringEncoding] autorelease];
		[messageBuffer appendString:dataStr];
		NSRange msgRange = [messageBuffer rangeOfString:@"\r\n"];
		while( msgRange.length > 0 )
		{
			NSString *msg = [messageBuffer substringToIndex:msgRange.location];
			[messageBuffer deleteCharactersInRange:NSMakeRange(0,msgRange.location+2)];
			msgRange = [messageBuffer rangeOfString:@"\r\n"];
//		// If the dataStr doesn't end with a crlf, then it's probably a split message, save the rest of it.
//		BOOL extraMsg = [dataStr hasSuffix:@"\r\n"]?NO:YES;
//		NSArray *messages = [dataStr componentsSeparatedByString:@"\r\n"];
//		NSEnumerator *msgEnum = [messages objectEnumerator];
//		NSString *msg;
//		
//		while( (msg = [msgEnum nextObject]) && socketHandle )
//		{
			if( [msg isEqualToString:@"RUBIOSMasterServer"] )
			{
				[self setStatus:@"Connected. Attempting to Register..."];
				NSString *passw = [NSString stringWithFormat:@"%@ %@", [self robotName], [self password]];
				[self sendMessageToRUBIOS:passw];
				okToSend = NO;
			}
			else if( [msg hasPrefix:@"Wrong RUBIOSPass"] )
			{
				[self setStatus:@"Invalid Password"];
				[self disconnect:self];
				okToSend = NO;
			}
			else if( [msg isEqualToString:@"Correct RUBIOSPassword. Proceed Proceed with Registration."] )
			{
				[self setStatus:@"Sending Name..."];
				[self sendMessageToRUBIOS:[NSString stringWithFormat:@"NodeName %@", [self nodeName]]];
				[self sendMessageToRUBIOS:[NSString stringWithFormat:@"OutputNodes %@", [self outputNodes]]];
				okToSend = NO;
			}
			else if( [msg hasPrefix:@"NodeName"] && [msg rangeOfString:@"is already taken"].length > 0 )
			{
				[self disconnect:self];
				[self setStatus:@"NodeName is already taken. Disconnected."];
			}
			else if( [msg hasPrefix:@"RUBIOS Registration Successful"] )
			{
				[self setStatus:@"Connected and Registered"];
				okToSend = YES;
			}
			else if( [msg hasPrefix:@"RUBIOS PING"] )
			{
				//[self setStatus:@"Connected and Registered"];
			}
			else if( [msg isEqualToString:@"-ERR Timeout"] )
			{
				[self disconnect:self];
				[self setStatus:@"Timed out. Disconnected."];
			}
			else if( [msg hasPrefix:@"RUBIOS Registration Failed"] )
			{
				[self disconnect:self];
				[self setStatus:@"Registration failed. Disconnected."];
			}
			else if( [msg hasPrefix:@"CERT"] )
			{
				// Ignore it. It seems to just be an echo from the server.
			}
//			else
//			{
//				if( [msg length] > 0 )
//				{
//					NSString *err = [NSString stringWithFormat:@"Unhandled RUBI message: %@", msg];
//					//[self setStatus:err];
//					NSLog( @"Unhandled RUBI message (%d): %@", [msg length], msg );
//				}
//			}
		}
		
		[[notification object] readInBackgroundAndNotify];
	}
	else
	{
		// The connection has been closed down? Even if it has, don't disconnect from RUBIOS. There might be other reasons why we receive an empty data.
//		[self disconnect:self];
//		[self setStatus:@"Disconnected"];
	}	
}

- (void)sendMessageToRUBIOS:(NSString *)msg
{
	// NSLog( @"Sending message to RUBIOS: %@", msg );
	@try
	{
		[socketHandle writeData:[[msg stringByAppendingString:@"\r\n"] dataUsingEncoding:NSASCIIStringEncoding]];
	}
	@catch( NSException *exception )
	{
		NSLog( @"An exception was thrown when trying to send data to RUBIOS: %@", [exception description] );
	}
	@catch( id exception )
	{
		NSLog( @"An unknown exception was thrown when trying to send data to RUBIOS: %@", [exception description] );
	}
	
	[self resetTimer];
}

- (void)resetTimer
{
//	static NSString *pingMessage = @"RUBIOS PING";
		
	if( pingTimer && [pingTimer isValid] )
		[pingTimer invalidate];
	
//	NSMethodSignature *sig = [self methodSignatureForSelector:@selector(sendMessageToRUBIOS:)];
//	NSInvocation *inv = [NSInvocation invocationWithMethodSignature:sig];
//	[inv setTarget:self];
//	[inv setArgument:&pingMessage atIndex:2];
//	pingTimer = [NSTimer scheduledTimerWithTimeInterval:55.0 invocation:inv repeats:NO]; 
	pingTimer = [NSTimer scheduledTimerWithTimeInterval:55.0 target:self selector:@selector(pingTimerFired:) userInfo:nil repeats:NO];
}

- (void)pingTimerFired:(NSTimer*)theTimer
{
	[self sendMessageToRUBIOS:@"RUBIOS PING"];
}

-(long long)currentTimeMillis
{
	struct timeval tp;
	gettimeofday(&tp, NULL);
	return (long long) tp.tv_sec * 1000 + (long long) (tp.tv_usec / 1000);
}
@end

