/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/***************************************************************************
 *            mainwindow.cc
 *
 *  Tue Nov 10 10:21:04 CET 2009
 *  Copyright 2009 Bent Bisballe Nyeng
 *  deva@aasimon.org
 ****************************************************************************/

/*
 *  This file is part of DrumGizmo.
 *
 *  DrumGizmo 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.
 *
 *  DrumGizmo 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 DrumGizmo; if not, write to the Free Software
 *  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA.
 */
#include "mainwindow.h"

#include <iostream>

#include <QHBoxLayout>
#include <QVBoxLayout>

#include <QStatusBar>
#include <QApplication>
#include <QMenuBar>
#include <QFileDialog>
#include <QToolBar>
#include <QToolButton>
#include <QMessageBox>

#include "settings.h"
#include "projectdialog.h"
#include "renderdialog.h"
#include "channeldialog.h"
#include "instrumentdialog.h"
#include "projectserialiser.h"
#include "instrumentwidget.h"
#include "channelswidget.h"
#include "aboutdialog.h"

#include "imageeditor.h"

#define MAXVAL 10000000L

MainWindow::MainWindow(Settings& settings)
	: settings(settings)
{
	tab_widget = new QTabWidget();
	tab_widget->setTabsClosable(true);
	tab_widget->setMovable(true);
	connect(tab_widget, SIGNAL(tabCloseRequested(int)),
	        this, SLOT(closeTab(int)));

	setCentralWidget(tab_widget);

	QMenu* fileMenu = menuBar()->addMenu(tr("&File"));

	QAction* act_new_project = new QAction(tr("&New Project"), this);
	fileMenu->addAction(act_new_project);
	connect(act_new_project, SIGNAL(triggered()), this, SLOT(newProject()));

	QAction* act_load_project = new QAction(tr("&Load Project..."), this);
	fileMenu->addAction(act_load_project);
	connect(act_load_project, SIGNAL(triggered()), this, SLOT(loadProject()));

	act_save_project = new QAction(tr("&Save Project"), this);
	act_save_project->setShortcut(QKeySequence::Save);
	fileMenu->addAction(act_save_project);
	connect(act_save_project, SIGNAL(triggered()), this, SLOT(saveProject()));

	QAction* act_save_project_as = new QAction(tr("Save Project As..."), this);
	fileMenu->addAction(act_save_project_as);
	connect(act_save_project_as, SIGNAL(triggered()),
	        this, SLOT(saveProjectAs()));

	QAction* act_quit = new QAction(tr("&Quit"), this);
	fileMenu->addAction(act_quit);
	connect(act_quit, SIGNAL(triggered()), this, SLOT(close()));

	QMenu* projectMenu = menuBar()->addMenu(tr("&Project"));
	QAction* act_render = new QAction(tr("&Export..."), this);
	projectMenu->addAction(act_render);
	connect(act_render, SIGNAL(triggered()), this, SLOT(render()));

	QAction* act_edit_project = new QAction(tr("&Edit Project..."), this);
	projectMenu->addAction(act_edit_project);
	connect(act_edit_project, SIGNAL(triggered()), this, SLOT(editProject()));

//	QMenu* testMenu = menuBar()->addMenu(tr("Test"));
//	QAction* act_test = new QAction(tr("Test"), this);
//	testMenu->addAction(act_test);
//	connect(act_test, SIGNAL(triggered()), this, SLOT(test()));

	QMenu* helpMenu = menuBar()->addMenu(tr("Help"));
	QAction* act_about = new QAction(tr("About"), this);
	helpMenu->addAction(act_about);
	connect(act_about, SIGNAL(triggered()), this, SLOT(showAbout()));

	instruments_dock = new QDockWidget(tr("Instruments:"), this);
	instruments_dock->setObjectName("instruments_dock");
	instruments_dock->setAllowedAreas(Qt::LeftDockWidgetArea);
	instruments_dock->setFeatures(QDockWidget::DockWidgetMovable);
	{
		auto w = new QWidget();
		auto l = new QVBoxLayout();
		w->setLayout(l);
		auto tools = new QToolBar();
		auto add = new QToolButton();
		add->setIcon(QPixmap(":icons/add_instrument.png"));
		connect(add, SIGNAL(clicked()), this, SLOT(addInstrument()));
		auto rem = new QToolButton();
		rem->setIcon(QPixmap(":icons/remove_instrument.png"));
		connect(rem, SIGNAL(clicked()), this, SLOT(removeInstrument()));
		auto edt = new QToolButton();
		edt->setIcon(QPixmap(":icons/edit_instrument.png"));
		connect(edt, SIGNAL(clicked()), this, SLOT(editInstrument()));
		tools->addWidget(add);
		tools->addWidget(rem);
		tools->addWidget(edt);
		instrument_list = new QListWidget();
		connect(instrument_list, SIGNAL(itemDoubleClicked(QListWidgetItem*)),
		        this, SLOT(instrumentDoubleClicked(QListWidgetItem*)));

		l->addWidget(tools);
		l->addWidget(instrument_list);

		instruments_dock->setWidget(w);
	}
	addDockWidget(Qt::LeftDockWidgetArea, instruments_dock);

	channels_dock = new QDockWidget(tr("Kit channels:"), this);
	channels_dock->setObjectName("channels_dock");
	channels_dock->setAllowedAreas(Qt::LeftDockWidgetArea);
	channels_dock->setFeatures(QDockWidget::DockWidgetMovable);
	{
		auto w = new QWidget();
		auto l = new QVBoxLayout();
		w->setLayout(l);
		auto tools = new QToolBar();
		auto add = new QToolButton();
		add->setIcon(QPixmap(":icons/add_channel.png"));
		connect(add, SIGNAL(clicked()), this, SLOT(addChannel()));
		auto rem = new QToolButton();
		rem->setIcon(QPixmap(":icons/remove_channel.png"));
		connect(rem, SIGNAL(clicked()), this, SLOT(removeChannel()));
		tools->addWidget(add);
		tools->addWidget(rem);
		channel_list = new QListWidget();
		connect(channel_list, SIGNAL(itemDoubleClicked(QListWidgetItem*)),
		        this, SLOT(channelDoubleClicked(QListWidgetItem*)));

		l->addWidget(tools);
		l->addWidget(channel_list);
		channels_dock->setWidget(w);
		addDockWidget(Qt::LeftDockWidgetArea, channels_dock);
	}

	loadSettings();

	statusBar()->showMessage(tr("Ready"));

	connect(&project, SIGNAL(projectChanged()), this, SLOT(projectChanged()));

	updateWindowTitle();

	setWindowEnabled(false);
}

MainWindow::~MainWindow()
{
}

void MainWindow::addInstrument()
{
	auto id = project.createInstrument();
	auto& instrument = project.getInstrument(id);

	InstrumentDialog dlg(this, instrument);
	dlg.show();
	dlg.exec();

	auto item = new QListWidgetItem();
	item->setIcon(QPixmap(":icons/instrument.png"));
	item->setText(instrument.getInstrumentName());
	item->setData(Qt::UserRole, id);
	instrument_list->addItem(item);
}

void MainWindow::editInstrument()
{
	auto items = instrument_list->selectedItems();
	for(auto item : items)
	{
		auto id = item->data(Qt::UserRole).toInt();

		auto& instrument = project.getInstrument(id);

		InstrumentDialog dlg(this, instrument);
		dlg.show();
		dlg.exec();

		item->setText(instrument.getInstrumentName());

		// Also update tab name if open
		for(int i = 0; i < tab_widget->count(); ++i)
		{
			if(tab_widget->widget(i)->property("id").toInt() == id)
			{
				tab_widget->setTabText(i, instrument.getInstrumentName());
			}
		}
	}
}

void MainWindow::removeInstrument()
{
	int ret =
		QMessageBox::question(this, tr("Delete Instrument"),
		                      tr("Are you sure you want to delete the selected "
		                         "instrument(s)?"),
		                      QMessageBox::Yes | QMessageBox::Cancel);
	if(ret != QMessageBox::Yes)
	{
		return;
	}

	auto items = instrument_list->selectedItems();
	for(auto item : items)
	{
		auto id = item->data(Qt::UserRole).toInt();

		// Also close tab if open.
		for(int i = 0; i < tab_widget->count(); ++i)
		{
			if(tab_widget->widget(i)->property("id").toInt() == id)
			{
				tab_widget->removeTab(i);
			}
		}

		project.deleteInstrument(id);
		delete item;
	}
}

void MainWindow::instrumentDoubleClicked(QListWidgetItem *item)
{
	int id = item->data(Qt::UserRole).toInt();
	auto& instr = project.getInstrument(id);

	for(int i = 0; i < tab_widget->count(); ++i)
	{
		if(tab_widget->widget(i)->property("id").toInt() == id)
		{
			tab_widget->setCurrentIndex(i);
			return;
		}
	}

	// Tab wasn't open already. Create it.
	tab_widget->addTab(new InstrumentWidget(settings, instr),
	                   QPixmap(":icons/instrument.png"),
	                   instr.getInstrumentName());
	// Make new tab active
	tab_widget->setCurrentIndex(tab_widget->count() - 1);
}

void MainWindow::addChannel()
{
	auto id = project.createChannel();
	auto& channel = project.getChannel(id);

	ChannelDialog dlg(this, channel);
	dlg.show();
	dlg.exec();

	auto item = new QListWidgetItem();
	item->setIcon(QPixmap(":icons/channel.png"));
	item->setData(Qt::UserRole, id);
	item->setText(channel.getChannelName());
	channel_list->addItem(item);
}

void MainWindow::removeChannel()
{
	int ret =
		QMessageBox::question(this, tr("Delete Channel"),
		                      tr("Are you sure you want to delete the selected "
		                         "channel(s)?"),
		                      QMessageBox::Yes | QMessageBox::Cancel);
	if(ret != QMessageBox::Yes)
	{
		return;
	}

	auto items = channel_list->selectedItems();
	for(auto item : items)
	{
		auto id = item->data(Qt::UserRole).toInt();

		auto instrument_ids = project.getInstrumentList();
		for(auto instrument_id : instrument_ids)
		{
			auto& instrument = project.getInstrument(instrument_id);
			auto audiofile_ids = instrument.getAudioFileList();
			for(auto audiofile_id : audiofile_ids)
			{
				auto& audiofile = instrument.getAudioFile(audiofile_id);
				if(audiofile.getChannelMapId() == id)
				{
					audiofile.setChannelMapId(-1); // Unset channel map
				}
			}
		}

		project.deleteChannel(id);
		delete item;
	}
}

void MainWindow::channelDoubleClicked(QListWidgetItem *item)
{
	// Tab wasn't open already. Create it.
	tab_widget->addTab(new ChannelsWidget(settings, project),
	                   QPixmap(":icons/channel.png"),
	                   tr("Channels"));
	// Make new tab active
	tab_widget->setCurrentIndex(tab_widget->count() - 1);
}

void MainWindow::updateWindowTitle()
{
	auto project_string = project.getProjectName();

	if(project_string == "")
	{
		project_string = tr("[Untitled Project]");
	}

	if(project.getProjectFile() != "")
	{
		project_string += " (" + QFileInfo(project.getProjectFile()).fileName() + ")";
	}

	act_save_project->setEnabled(project.getProjectFile() != "");

	if(project_dirty)
	{
		project_string += "*";
	}

	setWindowTitle("DGEdit - " + project_string);
}

void MainWindow::closeEvent(QCloseEvent* event)
{
	if(!checkDirty())
	{
		event->ignore();
		return;
	}

	saveSettings();
	QApplication::quit();
}

bool MainWindow::checkDirty()
{
	if(project_dirty)
	{
		int ret =
			QMessageBox::question(this, tr("Save before closing project?"),
			                      tr("The project has changed. Do you want to save "
			                         "before closing?"),
			                      QMessageBox::Yes | QMessageBox::No | QMessageBox::Cancel);
		switch(ret)
		{
		case QMessageBox::Yes:
			saveProject();
			if(project_dirty)
			{
				// Project still dirty - it was not saved (user pressed cancel?)
				return false;
			}
			break;
		case QMessageBox::No:
			// Proceed to quit
			break;
		case QMessageBox::Cancel:
			return false;
		default:
			break;
		}
	}

	return true; // not dirty or user chose not to save
}

void MainWindow::reset()
{
	// Close all tabs
	while(tab_widget->count())
	{
		tab_widget->removeTab(0);
	}

	instrument_list->clear();
	channel_list->clear();

	project.reset();
	updateWindowTitle();
}

void MainWindow::setWindowEnabled(bool enabled)
{
	instruments_dock->setEnabled(enabled);
	channels_dock->setEnabled(enabled);
	tab_widget->setEnabled(enabled);
}

void MainWindow::loadSettings()
{
	QByteArray state;
	QByteArray geometry;
	settings.loadGeometry(state, geometry);
	restoreGeometry(geometry);
	restoreState(state);
	// TODO: lineed_exportp->setText(settings.loadExportPath());
}

void MainWindow::saveSettings()
{
	settings.saveGeometry(saveState(), saveGeometry());
	// TODO: settings.saveExportPath(lineed_exportp->text());
}

void MainWindow::newProject()
{
	if(!checkDirty())
	{
		return;
	}

	reset();

	ProjectDialog dlg(this, project);
	dlg.show();
	dlg.exec();

	setWindowEnabled(true);
}

void MainWindow::loadProject()
{
	if(!checkDirty())
	{
		return;
	}

	QString filename =
		QFileDialog::getOpenFileName(this,
		                             tr("Load DGEdit Project"),
		                             "",
		                             tr("DGEdit Project Files (*.dgedit)"));
	if(filename == "")
	{
		// User clicked cancel
		return;
	}

	loadProject(filename);
}

void MainWindow::loadProject(QString filename)
{
	QFile file(filename);
	if(!file.open(QIODevice::ReadOnly))
	{
		return;
	}

	reset();

	QString xml(file.readAll());
	file.close();

	ProjectSerialiser ser;
	ser.deserialise(xml, project);

	project.setProjectFile(filename);

	project_dirty = false;
	updateWindowTitle();

	instrument_list->clear();
	auto instrument_ids = project.getInstrumentList();
	for(auto id : instrument_ids)
	{
		auto& instrument = project.getInstrument(id);
		auto item = new QListWidgetItem();
		item->setIcon(QPixmap(":icons/instrument.png"));
		item->setText(instrument.getInstrumentName());
		item->setData(Qt::UserRole, id);
		instrument_list->addItem(item);
	}

	channel_list->clear();
	auto channel_ids = project.getChannelList();
	for(auto id : channel_ids)
	{
		auto& channel = project.getChannel(id);
		auto item = new QListWidgetItem();
		item->setIcon(QPixmap(":icons/channel.png"));
		item->setText(channel.getChannelName());
		item->setData(Qt::UserRole, id);
		channel_list->addItem(item);
	}

	statusBar()->showMessage(tr("Loaded"));

	setWindowEnabled(true);
}

void MainWindow::saveProject()
{
	QString filename = project.getProjectFile();
	if(filename == "")
	{
		saveProjectAs();
		return;
	}

	QFile file(filename);
	if(!file.open(QIODevice::WriteOnly))
	{
		return;
	}

	ProjectSerialiser ser;
	auto xml = ser.serialise(project);

	file.write(xml.toUtf8());

	project_dirty = false;
	updateWindowTitle();

	statusBar()->showMessage(tr("Saved"));
}

void MainWindow::saveProjectAs()
{
	QString filename
		= QFileDialog::getSaveFileName(this, tr("Save DGEdit Project"),
		                               "",
		                               tr("DGEdit Project Files (*.dgedit)"));

	if(filename == "")
	{
		// User clicked cancel
		return;
	}

	if(filename.right(7) != ".dgedit")
	{
		filename += ".dgedit";
	}

	project.setProjectFile(filename);

	saveProject();
}

void MainWindow::projectChanged()
{
	project_dirty = true;
	updateWindowTitle();
}

void MainWindow::closeTab(int tab)
{
	tab_widget->removeTab(tab);
}

void MainWindow::test()
{
	ImageEditor* e = new ImageEditor(nullptr);
	e->setImage("/mnt/atuin/misc/stuff/deva/CrocellKit/crocellkit01.png");
	e->show();
}

void MainWindow::render()
{
	RenderDialog renderDialog(this, project);
	renderDialog.exec();
}

void MainWindow::editProject()
{
	ProjectDialog dlg(this, project);
	dlg.show();
	dlg.exec();
}

void MainWindow::showAbout()
{
	AboutDialog dlg(this, Qt::Dialog);
	dlg.show();
	dlg.exec();
}
