/*****************************************************************************
 *
 * confaudmp4mencoderdialog.cpp -- Input the MP4 audio codec parameters.
 *
 * Copyright (C) 2010 Starbuck <starbuck@labdv.com>
 *
 *****************************************************************************/

#include "../video/video_functions.h"

#include "confaudmp4mencoderdialog.h"
#include "../encoder/encoder.h"
#include "macros.h"
#include <QtGui>

ConfAudMp4MEncoderDialog::ConfAudMp4MEncoderDialog(encoder_audio_settings_t *settings, 
	audio_t *track, QWidget *parent)
	: QDialog(parent),
	bitrateMin( 16 ),
	bitrateMax( 320 ),
	qualityMin( 1 ),
	qualityMax( 1000 ),
	cutoffMin( 0 ),
	cutoffMax( 48000 ),
	isQualityMode( false )
{
	callerSettings = settings;
	audioSource = track;

	// Local copy allows to cancel changes
	encSettings = new encoder_audio_settings_t( *settings );

	setMinimumSize( 340, 460 );
	setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Expanding);

	verticalLayout = new QVBoxLayout(this);

	// Global buttons
	buttonBox = new QDialogButtonBox(this);

	buttonBox->setOrientation(Qt::Horizontal);
	buttonBox->setStandardButtons(QDialogButtonBox::Cancel|QDialogButtonBox::Ok);

	// Build all tabs
	tabWidget = new QTabWidget(this);
	tabWidget->setMovable(false);

	mainTab = new QWidget();

	tabWidget->addTab(mainTab, QString());

	verticalLayout->addWidget(tabWidget);
	verticalLayout->addWidget(buttonBox);

	tabWidget->setCurrentIndex(0);

	// Main tab 
	mainTabVLayout = new QVBoxLayout(mainTab);
	mainTabVLayout->setSpacing(12);

	// - Bitrate / Quality
	brqGroupBox = new QGroupBox(mainTab);
	brqVLayout = new QVBoxLayout(brqGroupBox);
	brqHLayout = new QHBoxLayout();
	bitrateSlider = new QSlider(brqGroupBox);
	bitrateLineEdit = new QLineEdit(brqGroupBox);
	qualitySlider = new QSlider(brqGroupBox);
	qualityLineEdit = new QLineEdit(brqGroupBox);
	brqButton = new QPushButton(brqGroupBox);

	bitrateSlider->setOrientation(Qt::Horizontal);
	bitrateSlider->setInvertedAppearance(false);
	bitrateSlider->setTickPosition(QSlider::NoTicks);
	bitrateSlider->setSizePolicy(QSizePolicy::Minimum, QSizePolicy::Minimum);
	bitrateSlider->setSingleStep( 2 );
	bitrateSlider->setRange( bitrateMin, bitrateMax );

	bitrateLineEdit->setSizePolicy(QSizePolicy::Maximum, QSizePolicy::Maximum);
	bitrateLineEdit->setMaxLength(4);
	bitrateLineEdit->setMaximumWidth(50);
		
	qualitySlider->setOrientation(Qt::Horizontal);
	qualitySlider->setInvertedAppearance(false);
	qualitySlider->setTickPosition(QSlider::NoTicks);
	qualitySlider->setSizePolicy(QSizePolicy::Minimum, QSizePolicy::Minimum);
	qualitySlider->setSingleStep( 10 );
	qualitySlider->setRange( qualityMin, qualityMax );

	qualityLineEdit->setSizePolicy(QSizePolicy::Maximum, QSizePolicy::Maximum);
	qualityLineEdit->setMaxLength(4);
	qualityLineEdit->setMaximumWidth(50);

	brqHLayout->addWidget(bitrateSlider);
	brqHLayout->addWidget(bitrateLineEdit);
	brqHLayout->addWidget(qualitySlider);
	brqHLayout->addWidget(qualityLineEdit);

	brqVLayout->addLayout(brqHLayout);
	brqVLayout->addWidget(brqButton);

	mainTabVLayout->addWidget(brqGroupBox);

	// - Sampling rate
	srateGroupBox = new QGroupBox(mainTab);
	srateHLayout = new QHBoxLayout(srateGroupBox);
	srateComboBox = new QComboBox(srateGroupBox);
	srateHSpacer = new QSpacerItem(40, 10, QSizePolicy::Expanding, QSizePolicy::Minimum);

	srateHLayout->addWidget(srateComboBox);
	srateHLayout->addItem(srateHSpacer);

	mainTabVLayout->addWidget(srateGroupBox);

	// - Extra settings
	extraGroupBox = new QGroupBox(mainTab);
	extraVLayout = new QVBoxLayout(extraGroupBox);
	cutoffHLayout = new QHBoxLayout();
	cutoffCheckBox = new QCheckBox(extraGroupBox);
	cutoffSlider = new QSlider(extraGroupBox);
	cutoffLineEdit = new QLineEdit(extraGroupBox);
	objectComboBox = new QComboBox(extraGroupBox);
	tnsCheckBox = new QCheckBox(extraGroupBox);
	
	cutoffSlider->setOrientation(Qt::Horizontal);
	cutoffSlider->setInvertedAppearance(false);
	cutoffSlider->setTickPosition(QSlider::NoTicks);
	cutoffSlider->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Minimum);
	cutoffSlider->setSingleStep( 10 );
	cutoffSlider->setRange( cutoffMin, cutoffMax );

	cutoffLineEdit->setSizePolicy(QSizePolicy::Maximum, QSizePolicy::Maximum);
	cutoffLineEdit->setMaxLength(5);
	cutoffLineEdit->setMaximumWidth(40);

	cutoffHLayout->addWidget(cutoffCheckBox);
	cutoffHLayout->addWidget(cutoffSlider);
	cutoffHLayout->addWidget(cutoffLineEdit);

	extraVLayout->addLayout(cutoffHLayout);
	extraVLayout->addWidget(objectComboBox);
	extraVLayout->addWidget(tnsCheckBox);

	mainTabVLayout->addWidget(extraGroupBox);

	// - Preset
	presetGroupBox = new QGroupBox(mainTab);
	presetVLayout = new QVBoxLayout(presetGroupBox);
	presetNoneRadioButton = new QRadioButton(presetGroupBox);
	presetVHQRadioButton = new QRadioButton(presetGroupBox);
	presetHQRadioButton = new QRadioButton(presetGroupBox);
	presetMQRadioButton = new QRadioButton(presetGroupBox);
	presetDRRadioButton = new QRadioButton(presetGroupBox);
	presetLRRadioButton = new QRadioButton(presetGroupBox);
	
	presetVLayout->addWidget(presetNoneRadioButton);
	presetVLayout->addWidget(presetVHQRadioButton);
	presetVLayout->addWidget(presetHQRadioButton);
	presetVLayout->addWidget(presetMQRadioButton);
	presetVLayout->addWidget(presetDRRadioButton);
	presetVLayout->addWidget(presetLRRadioButton);

	mainTabVLayout->addWidget(presetGroupBox);

	// - Spacer
	verticalSpacer = new QSpacerItem(20, 10, QSizePolicy::Minimum, QSizePolicy::Expanding);

	mainTabVLayout->addItem(verticalSpacer);

	// - Default settings
	defaultHLayout = new QHBoxLayout();
	defaultPushButton = new QPushButton(mainTab);
	defaultHSpacer = new QSpacerItem(40, 10, QSizePolicy::Expanding, QSizePolicy::Minimum);

	defaultHLayout->addWidget(defaultPushButton);
	defaultHLayout->addItem(defaultHSpacer);

	mainTabVLayout->addLayout(defaultHLayout);

	// Captions
	setWindowTitle( tr("MPEG-4 AAC audio encoder") );

	tabWidget->setTabText(tabWidget->indexOf(mainTab), tr("Audio settings") );

	srateGroupBox->setTitle( tr(" Sampling rate ") );

	extraGroupBox->setTitle( tr(" Options ") );
	cutoffCheckBox->setText( tr("Cutoff (Hz)") );
	tnsCheckBox->setText( tr("Temporal Noise Shaping") );

	presetGroupBox->setTitle( tr(" MP4 audio presets ") );
	presetNoneRadioButton->setText( tr("None") );
	presetVHQRadioButton->setText( tr("Very High Quality") );
	presetHQRadioButton->setText( tr("High Quality") );
	presetMQRadioButton->setText( tr("Medium Quality") );
	presetDRRadioButton->setText( tr("Default Rate") );
	presetLRRadioButton->setText( tr("Low Rate") );
	
	defaultPushButton->setText( tr("Default settings") );

	// Tool tips and What's this
	setToolTip();

	setAttribute(Qt::WA_AlwaysShowToolTips);

	bitrateValidator = new QIntValidator( this );
	bitrateValidator->setRange( bitrateMin, bitrateMax );
	bitrateLineEdit->setValidator( bitrateValidator );
	
	qualityValidator = new QIntValidator( this );
	qualityValidator->setRange( qualityMin, qualityMax );
	qualityLineEdit->setValidator( qualityValidator );
	
	cutoffValidator = new QIntValidator( this );
	cutoffValidator->setRange(  cutoffMin, cutoffMax );
	cutoffLineEdit->setValidator( cutoffValidator );

	populateSampling();
	populateObject();

	// Signals which don't change settings
	connect(bitrateLineEdit, SIGNAL(textChanged(QString)), 
		this, SLOT(bitrateLineEditChanged(QString)));
	connect(bitrateSlider, SIGNAL(valueChanged(int)), 
		this, SLOT(bitrateSliderChanged(int)));

	connect(qualityLineEdit, SIGNAL(textChanged(QString)), 
		this, SLOT(qualityLineEditChanged(QString)));
	connect(qualitySlider, SIGNAL(valueChanged(int)), 
		this, SLOT(qualitySliderChanged(int)));
	
	connect(cutoffLineEdit, SIGNAL(textChanged(QString)), 
		this, SLOT(cutoffLineEditChanged(QString)));
	connect(cutoffSlider, SIGNAL(valueChanged(int)), 
		this, SLOT(cutoffSliderChanged(int)));

	// Now connect other signals (which controls are up-to-date)
	connect(brqButton, SIGNAL(clicked()), 
		this, SLOT(toggleQualityMode()));

	connect(cutoffCheckBox, SIGNAL(toggled(bool)), 
		this, SLOT(toggleCutOff()));

	connect(srateComboBox, SIGNAL(activated(int)), 
		this, SLOT(selectSampling(int)));
	connect(objectComboBox, SIGNAL(activated(int)), 
		this, SLOT(selectObject(int)));

	connect(presetVHQRadioButton, SIGNAL(clicked()), 
		this, SLOT(selectPreset()));
	connect(presetHQRadioButton, SIGNAL(clicked()), 
		this, SLOT(selectPreset()));
	connect(presetMQRadioButton, SIGNAL(clicked()), 
		this, SLOT(selectPreset()));
	connect(presetDRRadioButton, SIGNAL(clicked()), 
		this, SLOT(selectPreset()));
	connect(presetLRRadioButton, SIGNAL(clicked()), 
		this, SLOT(selectPreset()));
	connect(presetNoneRadioButton, SIGNAL(clicked()), 
		this, SLOT(selectPreset()));

	connect(defaultPushButton, SIGNAL(clicked()), 
		this, SLOT(defaultPushButtonClicked()));

	// Global buttons
	connect(buttonBox, SIGNAL(rejected()), this, SLOT(close()));
	connect(buttonBox, SIGNAL(accepted()), this, SLOT(accept()));

	// Update controls from current settings before connecting signals
	if ( encSettings->mp4.bitrate > 0 )
	{
		toggleQualityMode( true, false );
	}
	else
	{
		toggleQualityMode( true, true );
	}

	updateControls();
}

void ConfAudMp4MEncoderDialog::accept()
{
	*callerSettings = *encSettings;

	QDialog::accept();
}

void ConfAudMp4MEncoderDialog::toggleQualityMode(bool force, bool fvalue)
{
	if ( force )
	{
		isQualityMode = fvalue;
	}
	else
	{
		isQualityMode = !isQualityMode;
	}

	if ( isQualityMode )
	{
		if ( encSettings->mp4.quality == 0 )
		{
			encSettings->mp4.pre_mq();

			qualitySlider->setValue( encSettings->mp4.quality );
			qualityLineEdit->setText( QString::number(encSettings->mp4.quality) );
		}

		encSettings->mp4.bitrate = 0;

		qualitySlider->setVisible(true);
		qualityLineEdit->setVisible(true);

		bitrateSlider->setVisible(false);
		bitrateLineEdit->setVisible(false);

		brqGroupBox->setTitle( tr(" Quality ") );
		brqButton->setText( tr("Quality Mode (click to switch to Bitrate Mode)") );

		brqGroupBox->setToolTip( boxToolTip("quality", tr("100"), QString("<%1-%2>").arg( qualityMin ).arg( qualityMax ),
						tr("Quality mode, the higher the better (mutually exclusive with bitrate). "
" Default: 100, averages at approx. 128 kbps VBR for a normal stereo input file at 16 bit and 44.1 kHz sample rate.")) );
	}
	else
	{
		if ( encSettings->mp4.bitrate == 0 )
		{
			encSettings->mp4.pre_dr();

			bitrateSlider->setValue( encSettings->mp4.bitrate );
			bitrateLineEdit->setText( QString::number(encSettings->mp4.bitrate) );
		}

		encSettings->mp4.quality = 0;

		bitrateSlider->setVisible(true);
		bitrateLineEdit->setVisible(true);

		qualitySlider->setVisible(false);
		qualityLineEdit->setVisible(false);

		brqGroupBox->setTitle( tr(" Bitrate ( kbps ) ") );
		brqButton->setText( tr("Bitrate Mode (click to switch to Quality Mode)") );

		brqGroupBox->setToolTip( boxToolTip("br", tr("128"), QString("<%1-%2>").arg( bitrateMin ).arg( bitrateMax ),
						tr("Average bitrate in kbps (mutually exclusive with quality).")) );
	}

	updateControls();
}

void ConfAudMp4MEncoderDialog::toggleCutOff()
{
	if ( cutoffCheckBox->isChecked() )
	{
		cutoffSlider->setEnabled(true);
		cutoffLineEdit->setEnabled(true);
	}
	else
	{
		cutoffSlider->setEnabled(false);
		cutoffLineEdit->setEnabled(false);

		encSettings->mp4.cutoff = 0;  // "Not Set"
	}
}

void ConfAudMp4MEncoderDialog::updateControls()
{
	int index;

	if ( isQualityMode )
	{
		qualitySlider->setValue( encSettings->mp4.quality );
		qualityLineEdit->setText( QString::number(encSettings->mp4.quality) );
	}
	else
	{
		bitrateSlider->setValue( encSettings->mp4.bitrate );
		bitrateLineEdit->setText( QString::number(encSettings->mp4.bitrate) );
	}

	tnsCheckBox->setChecked( encSettings->mp4.tns );
	cutoffCheckBox->setChecked( encSettings->mp4.cutoff );

	if ( encSettings->mp4.cutoff > 0 )
	{
		cutoffSlider->setValue( encSettings->mp4.cutoff );
		cutoffLineEdit->setText( QString::number(encSettings->mp4.cutoff) );
		
		cutoffSlider->setEnabled(true);
		cutoffLineEdit->setEnabled(true);
	}
	else
	{
		cutoffSlider->setEnabled(false);
		cutoffLineEdit->setEnabled(false);
	}

	index = srateComboBox->findData( encSettings->mp4.srate );

	if ( index == -1 ) // should not happened
	{
		srateComboBox->setCurrentIndex( 0 );
		encSettings->mp4.srate = srateComboBox->itemData( 0 ).toInt();
	}
	else
	{
		srateComboBox->setCurrentIndex( index );
	}

	index = objectComboBox->findData( encSettings->mp4.object );

	if ( index == -1 ) // should not happened
	{
		objectComboBox->setCurrentIndex( 0 );
		encSettings->mp4.object = objectComboBox->itemData( 0 ).toInt();
	}
	else
	{
		objectComboBox->setCurrentIndex( index );
	}

	if ( encSettings->mp4.is_vhq() )
	{
		presetVHQRadioButton->setChecked(true);
	}
	else if ( encSettings->mp4.is_hq() )
	{
		presetHQRadioButton->setChecked(true);
	}
	else if ( encSettings->mp4.is_mq() )
	{
		presetMQRadioButton->setChecked(true);
	}
	else if ( encSettings->mp4.is_dr() )
	{
		presetDRRadioButton->setChecked(true);
	}
	else if ( encSettings->mp4.is_lr() )
	{
		presetLRRadioButton->setChecked(true);
	}
	else
	{
		presetNoneRadioButton->setChecked(true);
	}
}

void ConfAudMp4MEncoderDialog::populateSampling()
{
	srateComboBox->clear();

	QMap<int, QString> sampling;
	sampling.insert( 0, tr("(input rate)") );
	sampling.insert( 11025, tr("11,025 Hz") );
	sampling.insert( 22050, tr("22,050 Hz") );
	sampling.insert( 44100, tr("44,100 Hz") );
	sampling.insert( 48000, tr("48,000 Hz") );
	sampling.insert( 96000, tr("96,000 Hz") );

	QMapIterator<int, QString> i(sampling);
	while ( i.hasNext() )
	{
		i.next();

		srateComboBox->addItem( i.value(), i.key() );
	}

	int index = srateComboBox->findData( encSettings->mp2.srate );

	if ( index == -1 ) // should not happened
	{
		srateComboBox->setCurrentIndex( 0 );
		encSettings->mp2.srate = srateComboBox->itemData( 0 ).toInt();
	}
	else
	{
		srateComboBox->setCurrentIndex( index );
	}
}

void ConfAudMp4MEncoderDialog::populateObject()
{
	objectComboBox->clear();

	QMap<int, QString> object;
	object.insert( 0, tr("(not set)") );
	object.insert( 2, tr("Low Complexity") );
	object.insert( 3, tr("Scalable Sample Rate") );
	object.insert( 1, tr("Main Profile (default)") );
	object.insert( 4, tr("Long Term Predictor") );

	QMapIterator<int, QString> i(object);
	while ( i.hasNext() )
	{
		i.next();

		objectComboBox->addItem( i.value(), i.key() );
	}

	int index = objectComboBox->findData( encSettings->mp4.object );

	if ( index == -1 ) // should not happened
	{
		objectComboBox->setCurrentIndex( 0 );
		encSettings->mp4.object = objectComboBox->itemData( 0 ).toInt();
	}
	else
	{
		objectComboBox->setCurrentIndex( index );
	}
}

void ConfAudMp4MEncoderDialog::bitrateLineEditChanged(const QString &value)
{
	encSettings->mp4.bitrate = value.toInt();

	bitrateSlider->setValue( encSettings->mp4.bitrate );
}

void ConfAudMp4MEncoderDialog::bitrateSliderChanged(const int value)
{
	encSettings->mp4.bitrate = value;

	bitrateLineEdit->setText( QString::number(value) );
}

void ConfAudMp4MEncoderDialog::qualityLineEditChanged(const QString &value)
{
	encSettings->mp4.quality = value.toInt();

	qualitySlider->setValue( encSettings->mp4.quality );
}

void ConfAudMp4MEncoderDialog::qualitySliderChanged(const int value)
{
	encSettings->mp4.quality = value;

	qualityLineEdit->setText( QString::number(value) );
}

void ConfAudMp4MEncoderDialog::cutoffLineEditChanged(const QString &value)
{
	encSettings->mp4.cutoff = value.toInt();

	cutoffSlider->setValue( encSettings->mp4.cutoff );
}

void ConfAudMp4MEncoderDialog::cutoffSliderChanged(const int)
{
	int value = cutoffSlider->value();
	encSettings->mp4.cutoff = value;

	cutoffLineEdit->setText( QString::number(value) );
}

void ConfAudMp4MEncoderDialog::selectSampling(const int index)
{
	// Do nothing if combobox is empty or reset
	if (index < 0)
	{
		return;
	}

	encSettings->mp4.srate = srateComboBox->itemData( index ).toInt();
}

void ConfAudMp4MEncoderDialog::selectObject(const int index)
{
	// Do nothing if combobox is empty or reset
	if (index < 0)
	{
		return;
	}

	encSettings->mp4.object = objectComboBox->itemData( index ).toInt();
}

void ConfAudMp4MEncoderDialog::selectPreset()
{
	// First do custom, the quick one
	if ( presetNoneRadioButton->isChecked() )
	{
		return;
	}

	// Then proceed for each preset
	if ( presetVHQRadioButton->isChecked() )
	{
		toggleQualityMode(true, true);
		encSettings->mp4.pre_vhq();
	}
	else if ( presetHQRadioButton->isChecked() )
	{
		toggleQualityMode(true, true);
		encSettings->mp4.pre_hq();
	}
	else if ( presetMQRadioButton->isChecked() )
	{
		toggleQualityMode(true, true);
		encSettings->mp4.pre_mq();
	}
	else if ( presetDRRadioButton->isChecked() )
	{
		toggleQualityMode(true, false);
		encSettings->mp4.pre_dr();
	}
	else if ( presetLRRadioButton->isChecked() )
	{
		toggleQualityMode(true, false);
		encSettings->mp4.pre_lr();
	}
	else
	{
		// Should not happen, a bug to pop-up and fix
#ifdef WIN32
#pragma message (Reminder "a bug to report in log - " __FUNCTION__)
#endif
		isQualityMode = false;
		encSettings->mp4.clear();
	}

	updateControls();
}

void ConfAudMp4MEncoderDialog::defaultPushButtonClicked()
{
	toggleQualityMode(true, false);
	encSettings->mp4.clear();

	updateControls();
}

void ConfAudMp4MEncoderDialog::setToolTip()
{
	// brqGroupBox -> setToolTip() see updateQualityMode()
	
	cutoffCheckBox->setToolTip( boxToolTip("cutoff", tr("sampling_rate/2"), QString("<%1-%2>").arg( cutoffMin ).arg( cutoffMax ),
						tr("Cutoff frequency of the low pass filter.")) );
	cutoffSlider->setToolTip( cutoffCheckBox->toolTip() );
	cutoffLineEdit->setToolTip( cutoffCheckBox->toolTip() );

	objectComboBox->setToolTip( boxToolTip("object", "Main Profile", "<1-4>",
						tr("Profile (object type complexity).")) );

	tnsCheckBox->setToolTip( boxToolTip("tns", "Not Set", "",
						tr("TNS uses a prediction approach in the frequency domain to shape the quantization noise "
"over time. It applies a filter to the original spectrum and quantizes this filtered signal. Additionally, quantized "
"filter coefficients are transmitted in the bitstream. These are used in the decoder to undo the filtering performed "
"in the encoder, leading to a temporally shaped distribution of quantization noise in the decoded audio signal.")) );
}

QString ConfAudMp4MEncoderDialog::labelToolTip( const QString &defaults, const QString &details )
{
	QString strDefault( tr("Default: ") ); 

	return tr(	"<p><b>%1</b> %2</p>"
				"<p></p>"
				"<p>%3</p>" )
				.arg(Qt::escape(strDefault)).arg(Qt::escape(defaults))
				.arg(Qt::escape(details));
}

QString ConfAudMp4MEncoderDialog::editToolTip( const QString &name, const QString &range )
{
	return tr("<b>%1</b> %2").arg( Qt::escape(name) ).arg( Qt::escape(range) );
}

QString ConfAudMp4MEncoderDialog::boxToolTip( const QString &name, const QString &defaults, const QString &range, const QString &details )
{
	QString strDefault( tr("Default: ") ); 

	return tr(	"<p><b>%1</b> %2</p>"
				"<p></p>"
				"<p><b>%3</b> %4</p>"
				"<p></p>"
				"<p>%5</p>" )
				.arg(Qt::escape(name)).arg(Qt::escape(range))
				.arg(Qt::escape(strDefault)).arg(Qt::escape(defaults))
				.arg(Qt::escape(details));
}
