/*
	Copyright (C) 2022-2024 Mark Tyler

	This program is free software; you can redistribute it and/or modify
	it under the terms of the GNU General Public License as published by
	the Free Software Foundation; either version 3 of the License, or
	(at your option) any later version.

	This program is distributed in the hope that it will be useful,
	but WITHOUT ANY WARRANTY; without even the implied warranty of
	MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
	GNU General Public License for more details.

	You should have received a copy of the GNU General Public License
	along with this program in the file COPYING.
*/

#include "qt5.h"



template < typename Tparser >
void populate_vars (
	Tparser			& parser,
	QTableWidget	* const	table
	)
{
	auto	const	& vars = parser.variables ();
	int		row = 0;

	for ( auto && it : vars )
	{
		QTableWidgetItem * const twItem = new QTableWidgetItem;

		table->setRowCount ( row + 1 );

		twItem->setText ( it.first.c_str() );
		table->setItem ( row++, 0, twItem );
	}

	// This hack ensures that all columns are right width (not just visible)
	table->setVisible ( false );
	table->resizeColumnsToContents ();
	table->setVisible ( true );

	table->setCurrentCell ( 0, 0 );
}

template < typename Tparser, typename Tnum >
int select_vars (
	Tparser			& parser,
	QTableWidget	* const	table,
	QPlainTextEdit	* const	text_edit,
	int		const	row,
	int		const	old_row,
	int		const	snip_size,
	Tnum		const	*& num,
	int			& num_digits
	)
{
	if ( row == old_row )
	{
		return 1;
	}

	QTableWidgetItem * twItem = table->item ( row, 0 );
	if ( ! twItem )
	{
		return 1;
	}

	std::string const var = twItem->text().toStdString();

	auto	const	& map = parser.variables();
	auto	const	it = map.find ( var );

	if ( it == map.end() )
	{
		text_edit->setPlainText ( "" );
		return 1;
	}

	num = &it->second;
	std::string const digits = num->to_string_snip ((size_t)snip_size);
	num_digits = (int)digits.size();

	text_edit->setPlainText ( digits.c_str() );

	return 0;
}

template < typename Tparser, typename Tnum >
int get_variable_and_filename (
	QWidget		* const	parent,
	Tparser		const	& parser,
	Tnum		const	*& var,
	QTableWidget	* const	table,
	std::string		& filename,
	char	const	* const	message
	)
{
	int const row = table->currentRow();

	if ( row < 0 )
	{
		// No current row
		return 1;
	}

	QTableWidgetItem * twItem = table->item ( row, 0 );
	if ( ! twItem )
	{
		// No row of this index
		return 1;
	}

	std::string const var_name = twItem->text().toStdString();

	auto	const	& map = parser.variables();
	auto	const	it = map.find ( var_name );

	if ( it == map.end() )
	{
		// Variable not found
		return 1;
	}

	var = &it->second;

	filename = choose_output_filename ( parent, message );

	return 0;
}



/// ----------------------------------------------------------------------------



template < typename Tparser >
void MainWindow::TabUIelements<Tparser>::click_show_vars ()
{
	DialogVars dialog ( &mainwindow );

	dialog.start ( &m_parser );
}

template void MainWindow::TabUIelements<mtDW::FloatParser>::click_show_vars ();
template void MainWindow::TabUIelements<mtDW::DoubleParser>::click_show_vars ();
template void MainWindow::TabUIelements<mtDW::IntegerParser>::click_show_vars ();
template void MainWindow::TabUIelements<mtDW::RationalParser>::click_show_vars ();



/// ----------------------------------------------------------------------------



template < typename Tparser >
void var_gui_build (
	DialogVars	* const	dialog,
	MainWindow	* const	mainwindow,
	Tparser	const * const	parser
	)
{
	MemPrefs const & mprefs = mainwindow->get_mprefs();

	dialog->setWindowTitle (
		(std::is_same<Tparser, mtDW::FloatParser>::value)
			? TEXT_TITLE_FLOAT " Variables" :
		(std::is_same<Tparser, mtDW::DoubleParser>::value)
			? TEXT_TITLE_DOUBLE " Variables" :
		(std::is_same<Tparser, mtDW::IntegerParser>::value)
			? TEXT_TITLE_INTEGER " Variables" :
		(std::is_same<Tparser, mtDW::RationalParser>::value)
			? TEXT_TITLE_RATIONAL " Variables" :
		TEXT_TITLE_ERROR
		);

	dialog->setModal ( true );

	QVBoxLayout * vbox = new QVBoxLayout;
	dialog->setLayout ( vbox );

	QWidget		* w;
	QVBoxLayout	* sgvb;
	QGroupBox	* groupBox;
	QGridLayout	* grid;
	QTableWidget	* table;
	QStringList	var_column_labels;

	var_column_labels << "Variable";

	groupBox = new QGroupBox ( "Variables" );
	vbox->addWidget ( groupBox );

	sgvb = new QVBoxLayout;
	groupBox->setLayout ( sgvb );

	w = new QWidget;
	sgvb->addWidget ( w );

	grid = new QGridLayout;
	w->setLayout ( grid );

	table = new QTableWidget;
	grid->addWidget ( table, 0, 0 );

	table->setSelectionMode ( QAbstractItemView::SingleSelection );
	table->setSelectionBehavior ( QAbstractItemView::SelectRows );
	table->setEditTriggers ( QAbstractItemView::NoEditTriggers );
	table->setColumnCount ( 1 );
	table->setShowGrid ( false );
	table->verticalHeader ()->setSectionResizeMode( QHeaderView::Fixed );
	table->verticalHeader ()->setSectionsClickable ( false );

	table->horizontalHeader ()->resizeSections (
		QHeaderView::ResizeToContents );
	table->horizontalHeader ()->setStretchLastSection ( true );
	table->horizontalHeader ()->setSectionsClickable ( false );

	QPlainTextEdit * text_var = new QPlainTextEdit;
	grid->addWidget ( text_var, 0, 1 );

	dialog->connect ( table, &QTableWidget::currentCellChanged,
		[parser, table, text_var, mprefs, groupBox](
			int const currentRow,
			int const ARG_UNUSED ( currentColumn ),
			int const previousRow,
			int const ARG_UNUSED ( previousColumn )
			)
		{
			std::string	str;
			int		digits;

			decltype(parser->get_variable(str)) num;

			if ( 0 == select_vars ( *parser, table,
				text_var, currentRow, previousRow,
				mprefs.calc_snip_size, num, digits )
				)
			{
				str += "Variables ";
				str += print_digits_label ( *num, digits );
			}

			groupBox->setTitle ( str.c_str() );
		});

	table->setHorizontalHeaderLabels ( var_column_labels );
	table->horizontalHeader ()->resizeSections (
		QHeaderView::ResizeToContents );

/// ----------------------------------------------------------------------------

	QDialogButtonBox * button_box = new QDialogButtonBox (
		QDialogButtonBox::Close | QDialogButtonBox::Save );
	vbox->addWidget ( button_box );
	QPushButton * const button_var_close = button_box->button (
		QDialogButtonBox::Close );
	dialog->connect ( button_var_close, &QPushButton::clicked, [dialog]()
		{
			dialog->reject();
		});
	QPushButton * const button_var_save = button_box->button (
		QDialogButtonBox::Save );
	dialog->connect ( button_var_save, &QPushButton::clicked,
		[dialog, table, parser]()
		{
			std::string filename;
			decltype(parser->get_variable(filename)) var;

			if ( 0 == get_variable_and_filename ( dialog,
				*parser, var, table, filename, "Save number" )
			)
			{
				save_a_number ( dialog, *var, filename );
			}
		});

	populate_vars ( *parser, table );

	if ( table->rowCount() > 0 )
	{
		button_var_save->setEnabled ( true );
	}
	else
	{
		button_var_save->setEnabled ( false );
	}

	dialog->resize ( 800, 600 );
	dialog->exec();
}

void DialogVars::start ( mtDW::FloatParser const * const parser )
{
	var_gui_build ( this, m_mainwindow, parser );
}

void DialogVars::start ( mtDW::DoubleParser const * const parser )
{
	var_gui_build ( this, m_mainwindow, parser );
}

void DialogVars::start ( mtDW::IntegerParser const * const parser )
{
	var_gui_build ( this, m_mainwindow, parser );
}

void DialogVars::start ( mtDW::RationalParser const * const parser )
{
	var_gui_build ( this, m_mainwindow, parser );
}

