/*
 * Cueplot: a GUI front-end to gnuplot
 * Copyright (C) 2007, 2008 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 "AnglesOptionWidget.h"
#include "AxisDataOptionWidget.h"
#include "AxisLabelOptionWidget.h"
#include "AxisRangeOptionWidget.h"
#include "AxisTicsOptionWidget.h"
#include "OptionDialog.h"
#include "BarOptionWidget.h"
#include "BoxWidthOptionWidget.h"
#include "DummyOptionWidget.h"
#include "EncodingOptionWidget.h"
#include "FormatOptionWidget.h"
#include "GridOptionWidget.h"
#include "KeyOptionWidget.h"
#include "LogScaleOptionWidget.h"
#include "MappingOptionWidget.h"
#include "MAxisTicsOptionWidget.h"
#include "OutputOptionWidget.h"
#include "ParametricOptionWidget.h"
#include "SamplesOptionWidget.h"
#include "SizeOptionWidget.h"
#include "StyleOptionWidget.h"
#include "TerminalOptionWidget.h"
#include "TitleOptionWidget.h"
#include "ViewOptionWidget.h"

#include <QtCore/QCoreApplication>
#include <QtGui/QCloseEvent>
#if QT_VERSION >= 0x040200
# include <QtGui/QDialogButtonBox>
#endif
#include <QtGui/QLabel>
#include <QtGui/QListWidget>
#include <QtGui/QListWidgetItem>
#include <QtGui/QHBoxLayout>
#include <QtGui/QMessageBox>
#include <QtGui/QPushButton>
#include <QtGui/QScrollArea>
#include <QtGui/QSplitter>
#include <QtGui/QStackedWidget>
#include <QtGui/QVBoxLayout>

OptionDialog *OptionDialog::instance_ = 0;

OptionDialog &OptionDialog::instance(QWidget *parent)
{
	if (!instance_) {
		instance_ = new OptionDialog(parent);
	}
	return *instance_;
}

void OptionDialog::destroyInstance()
{
	if (instance_) {
		delete instance_;
		instance_ = 0;
	}
}

OptionDialog::OptionDialog(QWidget *parent)
: QDialog(parent)
{
	setWindowTitle(tr("Options") + "[*] - " 
			+ QCoreApplication::applicationName());

	headerLabel = new QLabel;

	QFrame *headerFrame = new QFrame;
	headerFrame->setFrameShape(QFrame::HLine);
	headerFrame->setFrameShadow(QFrame::Sunken);

	pagesStackedWidget = new QStackedWidget;
	pagesStackedWidget->addWidget(new AnglesOptionWidget);
	pagesStackedWidget->addWidget(new AxisDataOptionWidget);
	pagesStackedWidget->addWidget(new AxisLabelOptionWidget);
	pagesStackedWidget->addWidget(new AxisRangeOptionWidget);
	pagesStackedWidget->addWidget(new AxisTicsOptionWidget);
	pagesStackedWidget->addWidget(new BarOptionWidget);
	pagesStackedWidget->addWidget(new BoxWidthOptionWidget);
	pagesStackedWidget->addWidget(new DummyOptionWidget);
	pagesStackedWidget->addWidget(new EncodingOptionWidget);
	pagesStackedWidget->addWidget(new FormatOptionWidget);
	pagesStackedWidget->addWidget(new GridOptionWidget);
	pagesStackedWidget->addWidget(new KeyOptionWidget);
	pagesStackedWidget->addWidget(new LogScaleOptionWidget);
	pagesStackedWidget->addWidget(new MappingOptionWidget);
	pagesStackedWidget->addWidget(new MAxisTicsOptionWidget);
	pagesStackedWidget->addWidget(new OutputOptionWidget);
	pagesStackedWidget->addWidget(new ParametricOptionWidget);
	pagesStackedWidget->addWidget(new SamplesOptionWidget);
	pagesStackedWidget->addWidget(new SizeOptionWidget);
	pagesStackedWidget->addWidget(new StyleOptionWidget);
	pagesStackedWidget->addWidget(new TerminalOptionWidget);
	pagesStackedWidget->addWidget(new TitleOptionWidget);
	pagesStackedWidget->addWidget(new ViewOptionWidget);

	const int count = pagesStackedWidget->count();
	initBitArray.resize(count);

	contentsListWidget = new QListWidget;
	for (int i = 0; i < count; i++) {
		QWidget *widget = pagesStackedWidget->widget(i);
		contentsListWidget->addItem(widget->windowTitle());
		contentsListWidget->item(i)->setWhatsThis(widget->whatsThis());
	}
	connect(contentsListWidget,
			SIGNAL(currentItemChanged(QListWidgetItem *, QListWidgetItem *)),
			this, SLOT(changePage(QListWidgetItem *, QListWidgetItem*)));
	// FIXME
	contentsListWidget->setCurrentRow(1);
	contentsListWidget->setCurrentRow(0);

	QScrollArea *scrollArea = new QScrollArea;
	scrollArea->setWidget(pagesStackedWidget);
	scrollArea->setWidgetResizable(true);
	scrollArea->setFrameShadow(QFrame::Plain);
	scrollArea->setFrameShape(QFrame::NoFrame);
	
	QFrame *lineFrame = new QFrame;
	lineFrame->setFrameShape(QFrame::HLine);
	lineFrame->setFrameShadow(QFrame::Sunken);

	QPushButton *initPushButton = new QPushButton;
	initPushButton->setText(tr("&Defaults"));
	initPushButton->setAutoDefault(false);
	initPushButton->setDefault(false);
	connect(initPushButton, SIGNAL(clicked()),
			this, SLOT(init()));

	QPushButton *acceptPushButton = new QPushButton;
	acceptPushButton->setText(tr("&OK"));
	acceptPushButton->setAutoDefault(true);
	acceptPushButton->setDefault(true);
	connect(acceptPushButton, SIGNAL(clicked()),
			this, SLOT(accept()));

	QPushButton *applyPushButton = new QPushButton;
	applyPushButton->setText(tr("&Apply"));
	connect(applyPushButton, SIGNAL(clicked()),
			this, SLOT(apply()));

	QPushButton *rejectPushButton = new QPushButton;
	rejectPushButton->setText(tr("&Cancel"));
	connect(rejectPushButton, SIGNAL(clicked()),
			this, SLOT(reject()));

#if QT_VERSION >= 0x040200
	QDialogButtonBox *dialogButtonBox = new QDialogButtonBox;
	dialogButtonBox->addButton(initPushButton,
			QDialogButtonBox::ResetRole);
	dialogButtonBox->addButton(acceptPushButton, 
			QDialogButtonBox::AcceptRole);
	dialogButtonBox->addButton(applyPushButton,
			QDialogButtonBox::ApplyRole);
	dialogButtonBox->addButton(rejectPushButton,
			QDialogButtonBox::RejectRole);
#else
	QHBoxLayout *buttonLayout = new QHBoxLayout;
	buttonLayout->addWidget(initPushButton);
	buttonLayout->addStretch();
	buttonLayout->addWidget(acceptPushButton);
	buttonLayout->addWidget(applyPushButton);
	buttonLayout->addWidget(rejectPushButton);
#endif

	QVBoxLayout *pagesLayout = new QVBoxLayout;
	pagesLayout->addWidget(headerLabel);
	pagesLayout->addWidget(headerFrame);
	pagesLayout->addWidget(scrollArea);
	pagesLayout->addWidget(lineFrame);
#if QT_VERSION >= 0x040200
	pagesLayout->addWidget(dialogButtonBox);
#else
	pagesLayout->addLayout(buttonLayout);
#endif

	QWidget *pagesWidget = new QWidget;
	pagesWidget->setLayout(pagesLayout);

	QSplitter *centerSplitter = new QSplitter;
	centerSplitter->addWidget(contentsListWidget);
	centerSplitter->addWidget(pagesWidget);

	QVBoxLayout *mainLayout = new QVBoxLayout;
	mainLayout->addWidget(centerSplitter);

	setLayout(mainLayout);
}

void OptionDialog::accept()
{
	apply();
	QDialog::accept();
}

void OptionDialog::apply()
{
	qobject_cast<OptionWidget *>(pagesStackedWidget->currentWidget())
		->apply();
	setWindowModified(false);
}

void OptionDialog::reject()
{
	// 変更を破棄
	if (isWindowModified()) {
		discard(qobject_cast<OptionWidget *>(pagesStackedWidget->
					currentWidget()));
	}
	QDialog::reject();
}

void OptionDialog::init()
{
	qobject_cast<OptionWidget *>(pagesStackedWidget->currentWidget())
		->init();
	setWindowModified(true);
}

void OptionDialog::discard(OptionWidget *optionWidget)
{
	optionWidget->disconnect(SIGNAL(modified()));
	qobject_cast<OptionWidget *>(pagesStackedWidget->currentWidget())
		->discard();
	connect(optionWidget, SIGNAL(modified()),
			this, SLOT(setModified()));
	setWindowModified(false);
}

void OptionDialog::changePage(QListWidgetItem *current, 
		QListWidgetItem *previous)
{
	if (!current) {
		current = previous;
	} else if (isWindowModified()) {
		// 設定を保存せずに、別の項目に移動しようとした場合に確認する
		switch (QMessageBox::warning(this, tr("Unsaved Changes") + " - "
					+ QCoreApplication::applicationName(),
					tr("There are unsaved changes in this option.\n"
						"Do you want to apply the changes "
						"before opening the new option?"),
					tr("&Apply"), tr("&Discard"), tr("&Cancel"), 0, 2)) {
		case 0:
			apply();
			break;
		case 1:
			{
				int index = contentsListWidget->row(previous);
				discard(qobject_cast<OptionWidget *>(pagesStackedWidget
							->widget(index)));
			}
			break;
		case 2:
			setWindowModified(false);
			contentsListWidget->setCurrentRow(
					contentsListWidget->row(previous));
			setWindowModified(true);
			return;
		}
	}
	int index = contentsListWidget->row(current);
	// 以前に表示した項目でなければ、ウィジェットを初期化する
	if (!initBitArray.at(index)) {
		OptionWidget *optionWidget 
			= qobject_cast<OptionWidget *>(pagesStackedWidget->widget(index));
		optionWidget->construct();
		optionWidget->init();
		connect(optionWidget, SIGNAL(executed(const QString &)),
				this, SIGNAL(executed(const QString &)));
		connect(optionWidget, SIGNAL(modified()),
				this, SLOT(setModified()));
		initBitArray.setBit(index);
	}
	QWidget *widget = pagesStackedWidget->widget(index);
	headerLabel->setText(widget->windowTitle() + " - " + widget->whatsThis());
	pagesStackedWidget->setCurrentIndex(index);
}

void OptionDialog::setModified()
{
	setWindowModified(true);
}

void OptionDialog::closeEvent(QCloseEvent *e)
{
	// ダイアログが閉じられたときに、設定を破棄する
	if (isWindowModified()) {
		discard(qobject_cast<OptionWidget *>(pagesStackedWidget->
					currentWidget()));
	}
	e->accept();
}
