/* -LICENSE-START-
** Copyright (c) 2020 Blackmagic Design
**  
** Permission is hereby granted, free of charge, to any person or organization 
** obtaining a copy of the software and accompanying documentation (the 
** "Software") to use, reproduce, display, distribute, sub-license, 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, in 
** accordance with:
** 
** (1) if the Software is obtained from Blackmagic Design, the End User License 
** Agreement for the Software Development Kit (“EULA”) available at 
** https://www.blackmagicdesign.com/EULA/DeckLinkSDK; or
** 
** (2) if the Software is obtained from any third party, such licensing terms 
** as notified by that third party,
** 
** and all subject to the following:
** 
** (3) 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.
** 
** (4) 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.
** 
** A copy of the Software is available free of charge at 
** https://www.blackmagicdesign.com/desktopvideo_sdk under the EULA.
** 
** -LICENSE-END-
*/

#include <QMessageBox>

#include "DeviceStatus.h"

static const QMap<BMDDuplexMode, QString> kDuplexModeMap =
{
	{ bmdDuplexFull,      "Full Duplex" },
	{ bmdDuplexHalf,      "Half Duplex" },
	{ bmdDuplexSimplex,   "Simplex" },
	{ bmdDuplexInactive,  "Inactive" },
};

DeviceStatus::DeviceStatus(QWidget *parent) :
	QDialog(parent),
	m_ui(new Ui::DeviceStatusDialog)
{
	setWindowFlags(Qt::Window
					| Qt::WindowMinimizeButtonHint
					| Qt::WindowMaximizeButtonHint
					| Qt::WindowCloseButtonHint);

	m_ui->setupUi(this);

	m_deviceListModel = new DeckLinkDeviceListModel(this);
	m_ui->deviceComboBox->setModel(m_deviceListModel);
	connect(m_ui->deviceComboBox,  QOverload<int>::of(&QComboBox::currentIndexChanged), this, &DeviceStatus::deviceChanged);

	m_statusDataTableModel = new DeckLinkStatusDataTableModel(this);
	m_ui->statusDataTableView->setModel(m_statusDataTableModel);
	m_ui->statusDataTableView->verticalHeader()->setSectionResizeMode(QHeaderView::ResizeToContents);
	m_ui->statusDataTableView->horizontalHeader()->setSectionResizeMode(QHeaderView::ResizeToContents);
	m_ui->statusDataTableView->horizontalHeader()->setStretchLastSection(true);

	setup();
}

void DeviceStatus::setup()
{
	// Create and initialise DeckLink device discovery and profile objects
	if (GetDeckLinkDiscoveryInstance(m_deckLinkDiscovery) != S_OK)
	{
		QMessageBox::critical(this, "DeckLink Device Discovery error", "Unable to create IDeckLinkDiscovery interface object. Check that the Blackmagic DeckLink drivers are installed.");
		return;
	}

	if (m_deckLinkDiscovery->InstallDeviceNotifications(m_deviceListModel->delegate()) != S_OK)
	{
		QMessageBox::critical(this, "DeckLink Device Notification error", "Unable to install DeckLink device discovery notifications.");
		return;
	}

	m_profileCallback = new DeckLinkProfileCallback();
	connect(m_profileCallback.get(), &DeckLinkProfileCallback::profileActivated, this, &DeviceStatus::profileActivated);
}

void DeviceStatus::closeEvent(QCloseEvent* event)
{
	if (m_selectedDeckLink)
	{
		com_ptr<IDeckLinkProfileManager>	deckLinkProfileManager(IID_IDeckLinkProfileManager, m_selectedDeckLink);
		com_ptr<IDeckLinkNotification>		deckLinkNotification(IID_IDeckLinkNotification, m_selectedDeckLink);

		// Unsubscribe to profile callback
		if (deckLinkProfileManager)
			deckLinkProfileManager->SetCallback(nullptr);

		// Unsubscribe to status changed callback
		if (deckLinkNotification)
			deckLinkNotification->Unsubscribe(bmdStatusChanged, m_statusDataTableModel->delegate());
	}

	// Uninstall device discovery notifications
	m_deckLinkDiscovery->UninstallDeviceNotifications();
}

void DeviceStatus::deviceChanged(int selectedDeviceIndex)
{
	if (m_selectedDeckLink)
	{
		com_ptr<IDeckLinkProfileManager>	deckLinkProfileManager(IID_IDeckLinkProfileManager, m_selectedDeckLink);
		com_ptr<IDeckLinkNotification>		deckLinkNotification(IID_IDeckLinkNotification, m_selectedDeckLink);

		// Unsubscribe to profile callback
		if (deckLinkProfileManager)
			deckLinkProfileManager->SetCallback(nullptr);

		// Unsubscribe to status changed callback
		if (deckLinkNotification)
			deckLinkNotification->Unsubscribe(bmdStatusChanged, m_statusDataTableModel->delegate());
	}

	QVariant selectedDeviceVariant = m_ui->deviceComboBox->itemData(selectedDeviceIndex, DeckLinkDeviceRole);
	m_selectedDeckLink = (IDeckLink*)selectedDeviceVariant.value<void*>();

	if (m_selectedDeckLink)
	{
		com_ptr<IDeckLinkProfileManager>	deckLinkProfileManager(IID_IDeckLinkProfileManager, m_selectedDeckLink);
		com_ptr<IDeckLinkNotification>		deckLinkNotification(IID_IDeckLinkNotification, m_selectedDeckLink);

		// Subscribe the selected device to profile callback
		if (deckLinkProfileManager)
			deckLinkProfileManager->SetCallback(m_profileCallback.get());

		// Subscribe the selected device to status changed callback
		if (deckLinkNotification)
			deckLinkNotification->Subscribe(bmdStatusChanged, m_statusDataTableModel->delegate());
	}

	updateDuplexMode(m_selectedDeckLink);

	// Reset status data table with initial status values
	m_statusDataTableModel->reset(m_selectedDeckLink);
}

void DeviceStatus::profileActivated(com_ptr<IDeckLinkProfile> deckLinkProfile)
{
	com_ptr<IDeckLink>		deckLink;

	if (deckLinkProfile->GetDevice(deckLink.releaseAndGetAddressOf()) == S_OK)
		updateDuplexMode(deckLink);
}

void DeviceStatus::updateDuplexMode(com_ptr<IDeckLink>& deckLink)
{
	com_ptr<IDeckLinkProfileAttributes>		deckLinkAttributes(IID_IDeckLinkProfileAttributes, deckLink);
	int64_t									duplexMode;
	QString									duplexModeString;

	m_ui->duplexLineEdit->clear();

	// Get duplex mode for the device
	if (!deckLinkAttributes)
		return;

	if (deckLinkAttributes->GetInt(BMDDeckLinkDuplex, &duplexMode) != S_OK)
		duplexMode = bmdDuplexInactive;

	auto iter = kDuplexModeMap.find((BMDDuplexMode)duplexMode);
	if (iter != kDuplexModeMap.end())
		m_ui->duplexLineEdit->setText(iter.value());
}
