#include "emainwindow.h"
#include <QFileDialog>
#include <QMessageBox>
#include <QMenu>
#include <QLabel>


EMainWindow::EMainWindow(QWidget * parent): QMainWindow(parent), action_show_all_tools(this), action_hide_all_tools(this),
action_show_all_docks(this), action_hide_all_docks(this), first_show(true) {
	tid = 0;
	menu_recent = 0;
	action_clear_recent = new QAction(QIcon(":/icons/edit-clear.png"), tr("Clear recent list"), this);
	connect(action_clear_recent, SIGNAL(triggered()), this, SLOT(clearRecent()));
	qRegisterMetaType<Qt::DockWidgetArea>("Qt::DockWidgetArea");
	action_show_all_tools.setText(tr("Show all"));
	action_show_all_docks.setText(tr("Show all"));
	action_hide_all_tools.setText(tr("Hide all"));
	action_hide_all_docks.setText(tr("Hide all"));
	action_show_all_tools.setIcon(QIcon(":/icons/layer-visible-on.png"));
	action_show_all_docks.setIcon(QIcon(":/icons/layer-visible-on.png"));
	action_hide_all_tools.setIcon(QIcon(":/icons/layer-visible-off.png"));
	action_hide_all_docks.setIcon(QIcon(":/icons/layer-visible-off.png"));
	max_recent = 8;
	setChanged(false);
	initMenus();
	initSession();
	tid = startTimer(200); // bad timer, too much overload
}


EMainWindow::~EMainWindow() {
	if (tid > 0) killTimer(tid);
	tid = 0;
}


void EMainWindow::setRecentFiles(const QStringList & rl) {
	clearRecent();
	for (int i = rl.size() - 1; i >= 0; --i)
		addToRecent(rl[i]);
}


QStringList EMainWindow::recentFiles() const {
	QStringList ret;
	foreach (QAction * a, actions_recent)
		ret << a->data().toString();
	return ret;
}


void EMainWindow::setRecentMenu(QMenu * m) {
	menu_recent = m;
	prepareRecent();
}


void EMainWindow::showEvent(QShowEvent * e) {
	QWidget::showEvent(e);
	initMenus();
	if (!first_show) return;
	first_show = false;
	QList<QDockWidget * > docks(findChildren<QDockWidget * >());
	foreach (QDockWidget * d, docks) {
		connect(d, SIGNAL(dockLocationChanged(Qt::DockWidgetArea)), this, SLOT(changedDock()));
		connect(d, SIGNAL(topLevelChanged(bool)), this, SLOT(changedDock()));
	}
	changedDock();
}


void EMainWindow::closeEvent(QCloseEvent * e) {
	if (!checkSave()) e->ignore();
	else saveSession();
}


bool EMainWindow::eventFilter(QObject * o, QEvent * e) {
	//qDebug() << o << e;
	if (tbars.contains((QTabBar*)o) && e->type() == QEvent::MouseButtonDblClick) {
		int tab = ((QTabBar*)o)->tabAt(((QMouseEvent * )e)->pos());
		if (tab >= 0) {
			QDockWidget * dock = (QDockWidget * )(((QTabBar*)o)->tabData(tab).toULongLong());
			if (dock != 0) {
				dock->setFloating(true);
			}
		}
	}
	if (e->type() == QEvent::Show || e->type() == QEvent::Hide || e->type() == QEvent::ChildAdded || e->type() == QEvent::ChildRemoved || e->type() == QEvent::MouseButtonPress) {
		if (tbars.contains((QTabBar*)o) || tdocks.contains((QDockWidget*)o)) {
			if (e->type() == QEvent::MouseButtonPress) {
				if (((QMouseEvent*)e)->button() == Qt::RightButton) {
					bool popup = tbars.contains((QTabBar*)o);
					if (tdocks.contains((QDockWidget*)o))
						popup = popup || ((QDockWidget*)o)->titleBarWidget()->geometry().contains(((QMouseEvent*)e)->pos());
					if (popup) {
						createPopupMenu()->popup(
#if QT_VERSION_MAJOR <= 5
										((QMouseEvent*)e)->globalPos()
#else
										((QMouseEvent*)e)->globalPosition().toPoint()
#endif
											);
						return true;
					}
				}
			}
			if (e->type() == QEvent::Show || e->type() == QEvent::Hide /*|| e->type() == QEvent::ChildAdded || e->type() == QEvent::ChildRemoved*/) {
				//qDebug() << "filter";
				//QMetaObject::invokeMethod(this, "changedDock", Qt::QueuedConnection);
				changedDock();
			}
		}
	}
	return QMainWindow::eventFilter(o, e);
}


void EMainWindow::timerEvent(QTimerEvent * e) {
	if (e->timerId() == tid) {
		changedDock();
		return;
	}
	QMainWindow::timerEvent(e);
}


void EMainWindow::changeEvent(QEvent * e) {
	QMainWindow::changeEvent(e);
	switch (e->type()) {
		case QEvent::LanguageChange:
			action_show_all_tools.setText(tr("Show all"));
			action_show_all_docks.setText(tr("Show all"));
			action_hide_all_tools.setText(tr("Hide all"));
			action_hide_all_docks.setText(tr("Hide all"));
			action_clear_recent->setText(tr("Clear recent list"));
			break;
		default:
			break;
	}
}


QMenu * EMainWindow::createPopupMenu() {
	QMenu * menuPopup = new QMenu;
	QWidgetAction * wa;
	QLabel * lbl;
	QAction * a;
	QFont f;
	f.setBold(true);
	// Toolbars
	QList<QToolBar * > tools = findChildren<QToolBar * >();
	if (!tools.isEmpty()) {
		wa = new QWidgetAction(menuPopup);
		lbl = new QLabel();
		lbl->setFrameShape(QFrame::StyledPanel);
		lbl->setFrameShadow(QFrame::Sunken);
		lbl->setText(tr("Toolbars"));
		lbl->setFont(f);
		lbl->setAlignment(Qt::AlignCenter);
		wa->setDefaultWidget(lbl);
		menuPopup->addAction(wa);
		foreach (QToolBar * i, tools) {
			if (i->property("ribbon").toBool()) continue;
			a = new QAction(i->windowTitle(), menuPopup);
			a->setCheckable(true);
			a->setChecked(!i->isHidden());
			a->setIcon(i->windowIcon());
			connect(a, SIGNAL(toggled(bool)), i, SLOT(setVisible(bool)));
			menuPopup->addAction(a);
		}
		menuPopup->addSeparator();
		menuPopup->addAction(&action_show_all_tools);
		menuPopup->addAction(&action_hide_all_tools);
	}
	// Docks
	QList<QDockWidget * > docks = findChildren<QDockWidget * >();
	if (!docks.isEmpty()) {
		wa = new QWidgetAction(menuPopup);
		lbl = new QLabel();
		lbl->setFrameShape(QFrame::StyledPanel);
		lbl->setFrameShadow(QFrame::Sunken);
		lbl->setText(tr("Docks"));
		lbl->setFont(f);
		lbl->setAlignment(Qt::AlignCenter);
		wa->setDefaultWidget(lbl);
		menuPopup->addAction(wa);
		foreach (QDockWidget * i, docks) {
			if (i->property("ribbon").toBool()) continue;
			a = new QAction(i->windowTitle(), menuPopup);
			a->setCheckable(true);
			a->setChecked(!i->isHidden());
			a->setIcon(i->windowIcon());
			connect(a, SIGNAL(toggled(bool)), i, SLOT(setVisible(bool)));
			menuPopup->addAction(a);
		}
		menuPopup->addSeparator();
		menuPopup->addAction(&action_show_all_docks);
		menuPopup->addAction(&action_hide_all_docks);
	}
	return menuPopup;
}


void EMainWindow::addToRecent(const QString & path) {
	if (path.isEmpty()) return;
	QFileInfo fi(path);
	QString fp = fi.absoluteFilePath();
	bool insert = true;
	for (int i = 0; i < actions_recent.size(); ++i)
		if (actions_recent[i]->data().toString() == fp) {
			actions_recent.push_front(actions_recent.takeAt(i));
			insert = false;
			prepareRecent();
			break;
		}
	if (!insert) return;
	QAction * a = new QAction(this);
	a->setData(fp);
	connect(a, SIGNAL(triggered()), this, SLOT(recentTriggered()));
	actions_recent.push_front(a);
	while (actions_recent.size() > max_recent)
		delete actions_recent.takeLast();
	prepareRecent();
}


void EMainWindow::prepareRecent() {
	for (int i = 0; i < actions_recent.size(); ++i) {
		QAction * a = actions_recent[i];
		a->setText(QString("&%1 - %2").arg(i + 1).arg(a->data().toString()));
	}
	if (!menu_recent) return;
	menu_recent->clear();
	menu_recent->addActions(actions_recent);
	menu_recent->addSeparator();
	menu_recent->addAction(action_clear_recent);
}


void EMainWindow::initMenus() {
	action_show_all_tools.disconnect();
	action_hide_all_tools.disconnect();
	action_show_all_docks.disconnect();
	action_hide_all_docks.disconnect();

	QList<QToolBar * > tools = findChildren<QToolBar * >();
	foreach (QToolBar * i, tools) {
		if (i->property("ribbon").toBool()) continue;
		i->toggleViewAction()->setIcon(i->windowIcon());
		connect(&action_show_all_tools, SIGNAL(triggered(bool)), i, SLOT(show()));
		connect(&action_hide_all_tools, SIGNAL(triggered(bool)), i, SLOT(hide()));
	}

	QList<QDockWidget * > docks = findChildren<QDockWidget * >();
	foreach (QDockWidget * i, docks) {
		if (i->property("ribbon").toBool()) continue;
		i->toggleViewAction()->setIcon(i->windowIcon());
		connect(&action_show_all_docks, SIGNAL(triggered(bool)), i, SLOT(show()));
		connect(&action_hide_all_docks, SIGNAL(triggered(bool)), i, SLOT(hide()));
	}

	QList<QAction * > actions = findChildren<QAction * >();
	foreach (QAction * i, actions)
		i->setIconVisibleInMenu(true);
	addActions(actions);
}


void EMainWindow::initSession() {
	connect(&session, SIGNAL(loading(QPIConfig & )), this, SLOT(sessionLoading(QPIConfig & )));
	connect(&session, SIGNAL(saving(QPIConfig & )), this, SLOT(sessionSaving(QPIConfig & )));
}


void EMainWindow::saveSession() {
	session.save();
}


void EMainWindow::loadSession() {
	session.load();
}


bool EMainWindow::checkSave() {
	if (!isWindowModified()) return true;
	return saveFile(true);
}


void EMainWindow::changedDock() {
	if (isHidden()) return;
	QList<QTabBar * > tabs(findChildren<QTabBar * >());
	QList<QDockWidget * > docks = findChildren<QDockWidget * >();
//	QSet<QDockWidget * > docks_tabs;
	QDockWidget * dock;
//	qDebug() << "### change";
	foreach (QTabBar * t, tabs) {
		if (!t->objectName().isEmpty() || t->isHidden()) continue;
		if (!tbars.contains(t)) {
			tbars << t;
			connect(t, SIGNAL(tabCloseRequested(int)), this, SLOT(closeDock(int)));
			t->installEventFilter(this);
#ifndef Q_OS_MACOS
			t->setIconSize(dockTabsIconSize());
#endif
			t->setTabsClosable(true);
		}
//		qDebug() << "tab" << t << t->count();
		for (int i = 0; i < t->count(); ++i) {
			dock = (QDockWidget * )t->tabData(i).toULongLong();
			//qDebug() << i << t->tabData(i);
			if (!docks.contains(dock)) continue;
#ifndef Q_OS_MACOS
			t->setIconSize(dockTabsIconSize());
#endif
			t->setTabIcon(i, dock->windowIcon());
//			docks_tabs << dock;
		}
	}

	foreach (QDockWidget * d, docks) {
		if (d->titleBarWidget() == 0) continue;
		QWidget * ctb = d->titleBarWidget();
		if (!d->property("__titleWidget").isValid()) {
			d->setProperty("__titleWidget", qulonglong(ctb));
			QWidget * ntb = new QWidget();
			int m = style()->pixelMetric(QStyle::PM_DockWidgetTitleMargin);
			ntb->setLayout(new QBoxLayout(QBoxLayout::BottomToTop));
			ntb->layout()->setContentsMargins(m, m, 0, 0);
			d->setProperty("__titleEmptyWidget", qulonglong(ntb));
		}
		if (!tdocks.contains(d)) {
			tdocks << d;
//			qDebug() << "connect" << d;
//			connect(d, SIGNAL(destroyed(QObject*)), this, SLOT(changedDockClose(QObject*)), Qt::UniqueConnection);
			d->installEventFilter(this);
		}
		//d->titleBarWidget()->setHidden(docks_tabs.contains(d));
		if (tabifiedDockWidgets(d).isEmpty()) {
			if (d->titleBarWidget() != (QWidget * )(d->property("__titleWidget").toULongLong()))
				d->setTitleBarWidget((QWidget * )(d->property("__titleWidget").toULongLong()));
		} else {
			if (d->titleBarWidget() != (QWidget * )(d->property("__titleEmptyWidget").toULongLong())) {
				d->setTitleBarWidget((QWidget * )(d->property("__titleEmptyWidget").toULongLong()));
				d->layout()->setContentsMargins(0, 20, 0, 0);
			}
		}
	}
}


//void EMainWindow::changedDockClose(QObject * dock) {
//	qDebug() << "changedDockClose" << dock;
//	if (!dock) return;
//	foreach (QTabBar * t, tbars) {
//		for (int i = 0; i < t->count(); ++i)
//			if (t->tabData(i).toULongLong() == (qulonglong)dock) {
//				t->removeTab(i);
//				break;
//			}
//	}
//}


void EMainWindow::closeDock(int index) {
	QDockWidget * dock = (QDockWidget * )((QTabBar*)sender())->tabData(index).toULongLong();
	if (dock == 0) return;
	dock->close();
}


void EMainWindow::recentTriggered() {
	QAction * a = qobject_cast<QAction * >(sender());
	if (!a) return;
	QString path = a->data().toString();
	if (path.isEmpty()) return;
	if (!checkSave()) return;
	if (load(path))
		addToRecent(path);
}


void EMainWindow::setMaxRecentItems(int mr) {
	max_recent = qMax(0, mr);
	for (int i = actions_recent.size() - 1; i >= mr; --i)
		delete actions_recent.takeLast();
}


void EMainWindow::newFile() {
	if (!checkSave()) return;
	reset(true);
	setWindowModified(false);
}


void EMainWindow::openFile() {
	if (!checkSave()) return;
	QString ret = QFileDialog::getOpenFileName(this, tr("Select file to open"), file_name, loadFilter());
	if (ret.isEmpty()) return;
	if (load(ret))
		addToRecent(ret);
}


void EMainWindow::openFiles() {
	if (!checkSave()) return;
	QStringList ret = QFileDialog::getOpenFileNames(this, tr("Select files to open"), file_name, loadFilter());
	foreach (QString s, ret) {
		if (load(s))
			addToRecent(s);
	}
}


bool EMainWindow::saveFile(bool ask) {
	if (ask) {
		int ret = QMessageBox::question(this, windowTitle(), tr("Save changes%1?").arg(!file_name.isEmpty() ? (tr(" in") + " \"" + file_name + "\"") : ""), QMessageBox::Save | QMessageBox::Discard | QMessageBox::Cancel, QMessageBox::Save);
		if (ret == QMessageBox::Cancel) return false;
		if (ret == QMessageBox::Save) return saveFile();
		return true;
	}
	if (file_name.isEmpty()) return saveAsFile();
	if (save(file_name))
		addToRecent(file_name);
	return true;
}


bool EMainWindow::saveAsFile() {
	QString ret = QFileDialog::getSaveFileName(this, tr("Select file to save"), file_name, saveFilter());
	if (ret.isEmpty()) return false;
	if (save(ret))
		addToRecent(ret);
	return true;
}


void EMainWindow::clearRecent() {
	qDeleteAll(actions_recent);
	actions_recent.clear();
	prepareRecent();
}
