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

#include "DeckLinkDevice.h"
#include "ControllerImp.h"

#include <QMessageBox>
#include <QObject>

#include <DeckLinkAPI.h>

static const int kMegabit = 1000000;

ControllerImp::ControllerImp()
{
	m_currentBitrate = 20 * kMegabit;
	m_selectedDevice = -1;
	m_selectedMode   = 0;
}

ControllerImp::~ControllerImp()
{

}

void ControllerImp::init(QObject* delegate)
{
	m_uiDelegate = delegate;

	m_deckLinkDiscovery = new DeckLinkDeviceDiscovery(this);
	m_deckLinkDiscovery->Enable();

	bool connected = QObject::connect(this, SIGNAL(recordingStarted(QString, uint32_t)), m_uiDelegate, SLOT(recordingStarted(QString, uint32_t)));
	Q_ASSERT(connected);
	connected = QObject::connect(this, SIGNAL(recordingFinished()), m_uiDelegate, SLOT(recordingFinished()));
	Q_ASSERT(connected);
	connected = QObject::connect(this, SIGNAL(displayErrorMessage(QString,QString)), this, SLOT(onDisplayErrorMessage(QString, QString)));
	Q_ASSERT(connected);
	connected = QObject::connect(this, SIGNAL(restartCapture(uint32_t)), this, SLOT(restartCaptureRequested(uint32_t)));
	Q_ASSERT(connected);
}

int ControllerImp::changeTargetRate(int rate)
{
	m_currentBitrate = rate;
	return kNoError;
}

int ControllerImp::startStopCapture()
{
	return startStopCapture(m_selectedMode);
}

int ControllerImp::startStopCapture(uint32_t selectedMode)
{
	if (m_selectedDevice < 0)
		return kInvalidOpError;

	DeckLinkDevice* device = m_deviceList[m_selectedDevice];
	if (device->isCapturing())
		stopCapture();
	else
		startCapture(selectedMode);

	return kNoError;
}

int ControllerImp::startCapture(uint32_t mode)
{
	m_selectedMode = mode;
	DeckLinkDevice* device = m_deviceList[m_selectedDevice];
	if (device->isCapturing())
		return kInvalidOpError;

	if (!device->startCapture(m_selectedMode, m_currentBitrate))
	{
		m_selectedMode = device->defaultMode();
		return kUnknownError;
	}

	emit recordingStarted(device->getDisplayModeName(m_selectedMode), device->getDisplayModeFrameRate(m_selectedMode));

	return kNoError;
}

int ControllerImp::stopCapture(bool deleteFile)
{
	if (m_selectedDevice < 0)
		return kInvalidOpError;

	DeckLinkDevice* device = m_deviceList[m_selectedDevice];
	if (device->isCapturing())
	{
		device->stopCapture(deleteFile);
		emit recordingFinished();
	}
	return kNoError;
}

void ControllerImp::restartCaptureRequested(uint32_t mode)
{
	DeckLinkDevice* device = m_deviceList[m_selectedDevice];
	if (device->isCapturing())
		stopCapture(true);
	startCapture(mode);
}

void ControllerImp::newDeviceSelected()
{
	m_selectedDevice = 0;
	m_selectedMode = m_deviceList[m_selectedDevice]->defaultMode();
}

void ControllerImp::addDevice(IDeckLink* deckLink)
{
	DeckLinkDevice* device = new DeckLinkDevice(this, deckLink);

	if (!device->init())
	{
		showErrorMessage(QString("Error initialising your device for encoding"), "This application is unable to initialise your device");
		device->Release();
		return;
	}

	m_deviceList.push_back(device);
	if (m_deviceList.size() == 1)
	{
		newDeviceSelected();
	}
}

void ControllerImp::removeDevice(IDeckLink* deckLink)
{
	DeviceList::iterator deviceToRemove;
	for (deviceToRemove = m_deviceList.begin(); deviceToRemove != m_deviceList.end(); ++deviceToRemove)
	{
		if ((*deviceToRemove)->m_deckLink == deckLink)
			break;
	}
	if (deviceToRemove == m_deviceList.end())
		return;

	int deviceIndex = std::distance(m_deviceList.begin(), deviceToRemove);
	if (m_selectedDevice == deviceIndex)
		stopCapture();

	DeckLinkDevice* deviceRemoved = *deviceToRemove;
	m_deviceList.erase(deviceToRemove);

	if (m_deviceList.size() == 0)
		m_selectedDevice = -1;
	else if (m_selectedDevice == deviceIndex)
		newDeviceSelected();

	deviceRemoved->Release();
}

void ControllerImp::showErrorMessage(const QString& title, const QString& message)
{
	emit displayErrorMessage(title, message);
}

bool ControllerImp::shouldRestartCaptureWithNewVideoMode()
{
	return false;
}

void ControllerImp::selectDetectedVideoModeWithIndex(uint32_t index)
{
	emit restartCapture(index);
}

void ControllerImp::onDisplayErrorMessage(const QString& title, const QString& message)
{
	QMessageBox msgBox;
	msgBox.setIcon(QMessageBox::Warning);
	msgBox.setWindowTitle(title);
	msgBox.setText(message);
	msgBox.exec();
}
