/*
 * 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 <QPushButton>

#include "src/delegates/account_status_delegate.h"
#include "src/delegates/account_status_value.h"

AccountStatusDelegate::AccountStatusDelegate(QWidget *parent)
    : QStyledItemDelegate(parent)
{
}

void AccountStatusDelegate::paint(QPainter *painter,
    const QStyleOptionViewItem &option, const QModelIndex &index) const
{
	if (index.data().canConvert<AccountStatusValue>()) {
		const AccountStatusValue accountStatusValue =
		    qvariant_cast<AccountStatusValue>(index.data());
		accountStatusValue.paint(painter, option);
	} else {
		QStyledItemDelegate::paint(painter, option, index);
	}
}

QSize AccountStatusDelegate::sizeHint(const QStyleOptionViewItem &option,
    const QModelIndex &index) const
{
	if (index.data().canConvert<AccountStatusValue>()) {
		const AccountStatusValue accountStatusValue =
		    qvariant_cast<AccountStatusValue>(index.data());
		return accountStatusValue.sizeHint(option);
	} else {
		return QStyledItemDelegate::sizeHint(option, index);
	}
}

QWidget *AccountStatusDelegate::createEditor(QWidget *parent,
    const QStyleOptionViewItem &option, const QModelIndex &index) const
{
	Q_UNUSED(option);
	Q_UNUSED(index);

	QPushButton *button = new (::std::nothrow) QPushButton(parent);
	if (Q_UNLIKELY(Q_NULLPTR == button)) {
		return Q_NULLPTR;
	}

	button->setText(tr("Disconnect"));

	connect(button, SIGNAL(clicked()),
	    this, SLOT(onButtonClicked()));

	return button;
}

void AccountStatusDelegate::setEditorData(QWidget *editor,
    const QModelIndex &index) const
{
	QPushButton *button = qobject_cast<QPushButton *>(editor);
	if (Q_UNLIKELY(Q_NULLPTR == button)) {
		Q_ASSERT(0);
		return;
	}

	if (Q_UNLIKELY(!index.data().canConvert<AccountStatusValue>())) {
		return;
	}

	AccountStatusValue accountStatusValue =
	    qvariant_cast<AccountStatusValue>(index.data());

	button->setEnabled(accountStatusValue.sessionActive);
}

void AccountStatusDelegate::updateEditorGeometry(QWidget *editor,
    const QStyleOptionViewItem &option,
    const QModelIndex &index) const
{
	Q_UNUSED(index);

	QPushButton *button = qobject_cast<QPushButton *>(editor);
	if (Q_UNLIKELY(Q_NULLPTR == button)) {
		Q_ASSERT(0);
		return;
	}

	if (Q_UNLIKELY(!index.data().canConvert<AccountStatusValue>())) {
		return;
	}

	AccountStatusValue accountStatusValue =
	    qvariant_cast<AccountStatusValue>(index.data());

	const QSize buttonSize = button->sizeHint();
	const QRect parentRect = option.rect;

	button->setGeometry(
	    parentRect.x() + parentRect.width() - buttonSize.width() - ACCOUNT_STATUS_MARGIN,
	    parentRect.y() + parentRect.height() - buttonSize.height() - ACCOUNT_STATUS_MARGIN,
	    buttonSize.width(),
	    buttonSize.height());

	button->setVisible(accountStatusValue.sessionActive);
}

/* Dynamic property name. */
#define USER_CLICKED_PROP_NAME "DYNPROP_USER_CLICKED"

void AccountStatusDelegate::setModelData(QWidget *editor,
    QAbstractItemModel *model, const QModelIndex &index) const
{
	/*
	 * This method is called whenever the editor is active and the cell is
	 * clicked. We don't need to set the data when the cell is clicked.
	 *
	 * We need to assure that the button is clicked. Therefore a dynamic
	 * property is set only when the button is clicked and setData()
	 * is called only when the dynamic property is set.
	 */

	QPushButton *button = qobject_cast<QPushButton *>(editor);
	if (Q_UNLIKELY(Q_NULLPTR == button)){
		Q_ASSERT(0);
		return;
	}

	{
		const QVariant dynPropVal = button->property(USER_CLICKED_PROP_NAME);
		if ((!dynPropVal.isValid()) || (!dynPropVal.toBool())) {
			/* Don't set model data when button not clicked. */
			return;
		}
	}

	/*
	 * Erase the dynamic property from the button so it can be used next
	 * time.
	 */
	button->setProperty(USER_CLICKED_PROP_NAME, QVariant());

	if (Q_UNLIKELY(!index.data().canConvert<AccountStatusValue>())) {
		return;
	}

	AccountStatusValue accountStatusValue =
	    qvariant_cast<AccountStatusValue>(index.data());

	accountStatusValue.sessionActive = false;

	model->setData(index, QVariant::fromValue(accountStatusValue),
	    Qt::EditRole);
}

void AccountStatusDelegate::onButtonClicked(void)
{
	QPushButton *button = qobject_cast<QPushButton *>(QObject::sender());
	if (Q_UNLIKELY(Q_NULLPTR == button)){
		Q_ASSERT(0);
		return;
	}

	/*
	 * Set the dynamic property for the button to indicate intentional
	 * button press.
	 */
	button->setProperty(USER_CLICKED_PROP_NAME, true);

	/* Let the delegate write the data. */
	emit commitData(button);
}

#undef USER_CLICKED_PROP_NAME
