#import "PhidgetInterfaceKitController.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 gotInputChange(CPhidgetInterfaceKitHandle phid, void *context, int ind, int val) {
	NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
	[(id)context performSelectorOnMainThread:@selector(InputChange:)
								  withObject:[NSArray arrayWithObjects:[NSNumber numberWithInt:ind], [NSNumber numberWithInt:val], nil]
							   waitUntilDone:NO];
	[pool release];
	return 0;
}
int gotSensorChange(CPhidgetInterfaceKitHandle phid, void *context, int ind, int val) {
	NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
	[(id)context performSelectorOnMainThread:@selector(SensorChange:)
								  withObject:[NSArray arrayWithObjects:[NSNumber numberWithInt:ind], [NSNumber numberWithInt:val], nil]
							   waitUntilDone:NO];
	[pool release];
	return 0;
}
int gotOutputChange(CPhidgetInterfaceKitHandle phid, void *context, int ind, int val) {
	NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
	[(id)context performSelectorOnMainThread:@selector(OutputChange:)
								  withObject:[NSArray arrayWithObjects:[NSNumber numberWithInt:ind], [NSNumber numberWithInt:val], nil]
							   waitUntilDone:NO];
	[pool release];
	return 0;
}

@implementation PhidgetInterfaceKitController

- (void)setPicture:(int)version:(CPhidget_DeviceID)devid
{
	NSImage *img = nil;
	
	switch(devid)
	{
		case PHIDID_INTERFACEKIT_0_0_4:
			img = [NSImage imageNamed:@"1014_1"];
			break;
		case PHIDID_INTERFACEKIT_0_0_8:
			img = [NSImage imageNamed:@"1017_0"];
			break;
		case PHIDID_INTERFACEKIT_0_16_16:
			img = [NSImage imageNamed:@"1012_1"];
			break;
		case PHIDID_INTERFACEKIT_2_2_2:
			img = [NSImage imageNamed:@"1011_0"];
			break;
		case PHIDID_INTERFACEKIT_8_8_8:
			if(version >= 800 && version < 824)
				img = [NSImage imageNamed:@"1013_0"];
				//could also be 1019 but there's no way to tell
			else if(version >= 824 && version < 900)
				img = [NSImage imageNamed:@"1018_1"];
			else if(version >= 900 && version < 1000)
				img = [NSImage imageNamed:@"1018_2"];
			break;
		case PHIDID_INTERFACEKIT_8_8_8_w_LCD:
			if(version >= 100 && version < 200)
				img = [NSImage imageNamed:@"1200_1"];
			else if(version >= 200 && version < 300)
				img = [NSImage imageNamed:@"1203_2"];
			break;
		case PHIDID_LINEAR_TOUCH:
			img = [NSImage imageNamed:@"1015_0"];
			break;
		case PHIDID_ROTARY_TOUCH:
			img = [NSImage imageNamed:@"1016_0"];
			break;
		case PHIDID_INTERFACEKIT_0_8_8_w_LCD:
		case PHIDID_INTERFACEKIT_4_8_8:
		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, i;
	int ratio, sensitivity;
	int ret;
	const char *name;
	CPhidget_DeviceID devid;
	
	CPhidget_getSerialNumber((CPhidgetHandle)ifkit, &serial);
	CPhidget_getDeviceVersion((CPhidgetHandle)ifkit, &version);
	CPhidget_getDeviceName((CPhidgetHandle)ifkit, &name);
	CPhidget_getDeviceID((CPhidgetHandle)ifkit, &devid);
	CPhidgetInterfaceKit_getSensorCount(ifkit, &numSensors);
	CPhidgetInterfaceKit_getInputCount(ifkit, &numInputs);
	CPhidgetInterfaceKit_getOutputCount(ifkit, &numOutputs);
	
	[connectedField setStringValue:[NSString stringWithUTF8String:name]];
	[serialField setIntValue:serial];
	[versionField setIntValue:version];
	[numOutputsField setIntValue:numOutputs];
	[numInputsField setIntValue:numInputs];
	[numSensorsField setIntValue:numSensors];
		
	if(numInputs)
	{
		[inputs renewRows:numInputs columns:1];
		[inputLabels renewRows:numInputs columns:1];
		for(i=0;i<numInputs;i++)
		{
			[[inputs cellWithTag:i] setEnabled:FALSE];
			[[inputs cellWithTag:i] setState:FALSE];
		}
		NSRect frame = [inputsBox frame];
		frame.origin.y = frame.origin.y + frame.size.height - (35+19*numInputs);
		frame.size.height = 35+19*numInputs;
		NSSize size = [inputs frame].size;
		size.height = 19*numInputs;
		[inputs setFrameSize:size];
		size = [inputLabels frame].size;
		size.height = 19*numInputs;
		[inputLabels setFrameSize:size];
		[inputsBox setFrame:frame];
		[inputsBox setHidden:FALSE];
	}
	
	if(numOutputs)
	{
		[outputs renewRows:numOutputs columns:1];
		[outputLabels renewRows:numOutputs columns:1];
		for(i=0;i<numOutputs;i++)
		{
			[[outputs cellWithTag:i] setEnabled:TRUE];
			[[outputs cellWithTag:i] setState:FALSE];
		}
		NSRect frame = [outputsBox frame];
		frame.origin.y = frame.origin.y + frame.size.height - (35+19*numOutputs);
		frame.size.height = 35+19*numOutputs;
		NSSize size = [outputs frame].size;
		size.height = 19*numOutputs;
		[outputs setFrameSize:size];
		size = [outputLabels frame].size;
		size.height = 19*numOutputs;
		[outputLabels setFrameSize:size];
		[outputsBox setFrame:frame];
		[outputsBox setHidden:FALSE];
	}
	
	if(numSensors)
	{
		[sensorSliders renewRows:numSensors columns:1];
		[sensorFields renewRows:numSensors columns:1];
		[sensorLabels renewRows:numSensors columns:1];
		for(i=0;i<numSensors;i++)
		{
			[[sensorSliders cellWithTag:i] setIntValue:0];
			[[sensorFields cellWithTag:i] setStringValue:@""];
			[[sensorSliders cellWithTag:i] setEnabled:TRUE];
		}
		
		if(!(ret = CPhidgetInterfaceKit_getRatiometric(ifkit, &ratio)))
		{
			if(ratio==0 || ratio==1)
			{
				[ratiometric setState:ratio];
			}
		}
		if(ret != EPHIDGET_UNSUPPORTED)
			[ratiometric setEnabled:TRUE];
		
		if(!(ret = CPhidgetInterfaceKit_getSensorChangeTrigger(ifkit, 0, &sensitivity)))
		{
			[sensitivityField setIntValue:sensitivity];
			[sensitivitySlider setIntValue:sensitivity];
		}
		if(ret != EPHIDGET_UNSUPPORTED)
			[sensitivitySlider setEnabled:TRUE];
		
		NSRect frame = [sensorsBox frame];
		frame.origin.y = frame.origin.y + frame.size.height - (123+19*numSensors);
		frame.size.height = 123+19*numSensors;
		NSSize size = [sensorSliders frame].size;
		size.height = 19*numSensors;
		[sensorSliders setFrameSize:size];
		size = [sensorLabels frame].size;
		size.height = 20*numSensors;
		[sensorLabels setFrameSize:size];
		size = [sensorFields frame].size;
		size.height = 20*numSensors;
		[sensorFields setFrameSize:size];
		[sensorsBox setFrame:frame];
		
		[sensorsBox setHidden:FALSE];
	}
	
	NSRect frame = [mainWindow frame];
	int heightChange = 0;
	if(numInputs == 16)
		heightChange = frame.size.height - 546;
	else if(numSensors == 8)
		heightChange = frame.size.height - 482;
	else if(numOutputs == 8)
		heightChange = frame.size.height - 394;
	else if(numOutputs == 4)
		heightChange = frame.size.height - 318;
	else if(numSensors == 1)
		heightChange = frame.size.height - 349;
	else if(numSensors == 2)
		heightChange = frame.size.height - 377;
		
	frame = [mainWindow frame];
	frame.origin.y += heightChange;
	frame.size.height -= heightChange;
	[mainWindow setMinSize:frame.size];
	[mainWindow setFrame:frame display:YES animate:NO];
		
	[self setPicture:version:devid];
}

- (void)phidgetRemoved:(id)nothing
{
	[connectedField setStringValue:@"Nothing"];
	[serialField setStringValue:@""];
	[versionField setStringValue:@""];
	[numInputsField setStringValue:@""];
	[numOutputsField setStringValue:@""];
	[numSensorsField setStringValue:@""];
	
	[inputsBox setHidden:TRUE];
	[outputsBox setHidden:TRUE];
	[sensorsBox setHidden:TRUE];
	
	[ratiometric setEnabled:FALSE];
	[sensitivityField setStringValue:@""];
	[sensitivitySlider setIntValue:0];
	[sensitivitySlider setEnabled:FALSE];
	
	[pictureBox setImage:nil];
	
	NSRect frame = [mainWindow frame];
	int heightChange = frame.size.height - 203;
	frame.origin.y += heightChange;
	frame.size.height -= heightChange;
	[mainWindow setMinSize:frame.size];
	[mainWindow setFrame:frame display:YES animate:NO];
}

- (void)OutputChange:(NSArray *)outputChangeData
{
	[[outputs cellWithTag:[[outputChangeData objectAtIndex:0] intValue]] 
				setState:[[outputChangeData objectAtIndex:1] intValue]];
}

- (void)InputChange:(NSArray *)inputChangeData
{
	[[inputs cellWithTag:[[inputChangeData objectAtIndex:0] intValue]] 
				setState:[[inputChangeData objectAtIndex:1] intValue]];
}

- (void)SensorChange:(NSArray *)sensorChangeData
{
	[[sensorFields cellWithTag:[[sensorChangeData objectAtIndex:0] intValue]] 
				   setIntValue:[[sensorChangeData objectAtIndex:1] intValue]];
	[[sensorSliders cellWithTag:[[sensorChangeData objectAtIndex:0] intValue]] 
					setIntValue:[[sensorChangeData objectAtIndex:1] intValue]];
}

- (IBAction)output:(id)sender
{
	CPhidgetInterfaceKit_setOutputState(ifkit, [sender selectedTag], [[sender cellWithTag:[sender selectedTag]] state]);
}

- (IBAction)ratiometric:(id)sender
{
	CPhidgetInterfaceKit_setRatiometric(ifkit, [sender state]);
}

- (IBAction)sensitivity:(id)sender
{
	int i;
	for(i=0;i<numSensors;i++)
		CPhidgetInterfaceKit_setSensorChangeTrigger(ifkit, i, [sender intValue]);
	[sensitivityField setIntValue:[sender intValue]];
}

- (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)ifkit, serial, NULL, [[passwordField stringValue] UTF8String]);
	else
		CPhidget_open((CPhidgetHandle)ifkit, serial);
}

/*
* This gets run when the GUI gets displayed
*/
- (void)awakeFromNib
{
	[(id)mainWindow setDelegate:self];
    
    CPhidget_enableLogging(PHIDGET_LOG_INFO, NULL);
	
	CPhidgetInterfaceKit_create(&ifkit);
	
	CPhidget_set_OnAttach_Handler((CPhidgetHandle)ifkit, gotAttach, self);
	CPhidget_set_OnDetach_Handler((CPhidgetHandle)ifkit, gotDetach, self);
	CPhidget_set_OnError_Handler((CPhidgetHandle)ifkit, gotError, self);
	CPhidgetInterfaceKit_set_OnInputChange_Handler(ifkit, gotInputChange, self);
	CPhidgetInterfaceKit_set_OnSensorChange_Handler(ifkit, gotSensorChange, self);
	CPhidgetInterfaceKit_set_OnOutputChange_Handler(ifkit, gotOutputChange, self);
	
	[self openCmdLine];
}

- (void)windowWillClose:(NSNotification *)aNotification {
	
	//Because this event is called twice
	if(ifkit)
	{
		//Stop all events before closing
		CPhidget_set_OnAttach_Handler((CPhidgetHandle)ifkit, NULL, NULL);
		CPhidget_set_OnDetach_Handler((CPhidgetHandle)ifkit, NULL, NULL);
		CPhidget_set_OnError_Handler((CPhidgetHandle)ifkit, NULL, NULL);
		CPhidgetInterfaceKit_set_OnInputChange_Handler(ifkit, NULL, NULL);
		CPhidgetInterfaceKit_set_OnSensorChange_Handler(ifkit, NULL, NULL);
		CPhidgetInterfaceKit_set_OnOutputChange_Handler(ifkit, NULL, NULL);
		
		CPhidget_close((CPhidgetHandle)ifkit);
		CPhidget_delete((CPhidgetHandle)ifkit);
		ifkit = 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)ifkit);
			[NSApp runModalForWindow:passwordPanel];
			break;
		case EEPHIDGET_BADVERSION:
			CPhidget_close((CPhidgetHandle)ifkit);
			NSRunAlertPanel(@"Version mismatch", [NSString stringWithFormat:@"%@\nApplication will now close.", errorString], nil, nil, nil);
			[NSApp terminate:self];
			break;
		case EEPHIDGET_PACKETLOST:
			break; //ignore this error
		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
