/*
 * IdealIRC - Internet Relay Chat client
 * Copyright (C) 2019  Tom-Andre Barstad
 *
 * 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 "IWin/IWinStatus.h"
#include "IWin/IWinChannel.h"
#include "IWin/IWinPrivate.h"
#include "MdiManager.h"
#include "ConfigMgr.h"
#include <QApplication>
#include <QDebug>

namespace {
constexpr int WINDOW_DEFAULT_WIDTH = 800;
constexpr int WINDOW_DEFAULT_HEIGHT = 600;
constexpr int WINDOW_STEP = 30;
constexpr int WINDOW_STEP_RESET_POSITION = 400;
constexpr int WINDOW_STEP_RESET_ADJUST = 60;
}

MdiManager* MdiManager::m_instance{ nullptr };

MdiManager::MdiManager(QMdiArea& mdiArea, QToolBar& buttonBar, QSystemTrayIcon& trayIcon)
	: m_mdiArea(mdiArea)
	, trayIcon(trayIcon)
	, m_bbMgr(&buttonBar)
{
	m_instance = this;
	connect(&m_mdiArea, &QMdiArea::subWindowActivated, this, &MdiManager::subwinActivated);
	connect(&m_bbMgr, &ButtonbarMgr::changeWindow, &m_mdiArea, &QMdiArea::setActiveSubWindow);
}

MdiManager& MdiManager::instance()
{
	return *m_instance;
}

MdiManager::ButtonBarEntry::ButtonBarEntry(QAction* button_, const QString& buttonText, QMdiSubWindow* parent)
	: subwin(parent)
	, button(button_)
{
	menu = new QMenu(parent);
	menuHead = menu->addAction(buttonText);
	QFont f = menuHead->font();
	f.setBold(true);
	menuHead->setFont(f);
	menuHead->setDisabled(true);
	menuSep = menu->addSeparator();
	menuClose = menu->addAction(tr("Close"));
	connect(menuClose, &QAction::triggered, [&](bool){
		subwin->close();
	});
}

IWin* MdiManager::createSubwindow(IWin* parent, const QString& buttonText, IWin::Type windowType, bool activate)
{
	IWin* basePtr{ nullptr };
	IWinStatus* statusParent{ nullptr };
	QMdiSubWindow* mdiwin{ nullptr };

	if (parent && parent->getType() == IWin::Type::Status)
		statusParent = dynamic_cast<IWinStatus*>(parent);

	switch (windowType) {
	case IWin::Type::Status:
		basePtr = new IWinStatus(buttonText);
		m_activeStatus = dynamic_cast<IWinStatus*>(basePtr);
		break;

	case IWin::Type::Channel:
		basePtr = new IWinChannel(statusParent, buttonText);
		break;

	case IWin::Type::Private:
		basePtr = new IWinPrivate(statusParent, buttonText);
		break;

	case IWin::Type::Custom:
		[[fallthrough]]; // Use createCustomSubwindow; treat this as an error.

	case IWin::Type::Undefined:
		break;
	}

	if (!basePtr) {
		qWarning()	<< "Failed to create subwindow of type" << IWin::TypeString[windowType] << "with button text" << buttonText;
		return nullptr;
	}

	connect(basePtr, &IWin::aboutToClose,
			this, &MdiManager::subwinAboutToClose);

	mdiwin = m_mdiArea.addSubWindow(basePtr, Qt::SubWindow);
	mdiwin->setAttribute(Qt::WA_DeleteOnClose);
	m_bbMgr.addButton(basePtr, mdiwin);

	QRect spawnCoord = generateSpawnCoordinates();
	mdiwin->setGeometry(spawnCoord);

	qInfo() << "Created a subwindow of type" << IWin::TypeString[windowType] << "with button text" << buttonText;

	if (!m_active || m_active->isMaximized())
		mdiwin->showMaximized();
	else
		mdiwin->show();

	if (!activate) {
		m_mdiArea.activatePreviousSubWindow();
		mdiwin->lower();
	}

	return basePtr;
}

IWin* MdiManager::currentWindow() const
{
	return m_active;
}

IWinStatus* MdiManager::currentStatus() const
{
	return m_activeStatus;
}

IWin* MdiManager::findWindow(IWin* statusParent, const QString& buttonText)
{
	for (QMdiSubWindow* mdiwin : m_mdiArea.subWindowList()) {
		IWin* subwin = dynamic_cast<IWin*>(mdiwin->widget());
		if (subwin->getParent() == statusParent && buttonText.compare(subwin->getButtonText(), Qt::CaseInsensitive) == 0) {
			subwin->setButtonText(buttonText);
			renameWindowButton(subwin, buttonText);
			return subwin;
		}
	}
	return nullptr;
}

QMdiSubWindow* MdiManager::toMdiwin(IWin* win)
{
	for (QMdiSubWindow* mdiwin : m_mdiArea.subWindowList()) {
		if (mdiwin->widget() == win)
			return mdiwin;
	}
	return nullptr;
}

QList<QMdiSubWindow*> MdiManager::mdiChildrenOf(const IWin* statusParent, IWin::Type type) const
{
	QList<QMdiSubWindow*> retlist;
	for (QMdiSubWindow* mdiwin : m_mdiArea.subWindowList()) {
		IWin* subwin = dynamic_cast<IWin*>(mdiwin->widget());
		if (type != IWin::Type::Undefined && subwin->getType() != type)
			continue;

		if (subwin->getParent() == statusParent)
			retlist.push_back(mdiwin);
	}
	return retlist;
}

QList<IWin*> MdiManager::childrenOf(const IWin* statusParent, IWin::Type type) const
{
	QList<IWin*> retlist;
	QList<QMdiSubWindow*> subwinList = mdiChildrenOf(statusParent, type);
	for (QMdiSubWindow* subwin : subwinList)
		retlist.push_back(qobject_cast<IWin*>(subwin->widget()));
	return retlist;
}

void MdiManager::showTrayInfo(const QString& title, const QString& message)
{
	showTray(title, message, QSystemTrayIcon::MessageIcon::Information);
}

void MdiManager::showTrayWarn(const QString& title, const QString& message)
{
	showTray(title, message, QSystemTrayIcon::MessageIcon::Warning);
}

int MdiManager::connectionsOnlineCount() const
{
	int c = 0;

	for (QMdiSubWindow* mdiwin : m_mdiArea.subWindowList()) {
		IWin* subwin = dynamic_cast<IWin*>(mdiwin->widget());
		if (subwin->getType() != IWin::Type::Status)
			continue;
		IWinStatus* status = dynamic_cast<IWinStatus*>(subwin);
		if (status->getConnection().isOnline())
			++c;
	}
	return c;
}

void MdiManager::broadcastProgramExit()
{
	for (QMdiSubWindow* mdiwin : m_mdiArea.subWindowList()) {
		IWin* subwin = dynamic_cast<IWin*>(mdiwin->widget());
		if (subwin->getType() != IWin::Type::Status)
			continue;
		IWinStatus* status = dynamic_cast<IWinStatus*>(subwin);
		if (status->getConnection().isOnline()) {
			connect(&status->getConnection(), &IRC::readyForExit, [this](){
				if (connectionsOnlineCount() == 0)
					emit readyForExit();
			});

			ConfigMgr& conf = ConfigMgr::instance();
			status->getConnection().disconnectForExit(conf.common("QuitMessage"));
		}
	}
}

void MdiManager::renameWindowButton(IWin* window, const QString& text)
{
	m_bbMgr.reloadButtonName(window, text);
}

void MdiManager::print(IWinStatus* parent, const QString& target, const PrintType ptype, const QString& text)
{
	IWin* subwin = findWindow(parent, target);
	if (!subwin || !subwin->print(ptype, text)) {
		if (ptype != PrintType::CTCP)
			parent->print(ptype, QStringLiteral("[%1] %2").arg(target).arg(text));
		else
			parent->print(ptype, text);
	}
}

void MdiManager::print(const QString& target, const PrintType ptype, const QString& text)
{
	print(m_activeStatus, target, ptype, text);
}

void MdiManager::printToActive(IWinStatus* parent, const PrintType ptype, const QString& text)
{
	if (!m_active)
		parent->print(ptype, text);
	else if (m_active->getParent() != parent)
		parent->print(ptype, text);
	else
		m_active->print(ptype, text);
}

void MdiManager::printToTypes(IWinStatus* parent, const IWin::Type toType, const PrintType ptype, const QString& text)
{
	for (QMdiSubWindow* mdiwin : m_mdiArea.subWindowList()) {
		IWin* subwin = qobject_cast<IWin*>(mdiwin->widget());
		if (subwin->getParent() == parent && subwin->getType() == toType)
			subwin->print(ptype, text);
	}
}

void MdiManager::printToAll(IWinStatus* parent, const PrintType ptype, const QString& text)
{
	for (QMdiSubWindow* mdiwin : m_mdiArea.subWindowList()) {
		IWin* subwin = qobject_cast<IWin*>(mdiwin->widget());
		if (subwin->getParent() == parent || subwin == parent)
			subwin->print(ptype, text);
	}
}

void MdiManager::showTray(const QString& title, const QString& message, QSystemTrayIcon::MessageIcon icon)
{
	if (QApplication::activeWindow() || QApplication::focusWidget()) return;

	ConfigMgr& conf = ConfigMgr::instance();
	if (conf.common("TrayNotify") != "1") return;
	int delay = conf.common("TrayNotifyDelay").toInt();
	trayIcon.showMessage(title, message, icon, delay*1000);
}

void MdiManager::subwinAboutToClose(IWin* who)
{
	qInfo() << "Closing" << who;
	m_bbMgr.delButton(who);
}

void MdiManager::subwinActivated(QMdiSubWindow* window)
{
	if (!window) return;

	IWin* cw = dynamic_cast<IWin*>(window->widget());
	qInfo() << "Activated:" << cw;
	if (!cw) {
		qWarning() << "NULL window!";
		return;
	}
	m_bbMgr.subwinActivated(m_active, cw);
	m_active = cw;
	if (cw->getParent() && cw->getParent()->getType() == IWin::Type::Status)
		m_activeStatus = dynamic_cast<IWinStatus*>(cw->getParent());
	else if (cw->getType() == IWin::Type::Status)
		m_activeStatus = qobject_cast<IWinStatus*>(cw);

	emit subwindowSwitched();
}

QRect MdiManager::generateSpawnCoordinates()
{
	QRect rectangle(nextXY, nextXY, WINDOW_DEFAULT_WIDTH, WINDOW_DEFAULT_HEIGHT);
	nextXY += WINDOW_STEP;

	if (nextXY > WINDOW_STEP_RESET_POSITION) {
		nextXYadjust += WINDOW_STEP_RESET_ADJUST;
		if (nextXYadjust > WINDOW_STEP_RESET_POSITION)
			nextXYadjust = 0;

		nextXY = nextXYadjust;
	}

	return rectangle;
}