/*
 * Cueplot: a GUI front-end to gnuplot
 * Copyright (C) 2007 Muneyuki Noguchi
 *
 * 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 2
 * 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; if not, write to the Free Software Foundation, 
 * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
 */
#include "MainWindow.h"
#include "FitDialog.h"
#include "OptionDialog.h"
#include "Plot.h"
#include "PlotOptionDialog.h"
#include "Process.h"
#include "RawCommandDialog.h"

#include <QtCore/QCoreApplication>
#include <QtGui/QCloseEvent>
#include <QtGui/QLineEdit>
#include <QtGui/QListWidget>
#include <QtGui/QFileDialog>
#include <QtGui/QMenu>
#include <QtGui/QMenuBar>
#include <QtGui/QMessageBox>
#include <QtGui/QPushButton>
#include <QtGui/QRadioButton>
#include <QtGui/QToolBar>
#include <QtGui/QVBoxLayout>
#include <QtGui/QWhatsThis>

MainWindow::MainWindow(QWidget *parent)
: QMainWindow(parent), process(new Process)
{
	setWindowTitle(QCoreApplication::applicationName());

	functionRadioButton = new QRadioButton;
	functionRadioButton->setChecked(true);
	functionRadioButton->setText(tr("Function"));
	connect(functionRadioButton, SIGNAL(toggled(bool)),
			this, SLOT(setAddPushButton(bool)));

	functionEdit = new QLineEdit;
	connect(functionRadioButton, SIGNAL(toggled(bool)), 
			functionEdit, SLOT(setEnabled(bool)));
	connect(functionEdit, SIGNAL(returnPressed()),
			this, SLOT(addData()));

	filenameRadioButton = new QRadioButton;
	filenameRadioButton->setText(tr("File Name"));

	filenameEdit = new QLineEdit;
	filenameEdit->setEnabled(false);
	connect(filenameRadioButton, SIGNAL(toggled(bool)), 
			filenameEdit, SLOT(setEnabled(bool)));
	connect(filenameEdit, SIGNAL(returnPressed()),
			this, SLOT(addData()));

	QPushButton *filenamePushButton = new QPushButton;
	filenamePushButton->setText(tr("&Open..."));
	filenamePushButton->setEnabled(false);
	connect(filenameRadioButton, SIGNAL(toggled(bool)), 
			filenamePushButton, SLOT(setEnabled(bool)));
	connect(filenamePushButton, SIGNAL(clicked()),
			this, SLOT(openDataFile()));

	addPushButton = new QPushButton;
	addPushButton->setText(tr("&Add"));
	addPushButton->setEnabled(false);
	connect(addPushButton, SIGNAL(clicked()),
			this, SLOT(addData()));
	connect(filenameEdit, SIGNAL(textChanged(const QString &)),
			this, SLOT(setAddPushButton(const QString &)));
	connect(functionEdit, SIGNAL(textChanged(const QString &)),
			this, SLOT(setAddPushButton(const QString &)));

	optionPushButton = new QPushButton;
	optionPushButton->setText(tr("O&ption..."));
	optionPushButton->setEnabled(false);
	optionPushButton->setSizePolicy(
			QSizePolicy(QSizePolicy::Fixed, QSizePolicy::Fixed));
	connect(optionPushButton, SIGNAL(clicked()),
			this, SLOT(setPlotOptions()));

	upPushButton = new QPushButton;
	upPushButton->setText(tr("&Up"));
	upPushButton->setEnabled(false);
	connect(upPushButton, SIGNAL(clicked()),
			this, SLOT(setDataUp()));

	downPushButton = new QPushButton;
	downPushButton->setText(tr("&Down"));
	downPushButton->setEnabled(false);
	connect(downPushButton, SIGNAL(clicked()),
			this, SLOT(setDataDown()));

	deletePushButton = new QPushButton;
	deletePushButton->setText(tr("D&elete"));
	deletePushButton->setEnabled(false);
	deletePushButton->setSizePolicy(
			QSizePolicy(QSizePolicy::Fixed, QSizePolicy::Fixed));
	connect(deletePushButton, SIGNAL(clicked()),
			this, SLOT(deleteData()));

	plotListWidget = new QListWidget;
	connect(plotListWidget, SIGNAL(itemSelectionChanged()),
			this, SLOT(setPushButton()));
	connect(plotListWidget, SIGNAL(itemChanged(QListWidgetItem *)),
			this, SLOT(changeItem(QListWidgetItem *)));

	// ----------- assemble layouts and widgets
	// ------------------------ create layouts

	QGridLayout *singleLayout = new QGridLayout;
	singleLayout->addWidget(functionRadioButton, 0, 0);
	singleLayout->addWidget(functionEdit, 0, 1, 1, 2);
	singleLayout->addWidget(filenameRadioButton, 1, 0);
	singleLayout->addWidget(filenameEdit, 1, 1);
	singleLayout->addWidget(filenamePushButton, 1, 2);

	QGridLayout *listLayout = new QGridLayout;
	listLayout->addWidget(addPushButton, 0, 0);
	listLayout->addWidget(optionPushButton, 1, 0);
	listLayout->addWidget(upPushButton, 2, 0);
	listLayout->addWidget(downPushButton, 3, 0);
	// ウィンドウを最大化したときのことを考えて、QSpacerItem を入れる
	listLayout->addItem(new QSpacerItem(20, 0, 
				QSizePolicy::Minimum, QSizePolicy::Expanding), 4, 0);
	listLayout->addItem(new QSpacerItem(40, 20, 
				QSizePolicy::Expanding, QSizePolicy::Minimum), 5, 0, 1, 2);
	listLayout->addWidget(deletePushButton, 5, 2);
	listLayout->addWidget(plotListWidget, 0, 1, 5, 2);

	QVBoxLayout *mainLayout = new QVBoxLayout;
	mainLayout->addLayout(singleLayout);
	mainLayout->addLayout(listLayout);

	QWidget *mainWidget = new QWidget;
	mainWidget->setLayout(mainLayout);
	setCentralWidget(mainWidget);

	createActions();
	createMenus();
	createToolBars();
}

void MainWindow::createActions()
{
	loadAct = new QAction(tr("&Open..."), this);
	loadAct->setShortcut(tr("Ctrl+O"));
	loadAct->setWhatsThis(tr("load \"&lt;input-file&gt;\""));
	loadAct->setIcon(QIcon(":/images/fileopen.png"));
	connect(loadAct, SIGNAL(triggered()),
			this, SLOT(load()));

	saveAct = new QAction(tr("&Save"), this);
	saveAct->setShortcut(tr("Ctrl+S"));
	saveAct->setWhatsThis(tr("save '&lt;filename&gt;'"));
	saveAct->setIcon(QIcon(":/images/filesave.png"));
	connect(saveAct, SIGNAL(triggered()),
			this, SLOT(save()));

	saveAsAct = new QAction(tr("Save &As..."), this);
	saveAsAct->setWhatsThis(tr("save '&lt;filename&gt;'"));
	saveAsAct->setIcon(QIcon(":/images/filesaveas.png"));
	connect(saveAsAct, SIGNAL(triggered()),
			this, SLOT(saveAs()));

	quitAct = new QAction(tr("&Quit"), this);
	quitAct->setShortcut(tr("Ctrl+Q"));
	quitAct->setWhatsThis("quit");
	quitAct->setIcon(QIcon(":/images/exit.png"));
#if QT_VERSION >= 0x040200 && defined(Q_OS_DARWIN)
	// Mac OS X 環境で、使用している言語によりメニューの場所が変わるのを
	// 防ぐための処理
	// setMenuRole() は Qt 4.2 で導入された
	quitAct->setMenuRole(QAction::QuitRole);
#endif
	connect(quitAct, SIGNAL(triggered()),
			this, SLOT(close()));


	plotAct = new QAction(tr("&2D Plot"), this);
	plotAct->setShortcut(tr("F2"));
	plotAct->setWhatsThis("plot");
	plotAct->setEnabled(false);
	connect(plotAct, SIGNAL(triggered()),
			this, SLOT(plot()));

	splotAct = new QAction(tr("&3D Plot"), this);
	splotAct->setShortcut(tr("F3"));
	splotAct->setWhatsThis("splot");
	splotAct->setEnabled(false);
	connect(splotAct, SIGNAL(triggered()),
			this, SLOT(splot()));

	rawCommandAct = new QAction(tr("&Raw Commands..."), this);
	rawCommandAct->setIcon(QIcon(":/images/run.png"));
	connect(rawCommandAct, SIGNAL(triggered()),
			this, SLOT(rawCommand()));

	fitAct = new QAction(tr("&Fittings..."), this);
	fitAct->setWhatsThis("fit");
	connect(fitAct, SIGNAL(triggered()),
			this, SLOT(fit()));

	setAct = new QAction(tr("&Settings..."), this);
	setAct->setWhatsThis("set");
#if QT_VERSION >= 0x040200 && defined(Q_OS_DARWIN)
	setAct->setMenuRole(QAction::TextHeuristicRole);
#endif
	connect(setAct, SIGNAL(triggered()),
			this, SLOT(set()));


	toolBarAct = new QAction(tr("&Tool Bar"), this);
	toolBarAct->setCheckable(true);

	consoleAct = new QAction(tr("&Console..."), this);
	consoleAct->setCheckable(true);
	connect(consoleAct, SIGNAL(toggled(bool)),
			this, SLOT(console(bool)));


	whatsThisAct = new QAction(tr("What's &This?"), this);
	whatsThisAct->setShortcut(tr("Shift+F1"));
	whatsThisAct->setIcon(QIcon(":/images/contexthelp.png"));
	connect(whatsThisAct, SIGNAL(triggered()),
			this, SLOT(whatsThis()));

	aboutAct = new QAction(tr("&About"), this);
#if QT_VERSION >= 0x040200 && defined(Q_OS_DARWIN)
	aboutAct->setMenuRole(QAction::AboutRole);
#endif
	connect(aboutAct, SIGNAL(triggered()),
			this, SLOT(about()));

	aboutQtAct = new QAction(tr("About &Qt"), this);
#if QT_VERSION >= 0x040200 && defined(Q_OS_DARWIN)
	aboutQtAct->setMenuRole(QAction::AboutQtRole);
#endif
	connect(aboutQtAct, SIGNAL(triggered()),
			QCoreApplication::instance(), SLOT(aboutQt()));
}

void MainWindow::createMenus()
{
	QMenu *fileMenu = menuBar()->addMenu(tr("&File"));
	fileMenu->addAction(loadAct);
	fileMenu->addAction(saveAct);
	fileMenu->addAction(saveAsAct);
	fileMenu->addSeparator();
	fileMenu->addAction(quitAct);

	QMenu *commandMenu = menuBar()->addMenu(tr("&Command"));
	commandMenu->addAction(plotAct);
	commandMenu->addAction(splotAct);
	commandMenu->addAction(rawCommandAct);
	commandMenu->addAction(fitAct);
	commandMenu->addAction(setAct);

	QMenu *settingsMenu = menuBar()->addMenu(tr("&Settings"));
	settingsMenu->addAction(toolBarAct);
	settingsMenu->addAction(consoleAct);
	connect(settingsMenu, SIGNAL(aboutToShow()),
			this, SLOT(setSettingsAction()));

	QMenu *helpMenu = menuBar()->addMenu(tr("&Help"));
	helpMenu->addAction(whatsThisAct);
	helpMenu->addSeparator();
	helpMenu->addAction(aboutAct);
	helpMenu->addAction(aboutQtAct);
}

void MainWindow::createToolBars()
{
	toolBar = addToolBar(tr("Tool Bar"));
	toolBar->addAction(loadAct);
	toolBar->addAction(saveAct);
	toolBar->addAction(plotAct);
	toolBar->addAction(splotAct);
	connect(toolBarAct, SIGNAL(toggled(bool)),
			toolBar, SLOT(setVisible(bool)));
}

MainWindow::~MainWindow()
{
	while (!plotList.isEmpty()) {
		delete plotList.takeFirst();
	}
}

void MainWindow::addData()
{
	// 関数が選択されていれば関数を追加し、
	// ファイルが選択されていればファイルを追加する
	bool isFunction = functionRadioButton->isChecked();
	QLineEdit *lineEdit = isFunction ? functionEdit : filenameEdit;
	QString data = lineEdit->text();
	if (data.isEmpty()) {
		return;
	}
	Plot *plot = new Plot;
	plot->setFunction(isFunction);
	isFunction ? plot->setFunction(data) : plot->setDataFile(data);
	if (plotListWidget->currentRow() < 0) {
		// リスト中に項目がないとき
		plotListWidget->addItem(data);
		plotListWidget->setCurrentRow(plotListWidget->count() - 1);

		plotList.append(plot);
	} else {
		// 項目があるとき、選択されている項目の次に追加
		int row = plotListWidget->currentRow() + 1;
		plotListWidget->insertItem(row, data);
		plotListWidget->setCurrentRow(row);

		plotList.insert(row, plot);
	}
	QListWidgetItem *item = plotListWidget->currentItem();
	item->setFlags(item->flags() | Qt::ItemIsEditable 
			| Qt::ItemIsUserCheckable);
	item->setCheckState(Qt::Checked);
	lineEdit->clear();
	plotAct->setEnabled(true);
	splotAct->setEnabled(true);
	setPushButton();
}

void MainWindow::setDataUp()
{
	int row = plotListWidget->currentRow();
	if (row > 0) {
		QListWidgetItem *item = plotListWidget->takeItem(row);
		plotListWidget->insertItem(row - 1, item);
		plotListWidget->setCurrentRow(row - 1);

		Plot *plot = plotList.takeAt(row);
		plotList.insert(row - 1, plot);
	}
}

void MainWindow::setDataDown()
{
	int row = plotListWidget->currentRow();
	if (row < plotListWidget->count() - 1) {
		QListWidgetItem *item = plotListWidget->takeItem(row);
		plotListWidget->insertItem(row + 1, item);
		plotListWidget->setCurrentRow(row + 1);

		Plot *plot = plotList.takeAt(row);
		plotList.insert(row + 1, plot);
	}
}

void MainWindow::deleteData()
{
	int row = plotListWidget->currentRow();
	delete plotListWidget->takeItem(row);
	delete plotList.takeAt(row);
	setAction();
	setPushButton();
}

void MainWindow::plot()
{
	QStringList commandList;
	for (int i = 0, j = plotList.size(); i < j; i++) {
		if (plotListWidget->item(i)->checkState() == Qt::Checked) {
			commandList << plotList.at(i)->command();
		}
	}
	process->execute("plot " + commandList.join(", "));
}

// TODO use Splot object
void MainWindow::splot()
{
	QStringList commandList;
	for (int i = 0, j = plotList.size(); i < j; i++) {
		if (plotListWidget->item(i)->checkState() == Qt::Checked) {
			commandList << plotList.at(i)->command();
		}
	}
	process->execute("splot " + commandList.join(", "));
}

void MainWindow::openDataFile()
{
	QString file = QFileDialog::getOpenFileName(this,
			tr("Open a data file") + " - " 
			+ QCoreApplication::applicationName(),
			currentDataFile);
	if (!file.isEmpty()) {
		filenameEdit->setText(file);
		currentDataFile = file;
	}
}

void MainWindow::save()
{
	if (currentFile.isEmpty()) {
		saveAs();
	} else {
		process->execute("save '" + currentFile + "'");
	}
}

void MainWindow::saveAs()
{
	QString file = QFileDialog::getSaveFileName(this,
			tr("Save under a file name") + " - " 
			+ QCoreApplication::applicationName(),
			currentFile); 
	if (!file.isEmpty()) {
		process->execute("save '" + file + "'");
		currentFile = file;
	}
}

void MainWindow::load()
{
	QString file = QFileDialog::getOpenFileName(this,
			tr("Open a file") + " - " + QCoreApplication::applicationName(),
			currentFile); 
	if (!file.isEmpty()) {
		process->execute("load \"" + file + "\"");
		currentFile = file;
	}
}

void MainWindow::rawCommand()
{
	RawCommandDialog *rawCommandDialog = &RawCommandDialog::instance();
	rawCommandDialog->disconnect();
	connect(rawCommandDialog, SIGNAL(executed(const QString &)),
			process, SLOT(execute(const QString &)));
	rawCommandDialog->show();
	rawCommandDialog->raise();
	rawCommandDialog->activateWindow();
}

void MainWindow::fit()
{
	FitDialog *fitDialog  = &FitDialog::instance();
	fitDialog->disconnect();
	connect(fitDialog, SIGNAL(executed(const QString &)),
			process, SLOT(execute(const QString &)));
	fitDialog->show();
	fitDialog->raise();
	fitDialog->activateWindow();
}

void MainWindow::set()
{
	OptionDialog *optionDialog = &OptionDialog::instance();
	optionDialog->disconnect();
	connect(optionDialog, SIGNAL(executed(const QString &)),
			process, SLOT(execute(const QString &)));
	optionDialog->show();
	optionDialog->raise();
	optionDialog->activateWindow();
}

void MainWindow::setPushButton()
{
	// 項目が一つでも選択されていたら、
	// オプションボタンと削除ボタンを有効にする
	bool isSelected = (plotListWidget->currentRow() >= 0);
	optionPushButton->setEnabled(isSelected);
	deletePushButton->setEnabled(isSelected);
	// 選択された項目があり、複数の項目がリスト中にあれば、
	// 上ボタンと下ボタンを有効にする
	bool isMulti = isSelected && (plotListWidget->count() > 1);
	upPushButton->setEnabled(isMulti);
	downPushButton->setEnabled(isMulti);
}

void MainWindow::setAddPushButton(const QString &text)
{
	addPushButton->setEnabled(!text.isEmpty());
}

void MainWindow::setAddPushButton(bool checked)
{
	setAddPushButton(
			(checked ? functionEdit : filenameEdit)->text());
}

void MainWindow::setPlotOptions()
{
	PlotOptionDialog *plotOptionDialog = new PlotOptionDialog;
	connect(plotOptionDialog, SIGNAL(executed(const QString &)),
			process, SLOT(execute(const QString &)));
	plotOptionDialog->setPlot(*plotList[plotListWidget->currentRow()]);
	plotOptionDialog->show();
}

void MainWindow::setSettingsAction()
{
	consoleAct->setChecked(process->isDialogVisible());
	toolBarAct->setChecked(toolBar->isVisible());
}

void MainWindow::console(bool visible)
{
	process->setDialogVisible(visible);
}

void MainWindow::whatsThis()
{
	QWhatsThis::enterWhatsThisMode();
}

void MainWindow::about()
{
	QMessageBox::about(this, tr("About %1")
			.arg(QCoreApplication::applicationName()), 
			"<h3>" + QCoreApplication::applicationName() + " 0.0.3</h3>"
			"<p>" + QCoreApplication::applicationName() 
			+ tr(": a GUI front-end to gnuplot<br>"
				"Copyright (C) 2007 Muneyuki Noguchi</p>"
				"<p>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 2 "
				"of the License, or (at your option) any later version.</p>"
				"<p>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.</p>"
				"<p>You should have received a copy of the GNU General Public License "
				"along with this program; if not, write to the Free Software "
				"Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.</p>"));
}

void MainWindow::changeItem(QListWidgetItem *item)
{
	plotListWidget->setCurrentItem(item);
	Plot *plot = plotList[plotListWidget->currentRow()];
	QString text = item->text();
	if (plot->isFunction()) {
		plot->setFunction(text);
	} else {
		plot->setDataFile(text);
	}
	setAction();
}

void MainWindow::closeEvent(QCloseEvent *event)
{
	// 一つでも項目がリスト中にあれば、終了させるか確認ダイアログを表示
	if (plotListWidget->count() > 0) {
		switch (QMessageBox::warning(this, tr("Confirmation") + " - "
					+ QCoreApplication::applicationName(),
					tr("Function(s) and/or data exist in the list.\n"
						"Are you sure you want to quit?"),
					tr("&Quit"), tr("&Cancel"), 0, 0, 1)) {
		case 1:
			event->ignore();
			return;
		}
	}
	// すべてのダイアログを閉じる
	FitDialog::destroyInstance();
	OptionDialog::destroyInstance();
	RawCommandDialog::destroyInstance();
	process->closeDialog();
	event->accept();
}

void MainWindow::setAction()
{
	// チェックされている項目があるか調べる
	bool isChecked = false;
	for (int i = 0, j = plotListWidget->count(); i < j; i++) {
		if (plotListWidget->item(i)->checkState() == Qt::Checked) {
			isChecked = true;
			break;
		}
	}
	plotAct->setEnabled(isChecked);
	splotAct->setEnabled(isChecked);
}
