/* -LICENSE-START-
 ** Copyright (c) 2011 Blackmagic Design
 **
 ** Permission is hereby granted, free of charge, to any person or organization
 ** obtaining a copy of the software and accompanying documentation covered by
 ** this license (the "Software") to use, reproduce, display, distribute,
 ** execute, and transmit the Software, and to prepare derivative works of the
 ** Software, and to permit third-parties to whom the Software is furnished to
 ** do so, all subject to the following:
 ** 
 ** The copyright notices in the Software and this entire statement, including
 ** the above license grant, this restriction and the following disclaimer,
 ** must be included in all copies of the Software, in whole or in part, and
 ** all derivative works of the Software, unless such copies or derivative
 ** works are solely in the form of machine-executable object code generated by
 ** a source language processor.
 ** 
 ** THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 ** IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 ** FITNESS FOR A PARTICULAR PURPOSE, TITLE AND NON-INFRINGEMENT. IN NO EVENT
 ** SHALL THE COPYRIGHT HOLDERS OR ANYONE DISTRIBUTING THE SOFTWARE BE LIABLE
 ** FOR ANY DAMAGES OR OTHER LIABILITY, WHETHER IN CONTRACT, TORT OR OTHERWISE,
 ** ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
 ** DEALINGS IN THE SOFTWARE.
 ** -LICENSE-END-
 */

#import "CapturePreview.h"
#import "DeckLinkController.h"

using namespace std;


@implementation TimecodeStruct
@synthesize timecode;
@synthesize userBits;

- (void)dealloc;
{
	[timecode release];
	[userBits release];
	
	[super dealloc];
}

-(id) copyWithZone: (NSZone *) zone
{
	TimecodeStruct *timecodeCopy = [[[self class] allocWithZone: zone] init];
	
	timecodeCopy.timecode = [NSString stringWithString:self.timecode];
	timecodeCopy.userBits = [NSString stringWithString:self.userBits];

	return timecodeCopy;
}

@end

@implementation AncillaryDataStruct

@synthesize vitcF1;
@synthesize vitcF2;
@synthesize rp188vitc1;
@synthesize rp188vitc2;
@synthesize rp188ltc;

- (void)dealloc
{
	[vitcF1 dealloc];
	[vitcF2 dealloc];
	[rp188vitc1 dealloc];
	[rp188vitc2 dealloc];
	[rp188ltc dealloc];
	
	[super dealloc];
}
@end

@implementation CapturePreviewAppDelegate

@synthesize window;

- (id)init
{
	self = [super init];
	if (self)
	{
		ancillaryDataValues = [[NSMutableArray arrayWithObjects:@"", @"", @"", @"", @"", @"", @"", @"", @"", @"", nil] retain];
		ancillaryDataTypes = [[NSMutableArray arrayWithObjects:
							@"VITC Timecode field 1", 
							@"VITC User bits field 1", 
							@"VITC Timecode field 2", 
							@"VITC User bits field 2", 
							@"RP188 VITC1 Timecode", 
							@"RP188 VITC1 User bits",
							@"RP188 LTC Timecode", 
							@"RP188 LTC User bits",
							@"RP188 VITC2 Timecode", 
							@"RP188 VITC2 User bits",
							nil] retain];
	}
	return self;
}

- (void)applicationDidFinishLaunching:(NSNotification *)aNotification {
	//
	// Setup UI
	
	// Empty popup menus
	[deviceListPopup removeAllItems];
	[modeListPopup removeAllItems];
	[ancillaryDataTable reloadData];
	
	// Disable the interface
	[startStopButton setEnabled:NO];
	[self enableInterface:NO];
	
	//
	// Create and initialise DeckLink device discovery and preview objects
	screenPreviewCallback = CreateCocoaScreenPreview(previewView);
	deckLinkDiscovery = new DeckLinkDeviceDiscovery(self);
	if ((screenPreviewCallback != NULL) && (deckLinkDiscovery != NULL))
	{
		deckLinkDiscovery->Enable();
	}
	else
	{
		[self showErrorMessage:@"This application requires the Desktop Video drivers installed." title:@"Please install the Blackmagic Desktop Video drivers to use the features of this application."];
	}
}

- (void)addDevice:(IDeckLink*)deckLink
{
	// Create new DeckLinkDevice object to wrap around new IDeckLink instance
	DeckLinkDevice* device = new DeckLinkDevice(self, deckLink);
	
	// Initialise new DeckLinkDevice object
	if (! device->init())
	{
		[self showErrorMessage:@"Error initialising the new device" title:@"This application is unable to initialise the new device"];
		device->Release();
		return;
	}

	[[deviceListPopup menu] addItemWithTitle:(NSString*)device->getDeviceName() action:nil keyEquivalent:@""];
	[[deviceListPopup lastItem] setTag:(NSInteger)device];
	
	if ([deviceListPopup numberOfItems] == 1)
	{
		// We have added our first item, enable the interface
		[deviceListPopup selectItemAtIndex:0];
		[self newDeviceSelected:nil];
		
		[startStopButton setEnabled:YES];
		[self enableInterface:YES];
	}
	
}

- (void)removeDevice:(IDeckLink*)deckLink
{
	DeckLinkDevice* deviceToRemove = NULL;
	DeckLinkDevice* removalCandidate = NULL;
	NSInteger index = 0;
	
	// Find the DeckLinkDevice that wraps the IDeckLink being removed
	for (NSMenuItem* item in [deviceListPopup itemArray])
	{
		removalCandidate = (DeckLinkDevice*)[item tag];

		if (removalCandidate->deckLink == deckLink)
		{
			deviceToRemove = removalCandidate;
			break;
		}
		++index;
	}
	
	if (deviceToRemove == NULL)
		return;
	
	// If capture is ongoing, stop it
	if (deviceToRemove->isCapturing())
		deviceToRemove->stopCapture();
	
	[deviceListPopup removeItemAtIndex:index];
	
	[startStopButton setTitle:@"Start Capture"];
	
	if ([deviceListPopup numberOfItems] == 0)
	{
		// We have removed the last item, disable the interface
		[startStopButton setEnabled:NO];
		[self enableInterface:NO];
		selectedDevice = NULL;
	}
	else if (selectedDevice == deviceToRemove)
	{
		// Select the first device in the list and enable the interface
		[deviceListPopup selectItemAtIndex:0];
		[self newDeviceSelected:nil];
		
		[startStopButton setEnabled:YES];
	}
	
	// Release DeckLinkDevice instance
	deviceToRemove->Release();
}


- (void)showErrorMessage:(NSString*)message title:(NSString*)title
{
	NSRunAlertPanel(title, message, @"OK", nil, nil);
}


- (void)refreshVideoModeList
{
	NSMutableArray*		modeNames;
	int					modeIndex = 0;
	
	// Clear the menu
	[modeListPopup removeAllItems];
	
	// Get the mode names
	modeNames = selectedDevice->getDisplayModeNames();
	
	// Add them to the menu
	while (modeIndex < [modeNames count])
		[modeListPopup addItemWithTitle:[modeNames objectAtIndex:modeIndex++]];
}


- (IBAction)newDeviceSelected:(id)sender
{
	// Get the DeckLinkDevice object for the selected menu item.
	selectedDevice = (DeckLinkDevice*)[[deviceListPopup selectedItem] tag];
	
	// Update the video mode popup menu
	[self refreshVideoModeList];
	
	// Enable the interface
	[self enableInterface:YES];
}


- (IBAction)toggleStart:(id)sender
{
	if (selectedDevice == NULL)
		return;
	
	if (selectedDevice->isCapturing())
		[self stopCapture];
	else
		[self startCapture];
}


- (void)startCapture
{
	if (selectedDevice && selectedDevice->startCapture([modeListPopup indexOfSelectedItem], screenPreviewCallback))
	{
		// Update UI
		[startStopButton setTitle:@"Stop"];
		[self enableInterface: NO];
	}
}


- (void)stopCapture
{
	if (selectedDevice)
		selectedDevice->stopCapture();
	
	// Update UI
	[startStopButton setTitle:@"Start Capture"];
	[self enableInterface:YES];
	[noValidSource setHidden:YES];
	
}


- (void)enableInterface:(BOOL)enabled
{
	[deviceListPopup setEnabled:enabled];
	[modeListPopup setEnabled:enabled];
	[noValidSource setHidden:YES];
	
	if (enabled == TRUE)
	{
		if (selectedDevice && selectedDevice->deviceSupportsFormatDetection())
		{
			[applyDetectedVideoMode setEnabled:TRUE];
			[applyDetectedVideoMode setState:NSOnState];
		}
		else
		{
			[applyDetectedVideoMode setEnabled:FALSE];
			[applyDetectedVideoMode setState:NSOffState];
		}
	}
	else
		[applyDetectedVideoMode setEnabled:FALSE];
}


- (BOOL)shouldRestartCaptureWithNewVideoMode
{
	return ([applyDetectedVideoMode state] == NSOnState) ? YES : NO;
}


- (void)updateInputSourceState:(BOOL)state
{
	// Check if the state has changed
	if ([noValidSource isHidden] != state)
	{
		[noValidSource setHidden:state];
	}
}


- (void)selectDetectedVideoModeWithIndex:(UInt32)newVideoModeIndex
{
	[modeListPopup selectItemAtIndex:newVideoModeIndex];
}

- (void)setAncillaryData:(AncillaryDataStruct *)latestAncillaryDataValues
{
	// VITC
	[ancillaryDataValues replaceObjectAtIndex:0 withObject:latestAncillaryDataValues.vitcF1.timecode];
	[ancillaryDataValues replaceObjectAtIndex:1 withObject:latestAncillaryDataValues.vitcF1.userBits];
	[ancillaryDataValues replaceObjectAtIndex:2 withObject:latestAncillaryDataValues.vitcF2.timecode];
	[ancillaryDataValues replaceObjectAtIndex:3 withObject:latestAncillaryDataValues.vitcF2.userBits];
	
	// RP188
	[ancillaryDataValues replaceObjectAtIndex:4 withObject:latestAncillaryDataValues.rp188vitc1.timecode];
	[ancillaryDataValues replaceObjectAtIndex:5 withObject:latestAncillaryDataValues.rp188vitc1.userBits];
	[ancillaryDataValues replaceObjectAtIndex:6 withObject:latestAncillaryDataValues.rp188ltc.timecode];
	[ancillaryDataValues replaceObjectAtIndex:7 withObject:latestAncillaryDataValues.rp188ltc.userBits];
	[ancillaryDataValues replaceObjectAtIndex:8 withObject:latestAncillaryDataValues.rp188vitc2.timecode];
	[ancillaryDataValues replaceObjectAtIndex:9 withObject:latestAncillaryDataValues.rp188vitc2.userBits];
}

- (void)reloadAncillaryTable;
{
	[ancillaryDataTable reloadData];
}

- (id)tableView:(NSTableView *)aTableView objectValueForTableColumn:(NSTableColumn *)aTableColumn row:(NSInteger)rowIndex
{
	if (([aTableColumn identifier] != nil) && [[aTableColumn identifier] isEqualToString:@"Type"])
	{
		if (rowIndex >= [ancillaryDataTypes count])
			return @"unknown row";
		
		// return ancillary data labels
		return [ancillaryDataTypes objectAtIndex:rowIndex];
	}
	
	if (([aTableColumn identifier] != nil) && [[aTableColumn identifier] isEqualToString:@"Value"])
	{
		if (rowIndex >= [ancillaryDataValues count])
			return @"unknown row";
		
		// return ancillary data values
		return [ancillaryDataValues objectAtIndex:rowIndex];
	}
	
	return @"unknown column";
}

- (NSInteger)numberOfRowsInTableView:(NSTableView *)aTableView
{
	return [ancillaryDataValues count];
}

- (void)applicationWillTerminate:(NSNotification *)notification
{
	// Stop the capture
	[self stopCapture];

	// Disable DeckLink device discovery
	deckLinkDiscovery->Disable();

	// Release all DeckLinkDevice instances
	while([deviceListPopup numberOfItems] > 0)
	{
		DeckLinkDevice* device = (DeckLinkDevice*)[[deviceListPopup itemAtIndex:0] tag];
		device->Release();
		[deviceListPopup removeItemAtIndex:0];
	}
	
	// Release DeckLink discovery & screen preview instances
	if (deckLinkDiscovery != NULL)
	{
		deckLinkDiscovery->Release();
		deckLinkDiscovery = NULL;
	}
	
	if (screenPreviewCallback)
	{
		screenPreviewCallback->Release();
		screenPreviewCallback = NULL;
	}

	[ancillaryDataValues release];
	[ancillaryDataTypes release];
}

@end

