/*
 * 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 <algorithm> /* ::std::max */
#include <QApplication>
#include <QFontMetrics>
#include <QStyleOptionViewItem>
#include <QPainter>

#include "src/datovka_shared/compat/compiler.h" /* macroStdMove */
#include "src/datovka_shared/settings/account.h"
#include "src/delegates/account_status_value.h"

AccountStatusValue::AccountStatusValue(void)
    : acntName(),
    username(),
    loginMethod(-1),
    sessionActive(false)
{
}

AccountStatusValue::AccountStatusValue(const AccountStatusValue &other)
    : acntName(other.acntName),
    username(other.username),
    loginMethod(other.loginMethod),
    sessionActive(other.sessionActive)
{
}

#ifdef Q_COMPILER_RVALUE_REFS
AccountStatusValue::AccountStatusValue(AccountStatusValue &&other) Q_DECL_NOEXCEPT
    : acntName(::std::move(other.acntName)),
    username(::std::move(other.username)),
    loginMethod(other.loginMethod),
    sessionActive(other.sessionActive)
{
}
#endif /* Q_COMPILER_RVALUE_REFS */

AccountStatusValue::AccountStatusValue(const QString &aName,
    const QString &uName, int lMethod, bool sActive)
    : acntName(aName),
    username(uName),
    loginMethod(lMethod),
    sessionActive(sActive)
{
}

#ifdef Q_COMPILER_RVALUE_REFS
AccountStatusValue::AccountStatusValue(QString &&aName, QString &&uName,
    int lMethod, bool sActive)
    : acntName(::std::move(aName)),
    username(::std::move(uName)),
    loginMethod(lMethod),
    sessionActive(sActive)
{
}
#endif /* Q_COMPILER_RVALUE_REFS */

AccountStatusValue &AccountStatusValue::operator=(const AccountStatusValue &other) Q_DECL_NOTHROW
{
	acntName = other.acntName;
	username = other.username;
	loginMethod = other.loginMethod;
	sessionActive = other.sessionActive;
	return *this;
}

#ifdef Q_COMPILER_RVALUE_REFS
AccountStatusValue &AccountStatusValue::operator=(AccountStatusValue &&other) Q_DECL_NOTHROW
{
	acntName = ::std::move(other.acntName);
	username = ::std::move(other.username);
	loginMethod = other.loginMethod;
	sessionActive = other.sessionActive;
	return *this;
}
#endif /* Q_COMPILER_RVALUE_REFS */

bool AccountStatusValue::operator==(const AccountStatusValue &other) const
{
	return (acntName == other.acntName)
	    && (username == other.username)
	    && (loginMethod == other.loginMethod)
	    && (sessionActive == other.sessionActive);
}

bool AccountStatusValue::operator!=(const AccountStatusValue &other) const
{
	return !operator==(other);
}

/*
 * Inspired by:
 * https://habr.com/ru/articles/134802/
 * https://stackoverflow.com/questions/45443619/delegate-with-multiple-widgets
 */

/*!
 * @brief Return login method description.
 *
 * @param[in] loginMethod Login method identification.
 * @return Text description.
 */
static
QString loginMethodDescription(int loginMethod)
{
	switch (loginMethod) {
	case AcntSettings::LIM_UNAME_PWD:
		return AccountStatusValue::tr("password");
		break;
	case AcntSettings::LIM_UNAME_CRT:
		return AccountStatusValue::tr("certificate");
		break;
	case AcntSettings::LIM_UNAME_PWD_CRT:
		return AccountStatusValue::tr("certificate + password");
		break;
	case AcntSettings::LIM_UNAME_PWD_HOTP:
		return AccountStatusValue::tr("password + security code");
		break;
	case AcntSettings::LIM_UNAME_PWD_TOTP:
		return AccountStatusValue::tr("password + security SMS");
		break;
	case AcntSettings::LIM_UNAME_MEP:
		return AccountStatusValue::tr("Mobile Key");
		break;
	default:
		return AccountStatusValue::tr("unknown");
		break;
	}
}

/* Factor to increase account name in list view element. */
#define STATUS_TITLE_FACTOR 1.3

/*!
 * @brief Paint element content. Editor controls are created elsewhere.
 *
 * @param[in] asv Account status value to be visualised.
 * @param[in] painter Painter.
 * @param[in] option Style options.
 */
static
void paintItem(const AccountStatusValue &asv, QPainter *painter,
    const QStyleOptionViewItem &option)
{
	painter->save();

	QRect bRect;
	painter->setClipRect(option.rect);

	//painter->setPen(QColor(210, 210, 210));
	//painter->setBrush(QColor(240, 240, 240));

	//painter->setPen(QColor(77, 77, 77));
	painter->translate(option.rect.topLeft());

	QFont font = option.font;
	font.setPointSize(font.pointSize() * STATUS_TITLE_FACTOR);

	/* Draw title with account name. */
	font.setWeight(QFont::Bold);
	QFontMetrics fMetrics(font);
	bRect = fMetrics.boundingRect(asv.acntName);
	const int titleHeight = bRect.height();
	painter->setFont(font);
	painter->drawText(ACCOUNT_STATUS_MARGIN , bRect.height(), asv.acntName);

	/* Move down. */
	painter->translate(0, bRect.height());

	{
		QString text = AccountStatusValue::tr("Login Method: ") +
		    loginMethodDescription(asv.loginMethod);
		font = option.font;
		fMetrics = QFontMetrics(font);
		bRect = fMetrics.boundingRect(text);
		painter->setFont(option.font);
		painter->drawText(ACCOUNT_STATUS_MARGIN , bRect.height(), text);
	}

	painter->translate(0, bRect.height());

	{
		QString text = AccountStatusValue::tr("Connection to ISDS: ") +
		    (asv.sessionActive ? AccountStatusValue::tr("active") : AccountStatusValue::tr("inactive"));
		//fMetrics = QFontMetrics(font);
		bRect = fMetrics.boundingRect(text);
		//painter->setFont(option.font);
		painter->drawText(ACCOUNT_STATUS_MARGIN , bRect.height(), text);
	}

	/* Draw fake button. */
	if (false) {
#define BUTTON_WIDTH titleHeight
#define BUTTON_HEIGHT titleHeight
		QStyleOptionButton button;
		QString text = AccountStatusValue::tr("Disconnect");
		int x, y, w, h;
		w = button.fontMetrics.boundingRect(text).size().width(); // button width
		h = BUTTON_HEIGHT; // button height
		x = option.rect.width() - w; // x coordinate
		y = 0; // y coordinate
		button.rect = QRect(x, y, w, h);
		button.text = macroStdMove(text);
		button.state = QStyle::State_Enabled;

		QApplication::style()->drawControl(QStyle::CE_PushButton,
		    &button, painter);
#undef BUTTON_WIDTH
#undef BUTTON_HEIGHT
	}

	painter->restore();
}

int AccountStatusValue::paint(QPainter *painter, const QStyleOptionViewItem &option) const
{
	if (Q_UNLIKELY(Q_NULLPTR == painter)) {
		Q_ASSERT(0);
		return 0;
	}

	painter->save();

	const QSize hint = sizeHint(option);

	int width = hint.width();

	paintItem(*this, painter, option);

	painter->restore();

	/* Paint bottom line between elements. */
	{
		painter->save();
		//painter->setPen(QColor(0xD7, 0xD7, 0xD7));
		painter->drawLine(option.rect.bottomLeft(), option.rect.bottomRight());
		painter->restore();
	}

	return width;
}

QSize AccountStatusValue::sizeHint(const QStyleOptionViewItem &option) const
{
	QSize firstLineSize;
	QSize secondLineSize;
	QSize thirdLineSize;
	int maxWidth = 0;

	QFont font = option.font;
	font.setPointSize(font.pointSize() * STATUS_TITLE_FACTOR);

	font.setWeight(QFont::Bold);
	QFontMetrics fMetrics(font);
	firstLineSize = fMetrics.boundingRect(this->acntName).size();
	maxWidth = firstLineSize.width();

	{
		QString text = AccountStatusValue::tr("Login Method: ") +
		    loginMethodDescription(this->loginMethod);
		font = option.font;
		fMetrics = QFontMetrics(font);
		secondLineSize = fMetrics.boundingRect(text).size();
		maxWidth = ::std::max(maxWidth, secondLineSize.width());
	}

	{
		QString text = AccountStatusValue::tr("Connection to ISDS: ") +
		    (this->sessionActive ? AccountStatusValue::tr("active") : AccountStatusValue::tr("inactive"));
		//font = option.font;
		//fMetrics = QFontMetrics(font);
		thirdLineSize = fMetrics.boundingRect(text).size();
		maxWidth = ::std::max(maxWidth, thirdLineSize.width());
	}

	return QSize(maxWidth,
	    firstLineSize.height() + secondLineSize.height() + thirdLineSize.height() + 10);
}
