/*
 * Copyright (C) 2014-2026 CZ.NIC
 *
 * 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 3 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, see <http://www.gnu.org/licenses/>.
 *
 * In addition, as a special exception, the copyright holders give
 * permission to link the code of portions of this program with the
 * OpenSSL library under certain conditions as described in each
 * individual source file, and distribute linked combinations including
 * the two.
 */

#include <QListView>
#include <QPushButton>
#include <QTabWidget>
#include <QVBoxLayout>

#include "src/delegates/account_status_value.h"
#include "src/io/isds_sessions.h"
#include "src/settings/accounts.h"
#include "src/widgets/account_status_widget.h"

/*!
 * @brief Generate status text according to number of online accounts.
 *
 * @return Status text.
 */
static
QString generateControlText(int nowActive)
{
	return (nowActive > 0)
	    ? AccountStatusControl::tr("Mode: online")
	    : AccountStatusControl::tr("Mode: offline");
}

#define LAYOUT_MARGIN_PX 3
#define TAB_REGULAR 0
#define TAB_SHADOW 1

AccountStatusControl::AccountStatusControl(AccountsMap *regularAccounts,
    AccountsMap *shadowAccounts, IsdsSessions *sessions, QWidget *parent)
    : QLabel(parent),
    m_sessions(sessions),
    m_accountPopup(Q_NULLPTR),
    m_regularAccountStatusDelegate(this),
    m_regularAccountStatusModel(regularAccounts, sessions, this),
    m_regularAccountStatusView(Q_NULLPTR),
    m_shadowAccountStatusDelegate(this),
    m_shadowAccountStatusModel(shadowAccounts, sessions, this),
    m_shadowAccountStatusView(Q_NULLPTR),
    m_disconnectAllButton(Q_NULLPTR)
{
	if (Q_NULLPTR != m_sessions) {
		this->setText(generateControlText(m_sessions->nowActive()));
		this->setToolTip(tr(
		    "The status control shows whether there are any\n"
		    "sessions (regular or shadow) for data boxes active.\n"
		    "Click on it to display more information."));

		connect(m_sessions, SIGNAL(addedSession(QString, int)),
		    this, SLOT(watchSessionChanges(QString, int)));
		connect(m_sessions, SIGNAL(removedSession(QString, int)),
		    this, SLOT(watchSessionChanges(QString, int)));
	} else {
		this->setText(tr("Sessions"));
	}
	this->setAlignment(Qt::AlignCenter | Qt::AlignVCenter);

	QVBoxLayout *verticalLayout = Q_NULLPTR;
	QTabWidget *tabWidget = Q_NULLPTR;
	QWidget *tab_0_regular = Q_NULLPTR;
	QVBoxLayout *verticalLayout_2 = Q_NULLPTR;
	QWidget *tab_1_shadow = Q_NULLPTR;
	QVBoxLayout *verticalLayout_3 = Q_NULLPTR;

	m_accountPopup = new (::std::nothrow) QWidget(parentWidget());
	if (Q_NULLPTR != m_accountPopup) {
		m_accountPopup->setObjectName(QString::fromUtf8("m_accountPopup"));
		m_accountPopup->setWindowFlags(Qt::Widget | Qt::Popup);

		m_regularAccountStatusView = new (::std::nothrow) QListView(m_accountPopup);
		if (Q_NULLPTR != m_regularAccountStatusView) {
			m_regularAccountStatusView->setObjectName(QString::fromUtf8("m_regularAccountStatusView"));

			m_regularAccountStatusView->setSelectionMode(QAbstractItemView::NoSelection);

			m_regularAccountStatusView->setItemDelegate(&m_regularAccountStatusDelegate);
			m_regularAccountStatusView->setModel(&m_regularAccountStatusModel);
		}

		m_shadowAccountStatusView = new (::std::nothrow) QListView(m_accountPopup);
		if (Q_NULLPTR != m_shadowAccountStatusView) {
			m_shadowAccountStatusView->setObjectName(QString::fromUtf8("m_shadowAccountStatusView"));

			m_shadowAccountStatusView->setSelectionMode(QAbstractItemView::NoSelection);

			m_shadowAccountStatusView->setItemDelegate(&m_shadowAccountStatusDelegate);
			m_shadowAccountStatusView->setModel(&m_shadowAccountStatusModel);
		}

		m_disconnectAllButton = new (::std::nothrow) QPushButton(m_accountPopup);
		if (Q_NULLPTR != m_disconnectAllButton) {
			m_disconnectAllButton->setObjectName(QString::fromUtf8("m_disconnectAllButton"));

			m_disconnectAllButton->setText(tr("Disconnect All Sessions"));
			m_disconnectAllButton->setToolTip(tr(
			    "Click here to deactivate all\n"
			    "active (regular or shadow) sessions."));
			m_disconnectAllButton->setEnabled(m_sessions->nowActive() > 0);

			connect(m_disconnectAllButton, SIGNAL(clicked()),
			   this, SLOT(disconnectAllSessions()));
		}

		verticalLayout = new (::std::nothrow) QVBoxLayout(m_accountPopup);
		if (Q_NULLPTR != verticalLayout) {
			verticalLayout->setObjectName(QString::fromUtf8("verticalLayout"));

			verticalLayout->setContentsMargins(
			    LAYOUT_MARGIN_PX, LAYOUT_MARGIN_PX,
			    LAYOUT_MARGIN_PX, LAYOUT_MARGIN_PX);

			tabWidget = new (::std::nothrow) QTabWidget(m_accountPopup);
			if (Q_NULLPTR != tabWidget) {
				tabWidget->setObjectName(QString::fromUtf8("tabWidget"));

				tab_0_regular = new (::std::nothrow) QWidget();
				if (Q_NULLPTR != tab_0_regular) {
					tab_0_regular->setObjectName(QString::fromUtf8("tab_0_regular"));
					verticalLayout_2 = new (::std::nothrow) QVBoxLayout(tab_0_regular);
					if (Q_NULLPTR != verticalLayout_2) {
						verticalLayout_2->setObjectName(QString::fromUtf8("verticalLayout_2"));
						verticalLayout_2->setContentsMargins(0, 0, 0, 0);

						verticalLayout_2->addWidget(m_regularAccountStatusView);
					}

					tabWidget->addTab(tab_0_regular, QString());

					tabWidget->setTabText(tabWidget->indexOf(tab_0_regular),
					    tr("Regular"));
					tabWidget->setTabToolTip(tabWidget->indexOf(tab_0_regular),
					    tr("Regular data boxes are listed in the main window."));
				}

				tab_1_shadow = new (::std::nothrow) QWidget();
				if (Q_NULLPTR != tab_1_shadow) {
					tab_1_shadow->setObjectName(QString::fromUtf8("tab_1_shadow"));
					verticalLayout_3 = new (::std::nothrow) QVBoxLayout(tab_1_shadow);
					if (Q_NULLPTR != verticalLayout_3) {
						verticalLayout_3->setObjectName(QString::fromUtf8("verticalLayout_3"));
						verticalLayout_3->setContentsMargins(0, 0, 0, 0);

						verticalLayout_3->addWidget(m_shadowAccountStatusView);
					}

					tabWidget->addTab(tab_1_shadow, QString());

					tabWidget->setTabText(tabWidget->indexOf(tab_1_shadow),
					    tr("Shadow"));
					tabWidget->setTabToolTip(tabWidget->indexOf(tab_1_shadow),
					    tr("Shadow data boxes exist in the background of the application."));
				}

				verticalLayout->addWidget(tabWidget);
			}

			verticalLayout->addWidget(m_disconnectAllButton);
		}
	}

	connect(&m_regularAccountStatusModel, SIGNAL(modelReset()),
	    this, SLOT(watchRegularModelReset()));
	connect(&m_regularAccountStatusModel, SIGNAL(rowsInserted(QModelIndex, int, int)),
	    this, SLOT(watchRegularRowsInserted(QModelIndex, int, int)));

	connect(&m_shadowAccountStatusModel, SIGNAL(modelReset()),
	    this, SLOT(watchShadowModelReset()));
	connect(&m_shadowAccountStatusModel, SIGNAL(rowsInserted(QModelIndex, int, int)),
	    this, SLOT(watchShadowRowsInserted(QModelIndex, int, int)));

	/* Open persistent editors. */
	watchRegularModelReset();
	watchShadowModelReset();
}

AccountStatusControl::~AccountStatusControl(void)
{
	delete m_accountPopup;
}

void AccountStatusControl::mousePressEvent(QMouseEvent *event)
{
	Q_UNUSED(event);

	static int viewItemWidth = -1;
	static int viewItemHeight = -1;
	if (Q_UNLIKELY(viewItemWidth < 0)) {
		QSize sizeHint = AccountStatusValue(
		    "Quite long account name used for view size estimation",
		    "username", 0, false)
		        .sizeHint(QStyleOptionViewItem());
		viewItemWidth = sizeHint.width() + 40; // 40 for borders etc.
		viewItemHeight = sizeHint.height() * 4; // 4 times the height of list entry
	}

	if (Q_NULLPTR != m_accountPopup) {
		const QPoint parentTopLeft = this->mapToGlobal(QPoint(0, 0));
		const int width = viewItemWidth;
		const int height = viewItemHeight;

		m_accountPopup->setGeometry(
		    parentTopLeft.x() - width + this->width(),
		    parentTopLeft.y() - height, width, height);
		m_accountPopup->show();
		m_accountPopup->raise();
	}
}

void AccountStatusControl::disconnectAllSessions(void)
{
	if (Q_UNLIKELY(Q_NULLPTR == m_sessions)) {
		return;
	}

	m_sessions->quitAllSessions();
}

void AccountStatusControl::watchSessionChanges(const QString &username,
    int nowActive)
{
	Q_UNUSED(username);

	if (Q_NULLPTR != m_disconnectAllButton) {
		m_disconnectAllButton->setEnabled(nowActive > 0);
	}
	this->setText(generateControlText(nowActive));
}

/*!
 * @brief Updates persistent editor settings based on account status model.
 */
static
void watchModelReset(QListView *accountStatusView,
    AccountStatusModel &accountStatusModel)
{
	if (Q_UNLIKELY(Q_NULLPTR == accountStatusView)) {
		return;
	}

	for (int row = 0; row < accountStatusModel.rowCount(); ++row) {
		accountStatusView->openPersistentEditor(
		    accountStatusModel.index(row, 0));
	}
}

void AccountStatusControl::watchRegularModelReset(void)
{
	watchModelReset(m_regularAccountStatusView,
	    m_regularAccountStatusModel);
}

/*!
 * @brief Updates persistent editor settings based on account status model.
 */
static
void watchrowsInserted(QListView *accountStatusView,
    AccountStatusModel &accountStatusModel, const QModelIndex &parent,
    int first, int last)
{
	if (Q_UNLIKELY(Q_NULLPTR == accountStatusView)) {
		return;
	}

	if (Q_UNLIKELY(parent.isValid())) {
		return;
	}

	/* Open persistent editor for last rows in view. */
	int insertedNum = last - first + 1;
	for (int row = accountStatusModel.rowCount() - insertedNum;
	     row < accountStatusModel.rowCount(); ++row) {
		accountStatusView->openPersistentEditor(
		    accountStatusModel.index(row, 0));
	}
}

void AccountStatusControl::watchRegularRowsInserted(const QModelIndex &parent,
    int first, int last)
{
	watchrowsInserted(m_regularAccountStatusView,
	    m_regularAccountStatusModel, parent, first, last);
}

void AccountStatusControl::watchShadowModelReset(void)
{
	watchModelReset(m_shadowAccountStatusView, m_shadowAccountStatusModel);
}

void AccountStatusControl::watchShadowRowsInserted(const QModelIndex &parent,
    int first, int last)
{
	watchrowsInserted(m_shadowAccountStatusView,
	    m_shadowAccountStatusModel, parent, first, last);
}
