#import "PhidgetIRController.h"
#import "PhidgetIRCodeInfo.h"

//Event callback functions for C, which in turn call a method on the GUI object in it's thread context
int gotAttach(CPhidgetHandle phid, void *context) {
	[(id)context performSelectorOnMainThread:@selector(phidgetAdded:)
								  withObject:nil
							   waitUntilDone:NO];
	return 0;
}
int gotDetach(CPhidgetHandle phid, void *context) {
	[(id)context performSelectorOnMainThread:@selector(phidgetRemoved:)
								  withObject:nil
							   waitUntilDone:NO];
	return 0;
}

int gotError(CPhidgetHandle phid, void *context, int errcode, const char *error) {
	NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
	[(id)context performSelectorOnMainThread:@selector(ErrorEvent:)
								  withObject:[NSArray arrayWithObjects:[NSNumber numberWithInt:errcode], [NSString stringWithUTF8String:error], nil]
							   waitUntilDone:NO];
	[pool release];
	return 0;
}
int gotCode(CPhidgetIRHandle phid, void *context, unsigned char *data, int dataLength, int bitCount, int repeat)
{
	NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
	char *codeStr = (char *)malloc(dataLength*2+3);
	int i;
	
	sprintf(codeStr, "0x");
	for(i=1;i<=dataLength;i++)
		sprintf(codeStr+i*2, "%02x", data[i-1]);
	
	[(id)context performSelectorOnMainThread:@selector(Code:)
								  withObject:[NSArray arrayWithObjects:[NSString stringWithUTF8String:codeStr], [NSNumber numberWithInt:bitCount], [NSNumber numberWithInt:repeat], nil]
							   waitUntilDone:NO];
	free(codeStr);
	[pool release];
	return 0;
}
int gotLearn(CPhidgetIRHandle phid, void *context, unsigned char *data, int dataLength, CPhidgetIR_CodeInfoHandle codeInfo)
{
	NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
	char *codeStr = (char *)malloc(dataLength*2+3);
	int i;
	
	sprintf(codeStr, "0x");
	for(i=1;i<=dataLength;i++)
		sprintf(codeStr+i*2, "%02x", data[i-1]);
	
	[(id)context performSelectorOnMainThread:@selector(Learn:)
								  withObject:[NSArray arrayWithObjects:[NSString stringWithUTF8String:codeStr], [[PhidgetIRCodeInfo alloc] initWithHandle:codeInfo], nil]
							   waitUntilDone:NO];
	free(codeStr);
	[pool release];
	return 0;
}

@implementation PhidgetIRController

- (void)setPicture:(int)version:(CPhidget_DeviceID)devid
{
	NSImage *img = nil;
	
	switch(devid)
	{
		case PHIDID_IR:
			img = [NSImage imageNamed:@"1055_0"];
			break;
		default:
			break;
	}
	
	//don't set the dock icon to nil
	if(img)
		[NSApp setApplicationIconImage:img];
	
	//ok to set the picture to nil
	[pictureBox setImage:img];
}

- (void)phidgetAdded:(id)nothing
{
	int serial, version;
	const char *name;
	CPhidget_DeviceID devid;
	
	CPhidget_getSerialNumber((CPhidgetHandle)ir, &serial);
	CPhidget_getDeviceVersion((CPhidgetHandle)ir, &version);
	CPhidget_getDeviceName((CPhidgetHandle)ir, &name);
	CPhidget_getDeviceID((CPhidgetHandle)ir, &devid);
	
	[connectedField setStringValue:[NSString stringWithUTF8String:name]];
	[serialField setIntValue:serial];
	[versionField setIntValue:version];
			
	[codeBox setHidden:FALSE];
	[learnBox setHidden:FALSE];
	
	NSRect frame = [mainWindow frame];
	int heightChange = frame.size.height - 489;
	frame.origin.y += heightChange;
	frame.size.height -= heightChange;
	[mainWindow setMinSize:frame.size];
	[mainWindow setFrame:frame display:YES animate:NO];
		
	repeatCounter = 0;
	
	[self setPicture:version:devid];
}

- (void)phidgetRemoved:(id)nothing
{
	[connectedField setStringValue:@"Nothing"];
	[serialField setStringValue:@""];
	[versionField setStringValue:@""];
	
	[pictureBox setImage:nil];
	
	[codeBox setHidden:TRUE];
	[learnBox setHidden:TRUE];
	
	[reTransmitButton setEnabled:FALSE];
	
	NSRect frame = [mainWindow frame];
	int heightChange = frame.size.height - 176;
	frame.origin.y += heightChange;
	frame.size.height -= heightChange;
	[mainWindow setMinSize:frame.size];
	[mainWindow setFrame:frame display:YES animate:NO];
}

- (void)Code:(NSArray *)codeData
{
	[codeField setStringValue:[codeData objectAtIndex:0]];
	[bitCountField setIntegerValue:[[codeData objectAtIndex:1] integerValue]];
	
	if([[codeData objectAtIndex:2] intValue])
		repeatCounter++;
	else
		repeatCounter=0;

	[repeatCountField setIntValue:repeatCounter];
}

- (void)Learn:(NSArray *)learnData
{
	CPhidgetIR_CodeInfoHandle codeInfo = [[learnData objectAtIndex:1] CodeInfo];
	int byteLength = codeInfo->bitCount/8;
	if(codeInfo->bitCount%8)
		byteLength++;
	char toggleStr[128];
	char repeatStr[1024];
	int i;
	
	sprintf(toggleStr, "0x");
	for(i=1;i<=byteLength;i++)
		sprintf(toggleStr+i*2, "%02x", codeInfo->toggle_mask[i-1]);
	
	repeatStr[0] = 0;
	for(i=0;i<26;i++)
	{
		if(codeInfo->repeat[i])
			sprintf(repeatStr+strlen(repeatStr), "%d, ", codeInfo->repeat[i]);
		else
			break;
	}
	if(strlen(repeatStr))
		repeatStr[strlen(repeatStr)-2] = 0;
	
	[learnedCodeField setStringValue:[learnData objectAtIndex:0]];
	
	[learnedBitCountField setIntValue:codeInfo->bitCount];
	switch(codeInfo->encoding)
	{
		case PHIDGET_IR_ENCODING_SPACE:
			[encodingField setStringValue:@"Space"];
			break;
		case PHIDGET_IR_ENCODING_PULSE:
			[encodingField setStringValue:@"Pulse"];
			break;
		case PHIDGET_IR_ENCODING_BIPHASE:
			[encodingField setStringValue:@"BiPhase"];
			break;
		case PHIDGET_IR_ENCODING_RC5:
			[encodingField setStringValue:@"RC5"];
			break;
		case PHIDGET_IR_ENCODING_RC6:
			[encodingField setStringValue:@"RC6"];
			break;
		default:
			[encodingField setStringValue:@"Error"];
			break;
	}
	switch(codeInfo->length)
	{
		case PHIDGET_IR_LENGTH_CONSTANT:
			[lengthField setStringValue:@"Constant"];
			break;
		case PHIDGET_IR_LENGTH_VARIABLE:
			[lengthField setStringValue:@"Variable"];
			break;
		default:
			[lengthField setStringValue:@"Error"];
			break;
	}
	[gapField setIntValue:codeInfo->gap];
	[trailField setIntValue:codeInfo->trail];
	[toggleMaskField setStringValue:[NSString stringWithUTF8String:toggleStr]];
	[minRepeatField setIntValue:codeInfo->min_repeat];
	if(codeInfo->header[0])
		[headerField setStringValue:[NSString stringWithFormat:@"%d, %d",codeInfo->header[0], codeInfo->header[1]]];
	else
		[headerField setStringValue:@"No Header"];
	[zeroField setStringValue:[NSString stringWithFormat:@"%d, %d",codeInfo->zero[0], codeInfo->zero[1]]];
	[oneField setStringValue:[NSString stringWithFormat:@"%d, %d", codeInfo->one[0], codeInfo->one[1]]];
	[repeatField setStringValue:[NSString stringWithUTF8String:repeatStr]];
	
	[reTransmitButton setEnabled:TRUE];
}

- (IBAction)reTransmit:(id)sender
{
	CPhidgetIR_CodeInfo codeInfo;
	unsigned char code[16];
	int codeLength = 16;
	
	CPhidgetIR_getLastLearnedCode(ir, code, &codeLength, &codeInfo);
	CPhidgetIR_Transmit(ir, code, &codeInfo);
}

- (void)openCmdLine
{
	int serial = -1, remote = 0;
	NSArray *args = [[NSProcessInfo processInfo] arguments];
	if([args count] > 1)
	{
		if([[args objectAtIndex:1] isEqualToString:@"remote"])
			remote = 1;
		serial = [[args objectAtIndex:[args count]-1] intValue];
		if(serial == 0) serial = -1;
	}
	
	if(remote)
		CPhidget_openRemote((CPhidgetHandle)ir, serial, NULL, [[passwordField stringValue] UTF8String]);
	else
		CPhidget_open((CPhidgetHandle)ir, serial);
}

/*
* This gets run when the GUI gets displayed
*/
- (void)awakeFromNib
{
	[mainWindow setDelegate:self];
	
	CPhidgetIR_create(&ir);
	
	CPhidget_set_OnAttach_Handler((CPhidgetHandle)ir, gotAttach, self);
	CPhidget_set_OnDetach_Handler((CPhidgetHandle)ir, gotDetach, self);
	CPhidgetIR_set_OnCode_Handler(ir, gotCode, self);
	CPhidgetIR_set_OnLearn_Handler(ir, gotLearn, self);
	
	[self openCmdLine];
}

- (void)windowWillClose:(NSNotification *)aNotification {
	
	//Because this event is called twice
	if(ir)
	{
		//Stop all events before closing
		CPhidget_set_OnAttach_Handler((CPhidgetHandle)ir, NULL, NULL);
		CPhidget_set_OnDetach_Handler((CPhidgetHandle)ir, NULL, NULL);
		CPhidget_set_OnError_Handler((CPhidgetHandle)ir, gotError, self);
		
		CPhidget_close((CPhidgetHandle)ir);
		CPhidget_delete((CPhidgetHandle)ir);
		ir = NULL;
	}
	[NSApp terminate:self];
}

int errorCounter = 0;
- (void)ErrorEvent:(NSArray *)errorEventData
{
	int errorCode = [[errorEventData objectAtIndex:0] intValue];
	NSString *errorString = [errorEventData objectAtIndex:1];
	
	switch(errorCode)
	{
		case EEPHIDGET_BADPASSWORD:
			CPhidget_close((CPhidgetHandle)ir);
			[NSApp runModalForWindow:passwordPanel];
			break;
		case EEPHIDGET_BADVERSION:
			CPhidget_close((CPhidgetHandle)ir);
			NSRunAlertPanel(@"Version mismatch", [NSString stringWithFormat:@"%@\nApplication will now close.", errorString], nil, nil, nil);
			[NSApp terminate:self];
			break;
		default:
			errorCounter++;
			
			NSAttributedString *string = [[NSAttributedString alloc] initWithString:[NSString stringWithFormat:@"%@\n",errorString]];
			
			[[errorEventLog textStorage] beginEditing];
			[[errorEventLog textStorage] appendAttributedString:string];
			[[errorEventLog textStorage] endEditing];
			
			[errorEventLogCounter setIntValue:errorCounter];
			if(![errorEventLogWindow isVisible])
				[errorEventLogWindow setIsVisible:YES];
			break;
	}
}

- (IBAction)clearErrorLog:(id)sender
{
	[[errorEventLog textStorage] setAttributedString:[[NSAttributedString alloc] initWithString:@""]];
	[errorEventLogCounter setIntValue:0];
	errorCounter = 0;
}

- (IBAction)passwordOK:(id)sender
{
	[passwordPanel setIsVisible:NO];
	[NSApp stopModal];
	[self openCmdLine];
	[passwordField setStringValue:@""];
}

- (IBAction)passwordCancel:(id)sender
{
	[passwordPanel setIsVisible:NO];
	[NSApp stopModal];
	[NSApp terminate:self];
}
@end
