/* -LICENSE-START-
 ** Copyright (c) 2018 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 <string>
#import <AppKit/NSAlert.h>
#import <Foundation/Foundation.h>
#import "AppDelegate.h"
#import "CocoaViewController.h"
#import "DeckLinkCoreMediaReader.h"

@implementation AppDelegate

static NSString* SecondsToHMS(int64_t seconds)
{
	int64_t hours = seconds / 3600;
	seconds -= (hours * 3600);
	int64_t minutes = (seconds / 60);
	seconds -= (minutes * 60);

	return [NSString stringWithFormat:@"%lld:%02lld:%02lld", hours, minutes, seconds];
}

- (void)applicationDidFinishLaunching:(NSNotification *)aNotification
{
	using namespace std::placeholders;

	m_selectedDevice	= kInvalidDevice;

	m_deviceState		= kDeviceIOIdle;

	m_deviceManager		= std::shared_ptr<DeviceManager>(new DeviceManager());
	m_deviceDiscovery	= std::unique_ptr<DeckLinkDeviceDiscovery>(new DeckLinkDeviceDiscovery());
	m_viewController	= std::unique_ptr<CocoaViewController>(std::move(new CocoaViewController(self)));
	m_mediaReader		= std::shared_ptr<DeckLinkMediaReader>(new DeckLinkCoreMediaReader());

	[m_streamTimeSlider setContinuous:YES];
	
	m_screenPreviewCallback = com_ptr<IDeckLinkScreenPreviewCallback>(CreateCocoaScreenPreview((__bridge void*)m_previewView));

	m_deviceManager->setMediaReader(m_mediaReader);

	// Connect device discovery to device manager
	m_deviceDiscovery->onDeviceArrival(std::bind(&DeviceManager::deckLinkArrived, m_deviceManager.get(), _1));
	m_deviceDiscovery->onDeviceRemoval(std::bind(&DeviceManager::deckLinkRemoved, m_deviceManager.get(), _1));

	// Connect device device manager to view controller
	m_viewController->setDeviceManager(m_deviceManager);
	m_deviceManager->setStatusListener(std::bind(&::CocoaViewController::deviceManagerUpdated, m_viewController.get(), _1, _2));

	// Ensure disabled devices stay disabled when popup button is clicked
	[m_deviceList setAutoenablesItems:NO];
	
	m_deviceDiscovery->start();
}

- (IBAction)deviceSelected:(id)sender
{
	m_viewController->selectDevice((intptr_t)[[m_deviceList selectedItem] tag]);
	// If a file is already open, preview it at the current position
	if (!m_filePath.empty())
		m_viewController->previewFile(m_filePath, [m_streamTimeSlider doubleValue]);
}

- (IBAction)openClicked:(id)sender
{
	NSOpenPanel* panel = [NSOpenPanel openPanel];

	[panel beginSheetModalForWindow:m_window completionHandler:^(NSInteger result)
	 {
		 if (result == NSFileHandlingPanelOKButton)
		 {
			 std::string filePath = [[[panel URLs] objectAtIndex:0] fileSystemRepresentation];
			 // Preview new file at the beginning
			 m_viewController->previewFile(filePath, 0.0);
		 }
	 }];
}

- (IBAction)playClicked:(id)sender
{
	if (m_deviceState == kDeviceIOEnabled)
		m_viewController->play();
	else
		m_viewController->stop();
}

- (IBAction)playPositionChanged:(id)sender
{
	m_viewController->setPosition([m_streamTimeSlider doubleValue]);
}

- (void)applicationWillTerminate:(NSNotification *)aNotification
{
	if (m_deviceDiscovery)
		m_deviceDiscovery->stop();
}

- (BOOL)applicationShouldTerminateAfterLastWindowClosed:(NSApplication*)app
{
	return TRUE;
}

- (void)updateDevice:(intptr_t)deviceID enabled:(BOOL)enabled
{
	
	for (NSMenuItem* menuItem in [m_deviceList itemArray])
	{
		if ((intptr_t)[menuItem tag] != deviceID)
			continue;
		
		[menuItem setEnabled:enabled];
		
		// Try to select an available device one is not already selected
		if (![[m_deviceList selectedItem] isEnabled] && enabled && m_selectedDevice != deviceID)
		{
			m_selectedDevice = deviceID;
			m_viewController->selectDevice(m_selectedDevice);
			[m_deviceList selectItem:menuItem];
			// If a file is already open, preview it at the current position
			if (!m_filePath.empty())
				m_viewController->previewFile(m_filePath, [m_streamTimeSlider doubleValue]);
		}
		break;
	}
}

- (void)addDevice:(intptr_t)deviceID displayName:(CFStringRef)displayName enabled:(BOOL)enabled
{
	[[m_deviceList menu] addItemWithTitle:(__bridge_transfer NSString*)displayName action:nil keyEquivalent:@""];
	[[m_deviceList lastItem] setTag:(NSInteger)deviceID];

	[self updateDevice:deviceID enabled:enabled];
	
	if ([m_deviceList numberOfItems] == 1)
	{
		m_selectedDevice = deviceID;
		m_viewController->selectDevice(m_selectedDevice);
		// If a file is already open, preview it at the current position
		if (!m_filePath.empty())
			m_viewController->previewFile(m_filePath, [m_streamTimeSlider doubleValue]);
	}
}

- (void)removeDevice:(intptr_t)deviceID
{
	NSInteger indexToRemove = [m_deviceList indexOfItemWithTag:(NSInteger)deviceID];
	if (indexToRemove < 0)
		return;
	
	bool selectedDeviceRemoved = (intptr_t)[[m_deviceList selectedItem] tag] == deviceID;
	
	[m_deviceList removeItemAtIndex:indexToRemove];
	
	if (selectedDeviceRemoved)
	{
		// Selected device was removed, select the first available device
		for (uint32_t i = 0; i < [m_deviceList numberOfItems]; ++i)
		{
			if (m_viewController->selectDevice((intptr_t)[[m_deviceList itemAtIndex:i] tag]))
			{
				[m_deviceList selectItemAtIndex:i];
				// If a file is already open, preview it at the current position
				if (!m_filePath.empty())
					m_viewController->previewFile(m_filePath, [m_streamTimeSlider doubleValue]);
				break;
			}
		}
	}
	
	if ([m_deviceList numberOfItems] == 0)
	{
		// Last device was removed
		m_selectedDevice = kInvalidDevice;
		m_viewController->selectDevice(m_selectedDevice);
		[self clearDisplayModeName];
	}
}

- (void)clearDisplayModeName
{
	[m_displayModeName setStringValue:@""];
}

- (void)setDisplayModeName:(NSInteger)displayMode displayModeName:(CFStringRef)displayModeName
{
	[m_displayModeName setStringValue:(__bridge_transfer NSString*)displayModeName];
}

- (void)setFilePath:(const std::string&)filePath
{
	m_filePath = filePath;
	[m_playbackFilePath setURL:[NSURL fileURLWithPath:[[NSString alloc] initWithUTF8String:filePath.c_str()]]];
}

- (void)setPlayPosition:(double)position elapsedSeconds:(int64_t)elapsedSeconds totalSeconds:(int64_t)totalSeconds
{
	[m_streamTimeSlider	setDoubleValue:position];
	[m_timeElapsed		setStringValue:SecondsToHMS(elapsedSeconds)];
	[m_timeRemaining	setStringValue:[NSString stringWithFormat:@"-%@", SecondsToHMS(totalSeconds - elapsedSeconds)]];
}

- (void)showError:(CFStringRef)title detail:(CFStringRef)detail
{
	NSAlert *alert = [[NSAlert alloc] init];
	[alert addButtonWithTitle:@"OK"];
	[alert setMessageText:(__bridge_transfer NSString*)title];
	[alert setInformativeText:(__bridge_transfer NSString*)detail];
	[alert setAlertStyle:NSCriticalAlertStyle];
	[alert beginSheetModalForWindow:m_window completionHandler:nil];
}

- (void)enableUI:(BOOL)enabled
{
	[m_deviceList			setEnabled:enabled];
	[m_openButton			setEnabled:enabled];
	[m_playButton			setEnabled:FALSE];
	[m_streamTimeSlider		setEnabled:FALSE];
	[m_playbackFilePath		setHidden:FALSE];
}

- (void)setDeviceState:(DeviceIOState)deviceState
{
	m_deviceState = deviceState;

	BOOL enableUI	= [[m_deviceList itemArray] count] > 0;

	switch (deviceState)
	{
		case kDeviceIOError:
		case kFileIOError:
		case kDeviceIOIdle:
			[self enableUI:enableUI];
			[m_playButton		setTitle:@"Play"];
			break;
		case kDeviceIOEnabled:
			[self enableUI:enableUI];
			[m_streamTimeSlider	setEnabled:TRUE];
			[m_playButton		setTitle:@"Play"];
			[m_playButton		setEnabled:TRUE];
			break;
		case kFileIORunning:
			[self enableUI:FALSE];
			[m_playButton		setTitle:@"Stop"];
			[m_playButton		setEnabled:TRUE];
			break;
		case kDeviceIOUnknown:
		default:
			[self enableUI:FALSE];
			[m_playButton		setTitle:@"Play"];
			break;
	}
}

- (com_ptr<IDeckLinkScreenPreviewCallback>)screenPreviewCallback
{
	return m_screenPreviewCallback;
}

@end
