/*
rktoolwindowbar - This file is part of RKWard (https://rkward.kde.org). Created: Fri Oct 12 2007
SPDX-FileCopyrightText: 2007-2020 by Thomas Friedrichsmeier <thomas.friedrichsmeier@kdemail.net>
SPDX-FileContributor: The RKWard Team <rkward-devel@kde.org>
SPDX-License-Identifier: GPL-2.0-or-later
*/

/* This code is based substantially on kate's katemdi! */

#include "rktoolwindowbar.h"

#include <QMenu>
#include <KLocalizedString>
#include <kparts/partmanager.h>
#include <kselectaction.h>
#include <kwidgetsaddons_version.h>

#include <QSplitter>
#include <QContextMenuEvent>
#include <QHBoxLayout>

#include "rkworkplace.h"
#include "rkworkplaceview.h"
#include "rkmdiwindow.h"
#include "../rkward.h"
#include "../misc/rkstandardicons.h"

#include "../debug.h"

#define DEFAULT_SPLITTER_SIZE 200
#define SPLITTER_MIN_SIZE 30

RKToolWindowBar::RKToolWindowBar (KMultiTabBarPosition position, QWidget *parent) : KMultiTabBar (position, parent),
	container (0) {
	RK_TRACE (APP);

	setStyle (KMultiTabBar::KDEV3ICON);
	last_known_size = SPLITTER_MIN_SIZE;
}

RKToolWindowBar::~RKToolWindowBar () {
	RK_TRACE (APP);
}

void RKToolWindowBar::captionChanged (RKMDIWindow* window) {
	RK_TRACE (APP);

	int id = widget_to_id.value (window);
	tab (id)->setText (window->shortCaption ());
}

void RKToolWindowBar::restoreSize (const KConfigGroup &cg) {
	RK_TRACE (APP);

	last_known_size = cg.readEntry (QString ("view_size_%1").arg (position ()), DEFAULT_SPLITTER_SIZE);
}

void RKToolWindowBar::saveSize (KConfigGroup &cg) const {
	RK_TRACE (APP);

	cg.writeEntry (QString ("view_size_%1").arg (position ()), last_known_size);
}

int RKToolWindowBar::getSplitterSize () const {
	RK_TRACE (APP);

	int pos = splitter->indexOf (container);
	if (pos < 0) {
		RK_ASSERT (false);
		return 0;
	}
	return (splitter->sizes().at(pos));
}

void RKToolWindowBar::setSplitterSize (int new_size) {
	RK_TRACE (APP);

	// HACK / WORKAROUND: reset the collapsed state of the container (if collapsed). Else we will not be able to open it again
	int index = splitter->indexOf (container);
	QList<int> sizes = splitter->sizes ();
	if (sizes[index] == 0) {
		sizes[index] = last_known_size;
		splitter->setSizes (sizes);
	}

	if (splitter->orientation () == Qt::Horizontal) {
		container->resize (new_size, container->height ());
	} else {
		container->resize (container->width (), new_size);
	}
}

void RKToolWindowBar::splitterMoved (int, int) {
	RK_TRACE (APP);

	int pos = getSplitterSize ();
	if (pos >= SPLITTER_MIN_SIZE) last_known_size = pos;

	if (!pos) {		// collapsed. Hide it properly.
		for (QMap<RKMDIWindow*, int>::const_iterator it = widget_to_id.constBegin (); it != widget_to_id.constEnd (); ++it) {
			if (isTabRaised (it.value ())) {
				hideWidget (it.key ());
				break;
			}
		}
	}
}

void RKToolWindowBar::setSplitter (QSplitter *splitter) {
	RK_TRACE (APP);
	RK_ASSERT (!container);

	RKToolWindowBar::splitter = splitter;
	container = new QWidget (splitter);
	new QHBoxLayout (container);
	splitter->setContentsMargins (0, 0, 0, 0);
	container->layout ()->setContentsMargins (0, 0, 0, 0);
	container->layout ()->setSpacing (0);
	container->layout ()->setMargin (0);
	container->hide ();

	connect (splitter, &QSplitter::splitterMoved, this, &RKToolWindowBar::splitterMoved);
}

void RKToolWindowBar::addWidget (RKMDIWindow *window) {
	RK_TRACE (APP);
	RK_ASSERT (window);
	if (window->tool_window_bar == this) return;	// may happen while restoring windows
	RK_ASSERT (container);

	static int id_count = 0;
	int id = ++id_count;

	if (window->tool_window_bar) {
		window->tool_window_bar->removeWidget (window);
	}

#if KWIDGETSADDONS_VERSION >= QT_VERSION_CHECK(5,13,0)
	appendTab (window->windowIcon (), id, window->shortCaption ());
#else
	appendTab (window->windowIcon ().pixmap (QSize (16, 16)), id, window->shortCaption ());
#endif

	window->tool_window_bar = this;
	widget_to_id.insert (window, id);
	connect (window, &QObject::destroyed, this, &RKToolWindowBar::windowDestroyed);

	connect (tab (id), &KMultiTabBarTab::clicked, this, &RKToolWindowBar::tabClicked);
	tab (id)->installEventFilter (this);

	if (window->isAttached ()) {
		reclaimDetached (window);
	}

	show ();
}

void RKToolWindowBar::reclaimDetached (RKMDIWindow *window) {
	RK_TRACE (APP);

	if (window->parent () == container) return;

	window->hide();
	window->setParent (container);
	container->layout ()->addWidget (window);
}

void RKToolWindowBar::windowDestroyed(QObject* window) {
	RK_TRACE (APP);

	int id = widget_to_id.take (static_cast<RKMDIWindow *> (window));
	removeTab (id);
}

void RKToolWindowBar::removeWidget (RKMDIWindow *widget) {
	RK_TRACE (APP);
	RK_ASSERT (widget_to_id.contains (widget));

	int id = widget_to_id[widget];
	bool was_active_in_bar = isTabRaised (id);

	removeTab (id);
	widget_to_id.remove (widget);
	disconnect (widget, &QObject::destroyed, this, &RKToolWindowBar::windowDestroyed);
	widget->tool_window_bar = 0;

	if (widget->isAttached ()) {
		widget->setParent (0);
		widget->hide ();
	}

	if (was_active_in_bar) {
		RK_ASSERT (widget->isAttached ());
		container->hide ();
		widget->active = false;
	}
	if (widget_to_id.isEmpty ()) hide ();
}

void RKToolWindowBar::closeOthers (RKMDIWindow* widget) {
	RK_TRACE (APP);

	for (QMap<RKMDIWindow*, int>::const_iterator it = widget_to_id.constBegin (); it != widget_to_id.constEnd (); ++it) {
		RKMDIWindow *cur = it.key ();
		if (cur != widget) {
			if (cur->isAttached ()) {
				cur->active = false;
				cur->hide ();
			}
			setTab (it.value (), false);
		}
	}
}

void RKToolWindowBar::showWidget (RKMDIWindow *widget) {
	RK_TRACE (APP);
	RK_ASSERT (widget_to_id.contains (widget));

	int id = widget_to_id[widget];

	// close any others
	closeOthers (widget);

	widget->show ();
	if (widget->isAttached ()) {
		setTab (id, true);
		container->show ();
		setSplitterSize (last_known_size);
	} else {
		widget->topLevelWidget ()->show ();
		widget->topLevelWidget ()->raise ();
	}
	widget->active = true;
}

void RKToolWindowBar::hideWidget (RKMDIWindow *widget) {
	RK_TRACE (APP);
	RK_ASSERT (widget_to_id.contains (widget));

	// prevent recursion
	if (!widget->active) return;
	int id = widget_to_id[widget];

	bool was_active_in_bar = ((widget->parent () == container) && widget->isVisible ());
	if (was_active_in_bar) {
		container->hide ();
	}

	RKWardMainWindow::getMain()->partManager()->setActivePart (0);
	widget->active = false;
	widget->hide ();

	setTab (id, false);

	RKWorkplace::mainWorkplace ()->view ()->setFocus ();
}

void RKToolWindowBar::tabClicked (int id) {
	RK_TRACE (APP);

	RKMDIWindow *widget = idToWidget (id);
	RK_ASSERT (widget);

	if (widget->isActive ()) {
		if (!widget->isAttached ()) widget->close (RKMDIWindow::NoAskSaveModified);
		else hideWidget (widget);
	} else {
		widget->activate (true);
	}
}

RKMDIWindow* RKToolWindowBar::idToWidget (int id) const {
	RK_TRACE (APP);

	for (QMap<RKMDIWindow*, int>::const_iterator it = widget_to_id.constBegin (); it != widget_to_id.constEnd (); ++it) {
		if (it.value () == id) {
			return (it.key ());
		}
	}

	return 0;
}

bool RKToolWindowBar::eventFilter (QObject *obj, QEvent *ev) {
	if (ev->type() == QEvent::ContextMenu) {
		RK_TRACE (APP);

		QContextMenuEvent *e = (QContextMenuEvent *) ev;
		KMultiTabBarTab *bt = dynamic_cast<KMultiTabBarTab*>(obj);

		if (bt) {
			id_of_popup = bt->id ();

			RKMDIWindow *widget = idToWidget (id_of_popup);
			RK_ASSERT (widget);
			if (widget) {
				QMenu menu (this);

				QAction *a = menu.addAction (RKStandardIcons::getIcon (widget->isAttached () ? RKStandardIcons::ActionDetachWindow : RKStandardIcons::ActionAttachWindow), widget->isAttached () ? i18n("Detach") : i18n("Attach"));
				connect (a, &QAction::triggered, this, &RKToolWindowBar::changeAttachment);

				KSelectAction *sel = new KSelectAction (i18n ("Position"), &menu);
				sel->addAction (RKStandardIcons::getIcon (RKStandardIcons::ActionMoveLeft), i18n ("Left Sidebar"));
				sel->addAction (RKStandardIcons::getIcon (RKStandardIcons::ActionMoveRight), i18n ("Right Sidebar"));
				sel->addAction (RKStandardIcons::getIcon (RKStandardIcons::ActionMoveUp), i18n ("Top Sidebar"));
				sel->addAction (RKStandardIcons::getIcon (RKStandardIcons::ActionMoveDown), i18n ("Bottom Sidebar"));
				sel->addAction (RKStandardIcons::getIcon (RKStandardIcons::ActionDelete), i18n ("Not shown in sidebar"));
#if KWIDGETSADDONS_VERSION >= QT_VERSION_CHECK(5,78,0)
				connect(sel, &KSelectAction::indexTriggered, this, &RKToolWindowBar::moveToolWindow);
#else
				connect (sel, static_cast<void (KSelectAction::*)(int)>(&KSelectAction::triggered), this, &RKToolWindowBar::moveToolWindow);
#endif
				menu.addAction (sel);
	
				menu.exec (e->globalPos());

				return true;
			}
		}
	}

	return false;
}

void RKToolWindowBar::contextMenuEvent (QContextMenuEvent* event) {
	RK_TRACE (APP);

	QMenu menu (this);
	foreach (const RKToolWindowList::ToolWindowRepresentation& rep, RKToolWindowList::registeredToolWindows ()) {
		QAction *a = menu.addAction (rep.window->windowIcon (), rep.window->shortCaption ());
		a->setCheckable (true);
		a->setChecked (rep.window->tool_window_bar == this);
		a->setData (rep.id);
	}
	connect (&menu, &QMenu::triggered, this, &RKToolWindowBar::addRemoveToolWindow);
	menu.exec (event->globalPos ());

	event->accept ();
}

void RKToolWindowBar::changeAttachment () {
	RK_TRACE (APP);

	RKMDIWindow *window = idToWidget (id_of_popup);
	RK_ASSERT (window);

	// toggle attachment
	if (window->isAttached ()) RKWorkplace::mainWorkplace ()->detachWindow (window);
	else RKWorkplace::mainWorkplace ()->attachWindow (window);
}

void RKToolWindowBar::moveToolWindow (int target) {
	RK_TRACE (APP);

	RK_ASSERT (target >= RKToolWindowList::Left);
	RK_ASSERT (target <= RKToolWindowList::Bottom);
	if (target == position ()) return;
	RKMDIWindow *window = idToWidget (id_of_popup);
	RK_ASSERT (window);

	RKWorkplace::mainWorkplace ()->placeInToolWindowBar (window, target);
}


void RKToolWindowBar::addRemoveToolWindow (QAction *action) {
	RK_TRACE (APP);
	RK_ASSERT (action);

	RKMDIWindow *win = RKToolWindowList::findToolWindowById (action->data ().toString ());
	if (action->isChecked ()) {
		RKWorkplace::mainWorkplace ()->placeInToolWindowBar (win, position ());
	} else {
		RK_ASSERT (win->tool_window_bar == this);
		removeWidget (win);
	}
}

