#include "emainwindow.h"

#include <QFileDialog>
#include <QLabel>
#include <QMenu>
#include <QMessageBox>
#include <edockwidget.h>


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) {
	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();
}


EMainWindow::~EMainWindow() {}


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();
	initDocks();
}


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


bool EMainWindow::eventFilter(QObject * o, QEvent * 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) {
				dock->setFloating(true);
				return true;
			}
		}
	}
	/*if (e->type() == QEvent::Show || e->type() == QEvent::Hide) {
	    if (tdocks.contains((QDockWidget*)o)) {
	        changedDock();
	    }
	}*/
	if (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;
					}
				}
			}
		}
	}
	return QMainWindow::eventFilter(o, 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);
		for (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);
		for (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 *>();
	for (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 *>();
	for (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 *>();
	for (QAction * i: actions)
		i->setIconVisibleInMenu(true);
	addActions(actions);
}


void EMainWindow::initDocks() {
	QList<QDockWidget *> docks(findChildren<QDockWidget *>());
	for (QDockWidget * d: docks) {
		static const char * pname = "__edock_inited__";
		if (d->property(pname).toBool()) continue;
		d->setProperty(pname, true);
		connect(d, SIGNAL(dockLocationChanged(Qt::DockWidgetArea)), this, SLOT(changedDock()));
		connect(d, SIGNAL(topLevelChanged(bool)), this, SLOT(changedDock()));
		connect(d, SIGNAL(visibilityChanged(bool)), this, SLOT(changedDock()));
	}
	changedDock();
}


void EMainWindow::initSession() {
	connect(&session, &SessionManager::loading, this, [this](QPIConfig & conf) { loadingSession(conf); });
	connect(&session, &SessionManager::saving, this, [this](QPIConfig & conf) { savingSession(conf); });
}


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


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


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


void EMainWindow::setChanged(bool yes) {
	is_changed = yes;
	setWindowModified(yes);
}


void EMainWindow::changedDock() {
	if (isHidden()) return;
	QSet<QDockWidget *> invalid_tab_docks;
	QList<QTabBar *> tabs(findChildren<QTabBar *>());
	QList<QDockWidget *> docks = findChildren<QDockWidget *>();
	QRect geom                 = rect();
	// qDebug() << "### change" << docks.size() << tabs.size();
	for (QTabBar * t: tabs) {
		if (!t->objectName().isEmpty() || t->isHidden()) continue;
		if (!tbars.contains(t)) {
			// qDebug() << "add tab" << t;
			tbars << t;
			connect(t, SIGNAL(tabCloseRequested(int)), this, SLOT(closeDock(int)));
			connect(t, &QObject::destroyed, this, [this, t]() { tbars.removeOne(t); });
			t->installEventFilter(this);
#ifndef Q_OS_MACOS
			t->setIconSize(dockTabsIconSize());
#endif
			t->setTabsClosable(true);
		}
		for (int i = 0; i < t->count(); ++i) {
			QDockWidget * dock = (QDockWidget *)t->tabData(i).toULongLong();
			// qDebug() << i << t->tabText(i) << t->isTabVisible(i) << dock;
			if (!dock) continue;
			if (!docks.contains(dock)) continue;
			if (!geom.intersects(t->geometry())) invalid_tab_docks << dock;
#ifndef Q_OS_MACOS
			QSize is = dockTabsIconSize();
			if (t->iconSize() != is) t->setIconSize(is);
#endif
			t->setTabIcon(i, dock->windowIcon());
		}
	}

	for (QDockWidget * d: docks) {
		if (!d->titleBarWidget()) continue;
		if (!tdocks.contains(d)) {
			// qDebug() << "add dock" << d;
			tdocks << d;
			connect(d, &QObject::destroyed, this, [this, d]() { tdocks.removeOne(d); });
			d->installEventFilter(this);
		}
		auto * ed = qobject_cast<EDockWidget *>(d);
		if (ed) {
			// qDebug() << d->windowTitle() << tabifiedDockWidgets(d);
			if (tabifiedDockWidgets(d).isEmpty() || invalid_tab_docks.contains(d))
				ed->setStandardHeader();
			else
				ed->setEmptyHeader();
		}
	}
}


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(file_name);
	return true;
}


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