/***************************************************************************
 *   Copyright (C) 2010 by Volker Lanz <vl@fidra.de>                       *
 *                                                                         *
 *   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 "gui/mainwindow.h"

#include "gui/chooseprofiledialog.h"
#include "gui/editprofiledialog.h"
#include "gui/activitypage.h"
#include "gui/profiledockwidget.h"
#include "gui/categoriesdockwidget.h"
#include "gui/calendardockwidget.h"
#include "gui/importdialog.h"
#include "gui/importprogressdialog.h"
#include "gui/syncsourcedialog.h"

#if !defined(HAVE_KDE)
#include "gui/aboutdialog.h"
#endif

#include "core/profile.h"
#include "core/activity.h"
#include "core/lap.h"
#include "core/track.h"
#include "core/storage.h"
#include "core/category.h"

#include "fileimport/importer.h"
#include "fileexport/exporter.h"
#include "fileexport/kmlexporter.h"

#include "devicesync/sync.h"
#include "devicesync/syncsource.h"

#include "config/configureoptionsdialog.h"

#include "veneer/standardaction.h"
#include "veneer/actioncollection.h"
#include "veneer/settings.h"
#include "veneer/contextmenu.h"
#include "veneer/messagebox.h"
#include "veneer/filedialog.h"
#include "veneer/io.h"
#include "veneer/url.h"
#include "veneer/iconloader.h"

#include "util/helpers.h"
#include "util/unit.h"
#include "util/weather.h"

#if defined(HAVE_KDE)
#include <kxmlguifactory.h>
#include <kstatusbar.h>
#include <kglobal.h>
#include <kcomponentdata.h>
#include <kaboutdata.h>
#else
#include <QStatusBar>
#include <QSettings>
#endif

#include <kicon.h>
#include <kaction.h>

#include <QDebug>
#include <QLabel>
#include <QPointer>
#include <QSqlDatabase>
#include <QTreeWidget>
#include <QTreeWidgetItem>
#include <QCloseEvent>
#include <QDomElement>
#include <QXmlStreamWriter>
#include <QMessageBox>
#include <QProcess>
#include <QTemporaryFile>
#include <QMenu>
#include <QDir>

#if defined(Q_OS_WIN)
#include <windows.h>
#endif

MainWindow::MainWindow(QWidget* parent) :
#if defined(HAVE_KDE)
	KXmlGuiWindow(parent),
#else
	QMainWindow(parent),
#endif
	Ui::MainWindow(),
	m_ActionCollection(new Veneer::ActionCollection(this)),
	m_Profile(NULL),
	m_ImportProgressDialog(NULL),
	m_Importer(NULL),
	m_Sync(NULL),
	m_Exporter(NULL)
{
	setupUi(this);
	init();
	setAttribute(Qt::WA_DeleteOnClose);
}

MainWindow::~MainWindow()
{
	delete m_Profile;
}

void MainWindow::init()
{
	setupActions();
	setupConnections();

#if defined(HAVE_KDE)
	// the KDE build does not use the toolbars or the menu bar from Designer
	delete Ui::MainWindow::menuBar;
	delete Ui::MainWindow::mainToolbar;
	delete Ui::MainWindow::editToolbar;
	
	setupGUI();
	QMenu* fileMenu = static_cast<QMenu*>(guiFactory()->container("file", this));
	if (fileMenu)
		fileMenu->setTitle(tr("Profile"));
	setWindowTitle(KGlobal::mainComponent().aboutData()->programName());
#else
	restoreGeometry(Veneer::Settings::self()->qs()->value("geometry").toByteArray());
	restoreState(Veneer::Settings::self()->qs()->value("state").toByteArray());
	setWindowTitle(QCoreApplication::applicationName());
#endif

	loadConfig();

	setProfile(NULL); // this triggers some disable stuff in the widgets, don't remove
	loadLastProfile();

	enableActions();

	on_tabWidget_currentChanged(tabWidget->currentIndex());
	profileDockWidget->updateProfile(m_Profile);
}

void MainWindow::closeEvent(QCloseEvent* event)
{
	if (m_Profile && m_Profile->isDirty())
	{
		switch (Veneer::messageBoxQuestionYesNoCancel(this,
			tr("There are unsaved changes.<br><br>Do you want to save your profile before quitting?"),
			tr("Save Changes to Profile?"),
			tr("Yes, Save Changes"),
			tr("No, Do Not Save Changes"),
			tr("Cancel, Do Not Quit"),
			"reallyQuit"))
		{
			case QMessageBox::Yes:
				onSaveProfile();
				break;

			case QMessageBox::No:
				break;

			case QMessageBox::Cancel:
				event->ignore();
				return;

			default:
				break;
		}
	}

	saveConfig();

#if defined(HAVE_KDE)
	KXmlGuiWindow::closeEvent(event);
#else
	Veneer::Settings::self()->qs()->setValue("geometry", saveGeometry());
	Veneer::Settings::self()->qs()->setValue("state", saveState());
	QMainWindow::closeEvent(event);
#endif
}

void MainWindow::setupActions()
{
	// File actions
	Veneer::StandardAction::quit(this, SLOT(close()), actionCollection());

	KAction* newProfile = Veneer::StandardAction::openNew(this, SLOT(onNewProfile()), actionCollection());
	newProfile->setToolTip(tr("Create a new profile"));
	newProfile->setStatusTip(tr("Create a new, empty profile"));

	KAction* openProfile = Veneer::StandardAction::open(this, SLOT(onOpenProfile()), actionCollection());
	openProfile->setToolTip(tr("Open a profile"));
	openProfile->setStatusTip(tr("Pick a profile from a list of existing profiles to open"));

	KAction* saveProfile = Veneer::StandardAction::save(this, SLOT(onSaveProfile()), actionCollection());
	saveProfile->setToolTip(tr("Save current profile"));
	saveProfile->setStatusTip(tr("Save the current profile"));

	KAction* closeProfile = Veneer::StandardAction::close(this, SLOT(onCloseProfile()), actionCollection());
	closeProfile->setToolTip(tr("Close current profile"));
	closeProfile->setStatusTip(tr("Close the current profile"));

	KAction* selectAll = Veneer::StandardAction::selectAll(this, SLOT(onSelectAll()), actionCollection());
	selectAll->setToolTip(tr("Select all entries"));
	selectAll->setStatusTip(tr("Select all entries in the current view"));

	QAction* deleteProfile = actionCollection()->addAction("deleteProfile", this, SLOT(onDeleteProfile()));
	deleteProfile->setText(tr("Delete Profile"));
	deleteProfile->setToolTip(tr("Delete current profile"));
	deleteProfile->setStatusTip(tr("Delete the current profile"));
	deleteProfile->setIcon(BarIcon("edit-delete-shred"));

	QAction* importAction = actionCollection()->addAction("import", this, SLOT(onImport()));
	importAction->setText(tr("Import..."));
	importAction->setToolTip(tr("Import from a file"));
	importAction->setStatusTip(tr("Import one or more activities from a file."));
	importAction->setIcon(BarIcon("document-import"));

	QAction* exportAction = actionCollection()->addAction("export", this, SLOT(onExport()));
	exportAction->setText(tr("Export..."));
	exportAction->setToolTip(tr("Export to a file"));
	exportAction->setStatusTip(tr("Export one or more activities to a file."));
	exportAction->setIcon(BarIcon("document-export"));

#if defined(HAVE_SYNC_SUPPORT)
	QAction* syncAction = actionCollection()->addAction("sync", this, SLOT(onSync()));
	syncAction->setText(tr("Sync with Device"));
	syncAction->setToolTip(tr("Synchronize with device"));
	syncAction->setStatusTip(tr("Synchronize activities with a tracking device."));
	syncAction->setIcon(BarIcon("folder-sync"));
#endif

	QAction* googleEarthAction = actionCollection()->addAction("googleEarth", this, SLOT(onGoogleEarth()));
	googleEarthAction->setText(tr("Open in Google Earth"));
	googleEarthAction->setToolTip(tr("Open activity in Google Earth"));
	googleEarthAction->setStatusTip(tr("Open an activity in Google Earth."));

	QAction* editProfile = actionCollection()->addAction("editProfile", this, SLOT(onEditProfile()));
	editProfile->setText(tr("Edit Profile"));
	editProfile->setToolTip(tr("Edit the profile"));
	editProfile->setStatusTip(tr("Edit the currently open profile"));
	editProfile->setIcon(BarIcon("document-edit"));

	QAction* deleteAction = actionCollection()->addAction("delete", this, SLOT(onDelete()));
	deleteAction->setText(tr("Delete"));
	deleteAction->setToolTip(tr("Delete the selection"));
	deleteAction->setStatusTip(tr("Delete the selection from the profile"));
	deleteAction->setIcon(BarIcon("edit-delete"));
	deleteAction->setShortcut(Qt::Key_Delete);

	actionCollection()->addAction("toggleCalendarDock", calendarDock->toggleViewAction());
	actionCollection()->addAction("toggleCategoriesDock", categoriesDock->toggleViewAction());
	actionCollection()->addAction("toggleProfileDock", profileDock->toggleViewAction());
	actionCollection()->addAction("toggleGraphDock", graphDock->toggleViewAction());
	actionCollection()->addAction("toggleMapDock", mapDock->toggleViewAction());

	// Settings Actions
	Veneer::StandardAction::preferences(this, SLOT(onConfigureOptions()), actionCollection());

#if !defined(HAVE_KDE)
	profileMenu->addAction(actionCollection()->action("file_new"));
	profileMenu->addSeparator();
	profileMenu->addAction(actionCollection()->action("file_open"));
	profileMenu->addAction(actionCollection()->action("file_save"));
	profileMenu->addSeparator();
	profileMenu->addAction(actionCollection()->action("file_close"));
	profileMenu->addSeparator();
	profileMenu->addAction(actionCollection()->action("file_quit"));

	editMenu->addAction(actionCollection()->action("edit_select_all"));
	editMenu->addAction(actionCollection()->action("editProfile"));
	editMenu->addAction(actionCollection()->action("delete"));

	viewMenu->addAction(actionCollection()->action("toggleCalendarDock"));
	viewMenu->addAction(actionCollection()->action("toggleCategoriesDock"));
	viewMenu->addAction(actionCollection()->action("toggleProfileDock"));
	viewMenu->addAction(actionCollection()->action("toggleGraphDock"));
	viewMenu->addAction(actionCollection()->action("toggleMapDock"));

	toolsMenu->addAction(actionCollection()->action("import"));
	toolsMenu->addAction(actionCollection()->action("export"));

	if (actionCollection()->action("sync"))
	{
		toolsMenu->addSeparator();
		toolsMenu->addAction(actionCollection()->action("sync"));
	}

	toolsMenu->addSeparator();
	toolsMenu->addAction(actionCollection()->action("googleEarth"));

	KAction* showStatusbarAction = Veneer::StandardAction::showStatusbar(this, SLOT(onShowStatusbar()), actionCollection());
	showStatusbarAction->setChecked(true);

	QMenu* toolbarMenu = settingsMenu->addMenu(tr("&Toolbars Shown"));
	toolbarMenu->addAction(Ui::MainWindow::mainToolbar->toggleViewAction());
	toolbarMenu->addAction(Ui::MainWindow::editToolbar->toggleViewAction());

	settingsMenu->addAction(actionCollection()->action("options_show_statusbar"));
	settingsMenu->addSeparator();
	settingsMenu->addAction(actionCollection()->action("options_configure"));

	KAction* aboutAppAction = Veneer::StandardAction::aboutApp(this, SLOT(onAbout()), actionCollection());
 	helpMenu->addAction(aboutAppAction);

	mainToolbar->addAction(actionCollection()->action("file_new"));
	mainToolbar->addAction(actionCollection()->action("file_open"));
	mainToolbar->addAction(actionCollection()->action("file_save"));

	editToolbar->addAction(actionCollection()->action("editProfile"));
	editToolbar->addSeparator();
	editToolbar->addAction(actionCollection()->action("import"));
	editToolbar->addAction(actionCollection()->action("export"));

	if (actionCollection()->action("sync"))
	{
		editToolbar->addSeparator();
		editToolbar->addAction(actionCollection()->action("sync"));
	}
#endif
}

void MainWindow::setupConnections()
{
	connect(activityWidget, SIGNAL(nameChanged()), SLOT(onDetailsChanged()));
	connect(activityWidget, SIGNAL(equipmentChanged()), SLOT(onDetailsChanged()));
	connect(activityWidget, SIGNAL(weatherChanged()), SLOT(onDetailsChanged()));
	connect(activityWidget, SIGNAL(temperatureChanged()), SLOT(onDetailsChanged()));
	connect(activityWidget, SIGNAL(windChanged()), SLOT(onDetailsChanged()));
	connect(activityWidget, SIGNAL(notesChanged()), SLOT(onDetailsChanged()));

	connect(athleteLogWidget, SIGNAL(dateChanged()), SLOT(onAthleteLogChanged()));
	connect(athleteLogWidget, SIGNAL(weightChanged()), SLOT(onAthleteLogChanged()));
	connect(athleteLogWidget, SIGNAL(bodyFatPercentageChanged()), SLOT(onAthleteLogChanged()));
	connect(athleteLogWidget, SIGNAL(restingHeartRateChanged()), SLOT(onAthleteLogChanged()));
	connect(athleteLogWidget, SIGNAL(maxHeartRateChanged()), SLOT(onAthleteLogChanged()));
	connect(athleteLogWidget, SIGNAL(bloodPressureSystolicChanged()), SLOT(onAthleteLogChanged()));
	connect(athleteLogWidget, SIGNAL(bloodPressureDiastolicChanged()), SLOT(onAthleteLogChanged()));
	connect(athleteLogWidget, SIGNAL(energyConsumptionChanged()), SLOT(onAthleteLogChanged()));
	connect(athleteLogWidget, SIGNAL(sleepDurationChanged()), SLOT(onAthleteLogChanged()));
	connect(athleteLogWidget, SIGNAL(notesChanged()), SLOT(onAthleteLogChanged()));
	connect(athleteLogWidget, SIGNAL(photoChanged()), SLOT(onAthleteLogChanged()));

	connect(athleteLogWidget, SIGNAL(athleteLogChanged()), SLOT(enableActions()));

	connect(profileDockWidget, SIGNAL(photoClicked()), SLOT(onEditProfile()));
}

void MainWindow::loadConfig()
{
	activityWidget->loadConfig();
	athleteLogWidget->loadConfig();

#if !defined(HAVE_KDE)
	actionCollection()->action("options_show_statusbar")->setChecked(Veneer::Settings::self()->showStatusbar());
	onShowStatusbar();
#endif
}

void MainWindow::saveConfig() const
{
	activityWidget->saveConfig();
	athleteLogWidget->saveConfig();

	Veneer::Settings::self()->setLastProfile((m_Profile && !m_Profile->isNew()) ? m_Profile->name() : "");

	Veneer::Settings::self()->writeConfig();
}

void MainWindow::enableActions()
{
	actionCollection()->action("file_save")->setEnabled(m_Profile && (m_Profile->isDirty() || m_Profile->isNew()));
	actionCollection()->action("editProfile")->setEnabled(m_Profile);
	actionCollection()->action("file_close")->setEnabled(m_Profile);
	actionCollection()->action("deleteProfile")->setEnabled(m_Profile);

	const bool canDelete = (tabWidget->currentIndex() == 0 && activityWidget->selectedActivities().size() > 0) || (tabWidget->currentIndex() == 1 && athleteLogWidget->selectedAthleteLogs().size() > 0);
	actionCollection()->action("delete")->setEnabled(canDelete);
	actionCollection()->action("import")->setEnabled(m_Profile);
	actionCollection()->action("export")->setEnabled(tabWidget->currentIndex() == 0 && activityWidget->selectedActivities().size() > 0);

	if (actionCollection()->action("sync"))
		actionCollection()->action("sync")->setEnabled(m_Profile);

	actionCollection()->action("googleEarth")->setEnabled(tabWidget->currentIndex() == 0 && activityWidget->selectedActivities().size() > 0);
}

void MainWindow::onShowStatusbar()
{
	bool b = actionCollection()->action("options_show_statusbar")->isChecked();
	Ui::MainWindow::statusBar->setVisible(b);
	Veneer::Settings::self()->setShowStatusbar(b);
}

void MainWindow::onConfigureOptions()
{
	QPointer<ConfigureOptionsDialog> dlg = new ConfigureOptionsDialog(this);

	if (dlg->exec() == QDialog::Accepted)
	{
		// force update of values if athlete log is visible
		if (athleteLogWidget->isVisible())
			athleteLogWidget->setProfile(m_Profile);

		if (activityWidget->isVisible())
			activityWidget->updateDetails();
	}

	delete dlg;
}

void MainWindow::onImport()
{
	if (m_Profile == NULL)
		return;

	const QStringList formats = Importer::supportedFileFormats();

	QString patterns;
	foreach(const QString& s, formats)
		patterns += s.split('|')[0] + ' ';

	patterns = patterns.simplified();

	const QString openFormats = tr("%1|Supported file formats (%1)\n").arg(patterns) +
			formats.join("\n") + '\n' +
			tr("*.*|All files (*.*)");

	const QStringList fileNames = Veneer::getOpenFileNames(this, "importFile", openFormats);

	if (fileNames.isEmpty())
		return;

	m_ImportProgressDialog = new ImportProgressDialog(this);

	m_Importer = new Importer(fileNames);
	connect(m_Importer, SIGNAL(finished()), SLOT(onImportFinished()));
	connect(m_Importer, SIGNAL(totalSteps(int)), m_ImportProgressDialog, SLOT(setMaximum(int)));
	connect(m_Importer, SIGNAL(progress(int)), m_ImportProgressDialog, SLOT(setProgress(int)));
	m_Importer->start();
}

void MainWindow::setProfile(Profile* p)
{
	delete m_Profile;
	m_Profile = p;

	activityWidget->setProfile(m_Profile);
	athleteLogWidget->setProfile(m_Profile);

	calendarDockWidget->updateActivities(m_Profile);
	profileDockWidget->updateProfile(m_Profile);
	categoriesDockWidget->updateCategoryTree(m_Profile);

	enableActions();
}

void MainWindow::onSaveProfile()
{
	Q_ASSERT(m_Profile);

	if (m_Profile == NULL)
		return;

	QSqlDatabase::database().transaction();

	if (!m_Profile->serialize())
	{
		QSqlDatabase::database().rollback();
		Veneer::messageBoxError(this, tr("Could not save profile to database."), tr("Error Saving Profile"));
		// TODO: better error reporting
		return;
	}

	QMainWindow::statusBar()->showMessage(tr("Profile successfully saved"));

	QSqlDatabase::database().commit();

	m_Profile->setDirty(false);
	enableActions();
}

void MainWindow::onNewProfile()
{
	if (m_Profile != NULL && m_Profile->isDirty())
	{
		switch (Veneer::messageBoxQuestionYesNo(this,
			tr("There are unsaved changes to your current profile.<br><br>Do you want to save the current profile before creating a new one?"),
			tr("Save Changes to Profile?"),
			tr("Yes, Save Changes"),
			tr("No, Do Not Save Changes"),
			"saveBeforeNewProfile"))
		{
			case QMessageBox::Yes:
				onSaveProfile();
				break;

			default:
				break;
		}

	}

	Profile* newProfile = new Profile();

	QPointer<EditProfileDialog> dlg = new EditProfileDialog(this, *newProfile);

	if (dlg->exec() == QDialog::Accepted)
		setProfile(newProfile);
	else
		delete newProfile;

	delete dlg;
}

bool MainWindow::loadLastProfile()
{
	if (Veneer::Settings::self()->lastProfile().isEmpty())
		return false;

	return loadProfile(Veneer::Settings::self()->lastProfile());
}

bool MainWindow::loadProfile(const QString& name)
{
	Profile* loadedProfile = NULL;

	if (!(loadedProfile = Profile::unserialize(name)))
	{
		Veneer::messageBoxError(this, tr("Could not load profile \"%1\" from database.").arg(name), tr("Error Opening Profile"));
		return false;
	}

	setProfile(loadedProfile);
	return true;
}

void MainWindow::onOpenProfile()
{
	if (m_Profile != NULL && m_Profile->isDirty())
	{
		switch (Veneer::messageBoxQuestionYesNo(this,
			tr("There are unsaved changes to your current profile.<br><br>Do you want to save the current profile before opening another one?"),
			tr("Save Changes to Profile?"),
			tr("Save Changes"),
			tr("Do Not Save Changes"),
			"saveBeforeOpenProfile"))
		{
			case QMessageBox::Yes:
				onSaveProfile();
				break;

			case QMessageBox::No:
				return;

			default:
				break;
		}
	}

	QPointer<ChooseProfileDialog> dlg = new ChooseProfileDialog(this);

	if (dlg->exec() == QDialog::Accepted && !dlg->profileName().isEmpty())
		loadProfile(dlg->profileName());

	delete dlg;
}

void MainWindow::onCloseProfile()
{
	if (m_Profile != NULL && m_Profile->isDirty())
	{
		switch (Veneer::messageBoxQuestionYesNo(this,
			tr("There are unsaved changes to your current profile.<br><br>Do you want to save the current profile before closing it?"),
			tr("Save Changes to Profile?"),
			tr("Save Changes"),
			tr("Do Not Save Changes"),
			"saveBeforeCloseProfile"))
		{
			case QMessageBox::Yes:
				onSaveProfile();
				break;

			case QMessageBox::No:
				return;

			default:
				break;
		}
	}

	setProfile(NULL);
}

void MainWindow::onDeleteProfile()
{
	Q_ASSERT(m_Profile != NULL);

	if (m_Profile == NULL)
		return;

	if (Veneer::messageBoxQuestionYesNo(this,
			tr("You are about to permanently delete the currently loaded profile for %1.<br><br><b>There is no way to recover your profile if you delete it now.</b>").arg(m_Profile->name()),
			tr("Really Delete Profile?"),
			tr("Delete Profile"),
			tr("Keep Profile"),
			"reallyDeleteProfile") == QMessageBox::No)
		return;

	if (!m_Profile->remove())
	{
		Veneer::messageBoxError(this, tr("Could not delete profile from database."), tr("Error Deleting Profile"));
		// TODO: better error reporting
		return;
	}

	setProfile(NULL);
}

void MainWindow::onEditProfile()
{
	Q_ASSERT(m_Profile != NULL);

	if (m_Profile == NULL)
		return;

	QPointer<EditProfileDialog> dlg = new EditProfileDialog(this, *m_Profile);

	if (dlg->exec() == QDialog::Accepted)
	{
		m_Profile->setDirty(true);
		profileDockWidget->updateProfile(m_Profile);;

		activityWidget->updateCategories(m_Profile->categories());
		activityWidget->updateEquipments(m_Profile->equipments());
		activityWidget->updateDetails();

		categoriesDockWidget->updateCategoryTree(m_Profile);
		enableActions();
	}

	delete dlg;
}

void MainWindow::onDelete()
{
	Q_ASSERT(m_Profile);

	if (m_Profile == NULL)
		return;

	switch(tabWidget->currentIndex())
	{
		case 0:
			if (!activityWidget->onDeleteActivity())
				return;
			break;

		case 1:
			if (!athleteLogWidget->on_removeAthleteLogButton_clicked(false))
				return;
			break;
	}

	m_Profile->setDirty(true);

	enableActions();
}

void MainWindow::onSelectAll()
{
	Q_ASSERT(m_Profile);

	if (m_Profile == NULL)
		return;

	switch(tabWidget->currentIndex())
	{
		case 0:
			activityWidget->onSelectAll();
			break;

		case 1:
			athleteLogWidget->onSelectAll();
			break;

		default:
			return;
	}

	enableActions();
}

void MainWindow::on_activityWidget_selectionChanged()
{
	Activity* a = activityWidget->selectedActivities().size() == 1 ? activityWidget->selectedActivities()[0] : NULL;

	graphDockWidget->setActivity(a);
	mapDockWidget->setActivity(a);
	calendarDockWidget->calendarWidget->setSelectedDate(a ? a->timeStamp().date() : QDate::currentDate());
	enableActions();
}

void MainWindow::on_categoriesDockWidget_selectedCategoryChanged(Category* category)
{
	activityWidget->showActivitiesForCategory(category);
	enableActions();
}

void MainWindow::on_athleteLogWidget_selectedAthleteLogChanged(AthleteLog* )
{
	enableActions();
}

void MainWindow::onDetailsChanged()
{
	Q_ASSERT(m_Profile != NULL);

	if (m_Profile == NULL)
		return;

	m_Profile->setDirty(true);
	enableActions();
}

void MainWindow::on_activityWidget_categoryChanged()
{
	onDetailsChanged();
	activityWidget->showActivitiesForCategory(categoriesDockWidget->selectedCategory());
}

void MainWindow::onAthleteLogChanged()
{
	Q_ASSERT(m_Profile != NULL);

	if (m_Profile == NULL)
		return;

	m_Profile->setDirty(true);
	enableActions();
}

void MainWindow::on_graphDock_visibilityChanged(bool b)
{
	if (b)
		graphDockWidget->setActivity(activityWidget->selectedActivities().size() == 1 ? activityWidget->selectedActivities()[0] : NULL);
}

void MainWindow::on_mapDock_visibilityChanged(bool b)
{
	if (b)
		mapDockWidget->setActivity(activityWidget->selectedActivities().size() == 1 ? activityWidget->selectedActivities()[0] : NULL);
}

void MainWindow::on_tabWidget_currentChanged(int idx)
{
	switch(idx)
	{
		case 0:
			activityWidget->updateDetails();
			break;

		case 1:
			break;
	}

	enableActions();
}

void MainWindow::onExport()
{
	const QStringList formats = Exporter::supportedFileFormats();

	const QString fileName = activityWidget->selectedActivities().count() == 1 ? activityWidget->selectedActivities()[0]->name() : QString();

	const Veneer::Url url = Veneer::getSaveUrl(this, "export", formats.join("\n"), fileName);

	if (!url.isEmpty())
	{
		QTemporaryFile* tempFile = new QTemporaryFile();

		if (!tempFile->open())
		{
			Veneer::messageBoxError(this, tr("Could not create temporary output file to save %1.").arg(url.fileName()), tr("Export Error"));
			return;
		}

		Activities exportActivities;
		exportActivities << activityWidget->selectedActivities();

		Q_ASSERT(m_Exporter == NULL);

		m_Exporter = new Exporter(url, tempFile,  exportActivities);
		connect(m_Exporter, SIGNAL(finished()), SLOT(onExportFinished()));
		m_Exporter->start();
	}
}

void MainWindow::onSync()
{
#if !defined(HAVE_SYNC_SUPPORT)
	qWarning() << "MainWindow::onSync() called without sync support.";
	return;
#else
	if (m_Profile == NULL)
		return;

	Q_ASSERT(m_Sync == NULL);

	m_Sync = new Sync();

	QPointer<SyncSourceDialog> dlg = new SyncSourceDialog(this, m_Sync);

	if (dlg->exec() == QDialog::Accepted)
	{
		SyncSource* source = m_Sync->sources()[dlg->selectedRow()];
		m_Sync->setActiveSource(source);

		m_ImportProgressDialog = new ImportProgressDialog(this);

		connect(m_Sync, SIGNAL(finished()), SLOT(onSyncFinished()));
		connect(m_Sync, SIGNAL(totalSteps(int)), m_ImportProgressDialog, SLOT(setMaximum(int)));
		connect(m_Sync, SIGNAL(progress(int)), m_ImportProgressDialog, SLOT(setValue(int)));
		m_Sync->start();
	}

	delete dlg;
#endif
}

void MainWindow::doImport(Activities& importedActivities, const QStringList& errorNames)
{
	Q_ASSERT(m_Profile);

	if (m_Profile == NULL)
		return;

	if (errorNames.size() > 0)
	{
		if (Veneer::messageBoxQuestionYesNoList(this,
				tr("Not all files you selected could be read. Do you wish to continue the import anyway?<br><br>These files could not be read:"),
				errorNames,
				tr("Continue With Import?"),
				tr("Continue with import"),
				tr("Cancel import"),
				"continueImportAfterErrors"
			) == QMessageBox::No)
		{
			return;
		}
	}

	// make sure all activites at least have the top category as category.
	m_Profile->setValidCategory(importedActivities);
	m_Profile->calcEnergyConsumption(importedActivities);
	Activity::calcTrackPointSpeed(importedActivities);
	Activity::calcLapTotalAscentAndDescent(importedActivities);
	m_Profile->setValidStartTime(importedActivities);
	m_Profile->setValidName(importedActivities);

	QPointer<ImportDialog> dlg = new ImportDialog(this);
	dlg->setProfileAndActivities(m_Profile, &importedActivities);

	if (dlg->exec() == QDialog::Accepted)
	{
		m_Profile->activities().append(importedActivities);
		importedActivities.clear();

		// this is just so that saving is enabled, it's actually a hack
		m_Profile->setDirty(true);
		enableActions();
		categoriesDockWidget->updateCategoryTree(m_Profile);
	}

	delete dlg;
}

void MainWindow::onSyncFinished()
{
	m_ImportProgressDialog->deleteLater();
	m_ImportProgressDialog = NULL;

	if (m_Sync->status() != Sync::Success)
	{
		Veneer::messageBoxError(this, tr("Could not synchronize with %1: %2").arg(m_Sync->activeSource()->name()).arg(Sync::errorMsg(m_Sync->status())), tr("Sync Error"));
		delete m_Sync;
		m_Sync = NULL;
		return;
	}

	// FIXME. yuck, O^2 searching
	Activities::iterator i = m_Sync->activities().begin();

	while (i != m_Sync->activities().end())
	{
		if (m_Profile->hasActivity(**i))
		{
			delete *i;
			i = m_Sync->activities().erase(i);
		}
		else
			i++;
	}

	if (m_Sync->activities().isEmpty())
		Veneer::messageBoxInformation(this, tr("No new activities found."), tr("No New Activities"));
	else
		doImport(m_Sync->activities(), m_Sync->errorNames());

	delete m_Sync;
	m_Sync = NULL;
}

void MainWindow::onImportFinished()
{
	m_ImportProgressDialog->deleteLater();
	m_ImportProgressDialog = NULL;

	if (m_Importer->activities().isEmpty())
		Veneer::messageBoxInformation(this, tr("No activities found that can be imported."), tr("No Activities"));
	else
		doImport(m_Importer->activities(), m_Importer->errorFileNames());

	delete m_Importer;
	m_Importer = NULL;
}

void MainWindow::onExportFinished()
{
	if (m_Exporter->status() == Exporter::Success)
		Veneer::copyFile(this, m_Exporter->file().fileName(), m_Exporter->targetUrl().url());
	else
		// TODO: that's way too little...
		Veneer::messageBoxError(this, tr("Export error."), tr("Export failed."));

	delete m_Exporter;
	m_Exporter = NULL;
}

void MainWindow::onGoogleEarth()
{
	const QString tempFileName = QString("%1%2workout-google-earth-%3.kml").arg(QDir::tempPath()).arg(QDir::separator()).arg(QCoreApplication::applicationPid());
	QFile tempFile(tempFileName);

	if (!tempFile.open(QIODevice::WriteOnly|QIODevice::Truncate))
	{
		Veneer::messageBoxError(this, tr("Could not create temporary output file %1 for export to Google Earth.").arg(tempFileName), tr("Export Error"));
		return;
	}

	QXmlStreamWriter xml(&tempFile);

	Activities googleEarthActivities;
	googleEarthActivities << activityWidget->selectedActivities();
	ExporterInterface* e = new KmlExporter();
	if (!e->run(xml, googleEarthActivities))
	{
		Veneer::messageBoxError(this, tr("Could not create KML file %1 for export to Google Earth.").arg(tempFileName), tr("Export Error"));
		return;
	}

#if defined(Q_OS_WIN)
	// set it to the default value in case reading the registry goes wrong...
	QString googleEarthInstallPath = qgetenv("PROGRAMFILES") + "/Google/Google Earth/";

	HKEY handle;
	DWORD result = RegOpenKeyEx(HKEY_CURRENT_USER, "Software\\Google\\Google Earth Plus", 0, KEY_READ, &handle);

	if (result != ERROR_SUCCESS)
		qWarning() << "Could not read Google Earth install path from registry -- failed to open registry key: " << result;
	else
	{
		DWORD valueSize = 16384;
		char value[16384];

		result = RegQueryValueEx(handle, "InstallLocation", 0, NULL, (LPBYTE) value, &valueSize);

		if (result == ERROR_SUCCESS)
			googleEarthInstallPath = QString(value);
		else
			qWarning() << "Could not read Google Earth install path from registry -- could not read value: " << result;

		RegCloseKey(handle);
	}

	QString googleEarth = googleEarthInstallPath + QDir::separator() + "Client" + QDir::separator() + "googleearth.exe";
#else
	QString googleEarth = "googleearth";
#endif

	if (QProcess::startDetached(googleEarth, QStringList() << tempFileName) == 0)
	{
		tempFile.remove();
		Veneer::messageBoxError(this, tr("Google Earth could not be run. Make sure it is installed and working properly."), tr("Error Running Google Earth"));
	}
}

#if !defined(HAVE_KDE)
void MainWindow::onAbout()
{
	QDialog* dlg = NULL;

	if (!(dlg = findChild<AboutDialog*>()))
		dlg = new AboutDialog(this);

	dlg->show();
}
#endif

void MainWindow::on_activityWidget_contextMenuRequested(const QPoint& p)
{
	Veneer::ContextMenu* menu = new Veneer::ContextMenu();

	menu->addAction(actionCollection()->action("delete"));
	menu->addAction(actionCollection()->action("export"));
	menu->addSeparator();
	menu->addAction(actionCollection()->action("googleEarth"));

	if (menu)
		menu->exec(p);
}

void MainWindow::on_athleteLogWidget_contextMenuRequested(const QPoint& p)
{
	Veneer::ContextMenu* menu = new Veneer::ContextMenu();

	menu->addAction(actionCollection()->action("delete"));

	if (menu)
		menu->exec(p);
}

