/* -LICENSE-START-
 ** Copyright (c) 2009 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-
 */

/* DeckLinkKeyerController.m */

#import "DeckLinkKeyerController.h"


@implementation DeckLinkKeyerController
- (void)awakeFromNib
{
	IDeckLinkIterator*					deckLinkIterator = NULL;
	IDeckLinkAttributes*				deckLinkAttributes = NULL;
	IDeckLinkDisplayModeIterator*		displayModeIterator = NULL;
	IDeckLinkDisplayMode*				deckLinkDisplayMode = NULL;
	bool								success = false;
	bool								supportHDKeying;
	bool								supportExternalKeying;
	bool								supportInternalKeying;
	
	
	// **** Find a DeckLink instance and obtain video output interface ****
	deckLinkIterator = CreateDeckLinkIteratorInstance();
	if (deckLinkIterator == NULL)
	{
		NSRunAlertPanel(@"This application requires the DeckLink drivers installed.", @"Please install the Blackmagic DeckLink drivers to use the features of this application.", @"OK", nil, nil);
		goto bail;
	}
	
	// Connect to the first DeckLink instance
	if (deckLinkIterator->Next(&deckLink) != S_OK)
	{
		NSRunAlertPanel(@"This application requires a DeckLink PCI card.", @"You will not be able to use the features of this application until a DeckLink PCI card is installed.", @"OK", nil, nil);
		goto bail;
	}
	
	// Obtain the audio/video output interface (IDeckLinkOutput)
	if (deckLink->QueryInterface(IID_IDeckLinkOutput, (void**)&deckLinkOutput) != S_OK)
		goto bail;

	// obtain the DeckLink Attribute interface (IDeckLinkAttributes)
	if (deckLink->QueryInterface(IID_IDeckLinkAttributes, (void**)&deckLinkAttributes) != S_OK)
		goto bail;
	
	// Is keying is supported in HD modes ?
	if (deckLinkAttributes->GetFlag(BMDDeckLinkSupportsHDKeying, &supportHDKeying) != S_OK)
		goto bail;
	
	
	// **** Setup the display mode menu ****	
	[outputModeMenu removeAllItems];
	if (deckLinkOutput->GetDisplayModeIterator(&displayModeIterator) != S_OK)
		goto bail;

	while(displayModeIterator->Next(&deckLinkDisplayMode) == S_OK)
	{
		CFStringRef		modeName;
		
		if (deckLinkDisplayMode->GetName(&modeName) == S_OK)
		{
			// Skip HD modes on cards such as DeckLink SDI (only PAL/NTSC are supported for keying)
			if ((deckLinkDisplayMode->GetWidth() > 720) && !supportHDKeying)
				continue;

			
			// Add this item to the video format poup menu
			[outputModeMenu addItemWithTitle:(NSString*)modeName];
			// Save the IDeckLinkDisplayMode in the menu item's tag
			[[outputModeMenu itemAtIndex:[outputModeMenu numberOfItems]-1]  setTag:(NSInteger)deckLinkDisplayMode];
			CFRelease(modeName);
		}
	}
	displayModeIterator->Release();
	
	currentDisplayMode = (IDeckLinkDisplayMode*) [[outputModeMenu selectedItem] tag];
	
	// obtain the keyer interface (IDeckLinkKeyer)
	if (deckLink->QueryInterface(IID_IDeckLinkKeyer, (void**)&deckLinkKeyer) != S_OK)
		goto bail;

	
	// **** Enable/Disable internal/external keying buttons ****
	[self disableControls];
	
	if (deckLinkAttributes->GetFlag(BMDDeckLinkSupportsExternalKeying, &supportExternalKeying) != S_OK)
		goto bail;
	
	if (!supportExternalKeying)
		[externalKeyRadio setEnabled:NO];
	
	if (deckLinkAttributes->GetFlag(BMDDeckLinkSupportsInternalKeying, &supportInternalKeying) != S_OK)
		goto bail;
	
	if (!supportInternalKeying)
		[internalKeyRadio setEnabled:NO];

	
	// **** Restore previous settings ****
	userDefaults = [NSUserDefaults standardUserDefaults];
	
	// Initial key image
	NSString *image;	
	image = [userDefaults stringForKey:@"InitialKeyImage"];
	if ((image == nil) || ([self setMovieFile:image] == NO))
		if ([self setMovieFile:[[NSBundle mainBundle] pathForResource:@"DeckLinkKeyer Default Image" ofType:@"psd"]] == NO)
			goto bail;
	
	// Display mode
	NSInteger index;
	for(index = 0; index < [outputModeMenu numberOfItems]; index++)
	{
		if ([[outputModeMenu itemTitleAtIndex:index] isEqualToString:[userDefaults stringForKey:@"DisplayMode"]])
		{
			[outputModeMenu selectItemAtIndex:index];
			break;
		}
	}
	
	// Alpha
	[alphaText setIntValue:[userDefaults integerForKey:@"AlphaValue"]];
	[alphaSlider setIntValue:[userDefaults integerForKey:@"AlphaValue"]];
	
	// Keying (off, internal, external)
	if ([userDefaults boolForKey:@"KeyingEnabled"] == YES)
	{
		if (([userDefaults boolForKey:@"InternalKey"] == YES) && ([internalKeyRadio isEnabled] == YES))
		{
			[radioMatrix selectCellAtRow:1 column:0];
			[self doKeyInternal: internalKeyRadio];
		}
		else
		{
			[radioMatrix selectCellAtRow:2 column:0];
			[self doKeyExternal: externalKeyRadio];
		}
	}
	
	// Install a notification to be called when the application terminates.
	[[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(applicationWillTerminate:) name:NSApplicationWillTerminateNotification object:nil];
	
	success = true;
	
bail:
	if (deckLinkAttributes != NULL)
		deckLinkAttributes->Release();
		
	if (success == false)
	{
		//disable the interface
		[self disableControls];
		[externalKeyRadio setEnabled:NO];
		[internalKeyRadio setEnabled:NO];
		
		//Release the video modes
		NSInteger index;
		IDeckLinkDisplayMode *mode;
		for(index = 0; index < [outputModeMenu numberOfItems]; index++)
		{
			mode = (IDeckLinkDisplayMode*)[[outputModeMenu selectedItem] tag];
			mode->Release();
		}
		
		// Release keyer interface
		if (deckLinkKeyer !=NULL)
		{
			deckLinkKeyer->Release();
			deckLinkKeyer = NULL;
		}
		
		// Release output interface
		if (deckLinkOutput != NULL)
		{
			deckLinkOutput->Release();
			deckLinkOutput = NULL;
		}
		
		// Release DeckLink resource
		if (deckLink != NULL)
		{
			deckLink->Release();
			deckLink = NULL;
		}
	}
	
	if (deckLinkIterator != NULL)
		deckLinkIterator->Release();
	
}

- (void)disableControls
{
	[modeLabel setTextColor:[NSColor disabledControlTextColor]];
	[outputModeMenu setEnabled:NO];
	[quickKeyLabel setTextColor:[NSColor disabledControlTextColor]];
	[keyTransitionLabel setTextColor:[NSColor disabledControlTextColor]];
	[durationText setEnabled:NO];
	[durationLabel setTextColor:[NSColor disabledControlTextColor]];
	[blendOffButton setEnabled:NO];
	[blendOnButton setEnabled:NO];
	[alphaLabel setTextColor:[NSColor disabledControlTextColor]];
	[alphaSlider setEnabled:NO];
	[alphaText setEnabled:NO];
	[movieView setAcceptDrags:NO];
}

- (void)enableControls
{
	[modeLabel setTextColor:[NSColor controlTextColor]];
	[outputModeMenu setEnabled:YES];
	[quickKeyLabel setTextColor:[NSColor controlTextColor]];
	[keyTransitionLabel setTextColor:[NSColor controlTextColor]];
	[durationText setEnabled:YES];
	[durationLabel setTextColor:[NSColor controlTextColor]];
	[blendOffButton setEnabled:YES];
	[blendOnButton setEnabled:YES];
	[alphaLabel setTextColor:[NSColor controlTextColor]];
	[alphaSlider setEnabled:YES];
	[alphaText setEnabled:YES];
	[movieView setAcceptDrags:YES];
}

- (void*)getFrameBuffer:(uint32_t)width height:(uint32_t)height
{
	void* buffer = NULL;
	
	if (keyFrameBuffer == NULL)
	{
		// If we dont have a DeckLinkVideoFrame yet, create one & fetch the associated buffer
		if (deckLinkOutput->CreateVideoFrame(width, height, width*4*sizeof(uint8_t), bmdFormat8BitARGB, bmdFrameFlagDefault, &keyFrameBuffer) == S_OK)
			keyFrameBuffer->GetBytes(&buffer);
	}
	else
	{
		if ((keyFrameBuffer->GetWidth() != width) || (keyFrameBuffer->GetHeight() != height))
		{
			// If we already have a DeckLinkVideoFrame, but its dimensions are different from the requested ones,
			// release the frame, create a new one with the requested dimensions & fetch the associated buffer
			keyFrameBuffer->Release();
			if (deckLinkOutput->CreateVideoFrame(width, height, width*4*sizeof(uint8_t), bmdFormat8BitARGB, bmdFrameFlagDefault, &keyFrameBuffer) == S_OK)
				keyFrameBuffer->GetBytes(&buffer);
		}
		else
			keyFrameBuffer->GetBytes(&buffer);
	}
	return buffer;
}

- (BOOL)setMovieFile:(NSString*)filename
{
	if ([movieView setMovieFile:filename forMode:(IDeckLinkDisplayMode*)[[outputModeMenu selectedItem] tag]] == NO ) 
		return NO;

	[userDefaults setObject:filename forKey:@"InitialKeyImage"];
	[self outputCurrentFrame];
	
	return YES;
}

- (void) outputCurrentFrame
{
	if (videoOutputOn)
		deckLinkOutput->DisplayVideoFrameSync(keyFrameBuffer);
}

- (void)videoOutputOn
{
	IDeckLinkDisplayMode *mode = (IDeckLinkDisplayMode*)[[outputModeMenu selectedItem] tag];
	if ((!videoOutputOn) || (currentDisplayMode != mode))
	{
		[self videoOutputOff];
		if (deckLinkOutput->EnableVideoOutput(mode->GetDisplayMode(), bmdVideoOutputFlagDefault) == S_OK)
		{
			videoOutputOn = YES;
			currentDisplayMode = mode;
			[self outputCurrentFrame];
	}	
	}
}

- (void)videoOutputOff
{
	if (videoOutputOn)
	{
		deckLinkOutput->DisableVideoOutput();
		videoOutputOn = NO;
	}
}

- (IBAction)performOpen:(id)sender
{
	NSOpenPanel*			openPanel;
	
	openPanel = [NSOpenPanel openPanel];
	[openPanel setDelegate:self];
	if ([openPanel runModalForTypes:nil] == NSOKButton)
	{
		if ([self setMovieFile:[openPanel filename]] == NO)
		{
			NSRunAlertPanel(@"The selected file could not be opened.", @"DeckLink Keyer could not opened the selected file.  It may not be an image format that QuickTime understands.", @"OK", nil, nil);
		}
	}
}

- (IBAction)doKeyOff:(id)sender
{
	deckLinkKeyer->Disable();
	[self videoOutputOff];
	[self disableControls];
	[userDefaults setBool:NO forKey:@"KeyingEnabled"];
	
}

- (IBAction)doKeyInternal:(id)sender
{
	[self enableControls];
	[self videoOutputOn];
	deckLinkKeyer->Enable(false);
	deckLinkKeyer->SetLevel([alphaText intValue]);
	[userDefaults setBool:YES forKey:@"KeyingEnabled"];
	[userDefaults setBool:YES forKey:@"InternalKey"];
}

- (IBAction)doKeyExternal:(id)sender
{
	[self enableControls];
	[self videoOutputOn];
	deckLinkKeyer->Enable(true);
	deckLinkKeyer->SetLevel([alphaText intValue]);
	[userDefaults setBool:YES forKey:@"KeyingEnabled"];
	[userDefaults setBool:NO forKey:@"InternalKey"];
}

- (IBAction)doBlendOn:(id)sender
{
	deckLinkKeyer->RampUp([durationText intValue]);
}

- (IBAction)doBlendOff:(id)sender
{
	deckLinkKeyer->RampDown([durationText intValue]);
}

- (IBAction)outputModeChanged:(id)sender
{
	[self videoOutputOn];
	[userDefaults setObject:[[outputModeMenu selectedItem] title] forKey:@"DisplayMode"];
}

- (IBAction)alphaSliderChanged:(id)sender
{
	[alphaText setIntValue:[sender intValue]];
	deckLinkKeyer->SetLevel([sender intValue]);
	[userDefaults setInteger:[sender intValue] forKey:@"AlphaValue"];
}

- (IBAction)alphaTextChanged:(id)sender
{
	[alphaSlider setIntValue:[sender intValue]];
	deckLinkKeyer->SetLevel([sender intValue]);
	[userDefaults setInteger:[sender intValue] forKey:@"AlphaValue"];
}

- (void)applicationWillTerminate:(NSNotification*)notification
{
	// Release the DeckLinkVideoFrame
	if (keyFrameBuffer != NULL)
	{
		keyFrameBuffer->Release();
		keyFrameBuffer = NULL;
	}
	
	//Release the video modes
	NSInteger index;
	IDeckLinkDisplayMode *mode;
	for(index = 0; index < [outputModeMenu numberOfItems]; index++)
	{
		mode = (IDeckLinkDisplayMode*)[[outputModeMenu selectedItem] tag];
		mode->Release();
	}
	
	// Release keyer interface
	if (deckLinkKeyer !=NULL)
	{
		deckLinkKeyer->Release();
		deckLinkKeyer = NULL;
	}
	
	// Release output interface
	if (deckLinkOutput != NULL)
	{
		deckLinkOutput->Release();
		deckLinkOutput = NULL;
	}
	
	// Release DeckLink resource
	if (deckLink != NULL)
	{
		deckLink->Release();
		deckLink = NULL;
	}
	
}

@end
