/*
 * Copyright (C) 2014-2022 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 <QApplication>
#include <QFileDialog>
#include <QFileInfo>
#include <QMessageBox>

#include "src/common.h"
#include "src/datovka_shared/isds/type_conversion.h"
#include "src/datovka_shared/log/log.h"
#include "src/datovka_shared/settings/pin.h"
#include "src/datovka_shared/worker/pool.h"
#include "src/global.h"
#include "src/gui/dlg_create_account.h"
#include "src/gui/dlg_pin_input.h"
#include "src/gui/dlg_user_ext2.h"
#include "src/gui/icon_container.h"
#include "src/gui/isds_login.h"
#include "src/io/account_db.h"
#include "src/io/account_db_tables.h"
#include "src/io/account_shadow_helper.h"
#include "src/io/dbs.h"
#include "src/io/isds_helper.h"
#include "src/io/message_db_set_container.h"
#include "src/io/message_db_set.h"
#include "src/isds/type_description.h"
#include "src/settings/accounts.h"
#include "src/views/table_home_end_filter.h"
#include "src/views/table_tab_ignore_filter.h"
#include "src/worker/task_add_data_box_user2.h"
#include "src/worker/task_delete_data_box_user2.h"
#include "src/worker/task_get_data_box_users2.h"
#include "src/worker/task_get_owner_info_from_login2.h"
#include "src/worker/task_get_user_info_from_login2.h"
#include "src/worker/task_update_data_box_user2.h"
#include "ui_dlg_create_account.h"

#ifdef Q_COMPILER_RVALUE_REFS
#  define macroStdMove(x) ::std::move(x)
#else /* Q_COMPILER_RVALUE_REFS */
#  define macroStdMove(x) (x)
#endif /* Q_COMPILER_RVALUE_REFS */

#define PAGE_DESCRIPTION 0
#define PAGE_CREDENTIALS 1
#define PAGE_PRIVILEGES 2
#define PAGE_USERS 3

static Isds::DbOwnerInfoExt2 _owner; /* Box owner info. */
static Isds::DbUserInfoExt2 _user; /* Box user info. */

/*!
 * @brief Login method order as they are listed in the dialogue.
 */
enum LoginMethodIndex {
	USER_NAME = 0,
	CERTIFICATE = 1,
	USER_CERTIFICATE = 2,
	HOTP = 3,
	TOTP = 4,
	MEP = 5
};

DlgCreateAccount::DlgCreateAccount(const AcntData &accountInfo,
    enum Action action, const QString &syncAllActName, QWidget *parent)
    : QDialog(parent),
    m_ui(new (::std::nothrow) Ui::DlgCreateAccount),
    m_accountSettingsModel(this),
    m_regularAccounts(Q_NULLPTR),
    m_shadowAccounts(Q_NULLPTR),
    m_accountInfo(),
    m_action(action),
    m_showViewPwd((ACT_ADDNEW == action) ||
        ((ACT_EDIT == action) && GlobInstcs::pinSetPtr->pinConfigured())),
    m_loginMethod(USER_NAME),
    m_certPath(),
    m_hidePwdTimer(this),
    m_viewPwdUpdate(100),
    m_viewPwdDuration(10000), /* 10 seconds */
    m_viewPwdRemainingCycles(0),
    m_usersModel(this),
    m_userListProxyModel(this),
    m_userListMenu(this),
    m_actionUserAdd(Q_NULLPTR),
    m_actionUserChangeContactAddress(Q_NULLPTR),
    m_actionUserChangePrivils(Q_NULLPTR),
    m_actionUserDelete(Q_NULLPTR),
    m_treeViewMenu(this),
    m_actionAddShadow(Q_NULLPTR),
    m_actionRemoveShadow(Q_NULLPTR)
{
	m_ui->setupUi(this);
	/* Tab order is defined in UI file. */

	m_userListProxyModel.setSortRole(UsersModel::ROLE_PROXYSORT);
	m_userListProxyModel.setSourceModel(&m_usersModel);

	m_ui->treeView->setModel(&m_accountSettingsModel);
	m_ui->userTableView->setModel(&m_userListProxyModel);

	m_ui->userTableView->setSortingEnabled(true);
	m_ui->userTableView->sortByColumn(UsersModel::COL_GIV_NAMES,
	    Qt::AscendingOrder);

	m_ui->splitter->setChildrenCollapsible(false);
	m_ui->splitter_2->setChildrenCollapsible(false);

	m_ui->addCertButton->setIcon(
	    IconContainer::construcIcon(IconContainer::ICON_PLUS));

	m_ui->loginMethodComboBox->addItem(tr("Password"));
	m_ui->loginMethodComboBox->addItem(tr("Certificate"));
	m_ui->loginMethodComboBox->addItem(tr("Certificate + Password"));
	m_ui->loginMethodComboBox->addItem(tr("Password + Security code"));
	m_ui->loginMethodComboBox->addItem(tr("Password + Security SMS"));
	m_ui->loginMethodComboBox->addItem(tr("Mobile key"));

	m_ui->viewPwdProgress->setFixedHeight(m_ui->pwdLine->height() / 4);
	m_ui->viewPwdProgress->setTextVisible(false);
	m_ui->viewPwdProgress->setVisible(false);

	m_ui->certLabel->setEnabled(false);
	m_ui->addCertButton->setEnabled(false);

	m_ui->mepLabel->setEnabled(false);
	m_ui->mepLine->setEnabled(false);

	m_ui->syncAllCheckBox->setText(
	    tr("Synchronise this account when '%1' is activated")
	        .arg(syncAllActName));

	m_ui->buttonBox->button(QDialogButtonBox::Ok)->setEnabled(false);

	connect(m_ui->loginMethodComboBox, SIGNAL(currentIndexChanged(int)),
	    this, SLOT(activateContent(int)));
	connect(m_ui->showHidePwdButton, SIGNAL(clicked()), this,
	    SLOT(togglePwdVisibility()));
	connect(m_ui->addCertButton, SIGNAL(clicked()), this,
	    SLOT(addCertificateFile()));
	connect(m_ui->buttonBox, SIGNAL(accepted()), this,
	    SLOT(collectAccountData()));
	connect(m_ui->acntNameLine, SIGNAL(textChanged(QString)),
	    this, SLOT(checkInputFields()));
	connect(m_ui->usernameLine, SIGNAL(textChanged(QString)),
	    this, SLOT(checkInputFields()));
	connect(m_ui->pwdLine, SIGNAL(textChanged(QString)),
	    this, SLOT(checkInputFields()));
	connect(m_ui->mepLine, SIGNAL(textChanged(QString)),
	    this, SLOT(checkInputFields()));

	connect(&m_hidePwdTimer, SIGNAL(timeout()), this,
	    SLOT(updatePwdVisibilityProgress()));

	m_ui->showHidePwdButton->setVisible(m_showViewPwd);
	m_ui->showHidePwdButton->setEnabled(
	    m_showViewPwd && !m_ui->pwdLine->text().isEmpty());

	/* Set dialogue content for existing account. */
	if (ACT_ADDNEW != m_action) {
		setContent(accountInfo, FC_EDITABLE);
	}
}

DlgCreateAccount::~DlgCreateAccount(void)
{
	delete m_ui;
}

bool DlgCreateAccount::modify(AcntData &accountInfo, enum Action action,
    const QString &syncAllActName, int properties, QWidget *parent)
{
	DlgCreateAccount dlg(accountInfo, action, syncAllActName, parent);
	dlg.m_ui->treeView->setVisible(false);
	dlg.m_ui->treeViewButton->setVisible(false);
	dlg.m_ui->verticalStackedWidget->setCurrentIndex(PAGE_CREDENTIALS);
	if (properties != FC_NONE) {
		dlg.setContent(accountInfo, properties);
	}
	if (QDialog::Accepted == dlg.exec()) {
		/* Store submitted data. */
		accountInfo = dlg.m_accountInfo;
		return true;
	} else {
		return false;
	}
}

bool DlgCreateAccount::modify(AcntData &accountInfo, enum Action action,
    const QString &syncAllActName, QWidget *parent)
{
	return modify(accountInfo, action, syncAllActName, FC_NONE, parent);
}

bool DlgCreateAccount::manage(AccountsMap &regularAccounts,
    AccountsMap &shadowAccounts, const QString &syncAllActName,
    QWidget *parent)
{
	if (Q_UNLIKELY(regularAccounts.isEmpty())) {
		return false;
	}

	/* Manage a copy of shadow accounts. */
	AccountsMap shadowAccountsCopy(AcntContainer::CT_SHADOW);
	shadowAccountsCopy.load(shadowAccounts);

	DlgCreateAccount dlg(AcntData(), ACT_EDIT, syncAllActName, parent);
	dlg.m_regularAccounts = &regularAccounts;
	dlg.m_shadowAccounts = &shadowAccountsCopy;
	dlg.m_accountSettingsModel.loadContent(dlg.m_regularAccounts,
	    dlg.m_shadowAccounts);
	connect(dlg.m_ui->treeView->selectionModel(),
	    SIGNAL(currentChanged(QModelIndex, QModelIndex)),
	    &dlg, SLOT(accountCurrentChanged(QModelIndex, QModelIndex)));
	dlg.m_ui->treeView->expandAll();

	dlg.m_ui->treeViewButton->setPopupMode(QToolButton::InstantPopup);
	dlg.m_actionAddShadow = dlg.m_treeViewMenu.addAction(
	    tr("Add Shadow Account"), &dlg, SLOT(addShadowAccount()));
	dlg.m_actionRemoveShadow = dlg.m_treeViewMenu.addAction(
	    tr("Remove Shadow Account"), &dlg, SLOT(removeSelectedShadowAccount()));
	dlg.m_ui->treeViewButton->setMenu(&dlg.m_treeViewMenu);

	connect(dlg.m_ui->downloadAccountInfoButton, SIGNAL(clicked()), &dlg,
	    SLOT(refreshAccountInformation()));
	dlg.m_ui->downloadAccountInfoButton->setToolTip(
	    tr("Refresh account information from ISDS."));

	dlg.m_ui->treeView->setCurrentIndex(dlg.m_accountSettingsModel.index(
	    AccountSettingsModel::ROW_REGULAR, 0));

	connect(dlg.m_ui->downloadUserListButton, SIGNAL(clicked()), &dlg,
	    SLOT(refreshUsersInformation()));

	dlg.m_ui->userTableView->setNarrowedLineHeight();
	dlg.m_ui->userTableView->horizontalHeader()->setDefaultAlignment(Qt::AlignLeft);
	dlg.m_ui->userTableView->setSelectionMode(
	    QAbstractItemView::SingleSelection);
	dlg.m_ui->userTableView->setSelectionBehavior(
	    QAbstractItemView::SelectRows);

	dlg.m_ui->userTableView->installEventFilter(
	    new (::std::nothrow) TableHomeEndFilter(dlg.m_ui->userTableView));
	dlg.m_ui->userTableView->installEventFilter(
	    new (::std::nothrow) TableTabIgnoreFilter(dlg.m_ui->userTableView));

	connect(dlg.m_ui->userTableView->selectionModel(),
	    SIGNAL(currentChanged(QModelIndex, QModelIndex)),
	    &dlg, SLOT(userCurrentChanged(QModelIndex, QModelIndex)));

	dlg.m_ui->userListButton->setPopupMode(QToolButton::InstantPopup);
	dlg.m_actionUserAdd = dlg.m_userListMenu.addAction(
	    tr("Add User"), &dlg, SLOT(addUser()));
	dlg.m_actionUserChangeContactAddress = dlg.m_userListMenu.addAction(
	    tr("Change Contact Address"), &dlg, SLOT(modifyUserContactAddress()));
	dlg.m_actionUserChangePrivils = dlg.m_userListMenu.addAction(
	    tr("Change Privileges"), &dlg, SLOT(modifyUserPrivileges()));
	dlg.m_actionUserDelete = dlg.m_userListMenu.addAction(
	    tr("Delete User"), &dlg, SLOT(deleteUser()));
	dlg.m_ui->userListButton->setMenu(&dlg.m_userListMenu);

	if (QDialog::Accepted == dlg.exec()) {
		/* Collect data that might have been changed. */
		const QModelIndex current(dlg.m_ui->treeView->currentIndex());
		if ((dlg.m_shadowAccounts != Q_NULLPTR) &&
		    (AccountSettingsModel::nodeShadowAccountTop ==
		     AccountSettingsModel::nodeType(current))) {
			dlg.m_shadowAccounts->updateAccount(dlg.getContent());
		}
		/* Save edited copy when dialogue accepted. */
		shadowAccounts.load(shadowAccountsCopy);
		return true;
	} else {
		return false;
	}
}

bool DlgCreateAccount::accountExists(const AcntId &acntId,
    const AccountsMap &regularAccounts, const AccountsMap &shadowAccounts)
{
	if (Q_UNLIKELY(!acntId.isValid())) {
		Q_ASSERT(0);
		return true; /* Fake the existence of the invalid account. */
	}

	return regularAccounts.keys().contains(acntId) ||
	    shadowAccounts.acntIds().contains(acntId);
}

QString DlgCreateAccount::htmlAccountInfo(const AcntData &acntData,
    MessageDbSet *dbSet)
{
	Q_ASSERT(acntIdFromData(acntData).isValid());

	QString html;
	DbEntry userEntry;
	DbEntry accountEntry;

	html.append(indentDivStart);
	html.append("<h3>");


	if (acntData.isTestAccount()) {
		html.append(tr("Test environment account"));
	} else {
		html.append(tr("Production environment account"));
	}
	html.append("<br/><img src=\":/dschranka.png\" alt=\"ISDS\">");
	html.append("</h3>");

	html.append(strongAccountInfoLine(tr("Account name"),
	    acntData.accountName()));

	const QString acntDbKey(AccountDb::keyFromLogin(acntData.userName()));
	if (GlobInstcs::accntDbPtr->dbId(acntDbKey).isEmpty()) {
		/*
		 * Generate this message if no account information can
		 * be obtained.
		 */
		html.append(QString("<div><strong>") +
		    tr("Account and user information could not be acquired.") +
		    QString("</strong></div>"));
		goto lastPart;
	}

	html.append("<table cellpadding=\"5\"><tr><td>");
	html.append(QString("<div><strong>") + tr("User information") +
	    QString("</strong></div>"));
	html.append("</td><td width=\"100\"></td><td>");
	html.append(QString("<div><strong>") + tr("Databox information") +
	    QString("</strong></div>"));
	html.append("</td></tr><tr><td>");

	userEntry = GlobInstcs::accntDbPtr->userEntry(acntDbKey);
	html.append(strongAccountInfoLine(tr("User name"), acntData.userName()));
	/* Print non-empty entries. */
	for (int i = 0; i < userinfTbl.knownAttrs.size(); ++i) {
		const QString &key = userinfTbl.knownAttrs[i].first;
		if (userEntry.hasValue(key) &&
		    !userinfTbl.attrProps[key].desc.isEmpty()) {
			switch (userinfTbl.knownAttrs[i].second) {
			case DB_INTEGER:
				if (key == "ic") {
					if (userEntry.value(key).toInt() > 0) {
						html.append(strongAccountInfoLine(
						    userinfTbl.attrProps[key].desc,
						    QString("%1").arg(
						        accountEntry.value(key).toInt(), 8, 10, QLatin1Char('0'))));
					}
				} else if (key == "userPrivils") {
					html.append(strongAccountInfoLineNoEscape(
					    userinfTbl.attrProps[key].desc,
					    Isds::Description::htmlDescrPrivileges(
					        Isds::variant2Privileges(userEntry.value(key)))));
				} else {
					html.append(strongAccountInfoLine(
					    userinfTbl.attrProps[key].desc,
					    QString::number(userEntry.
					        value(key).toInt())));
				}
				break;
			case DB_TEXT:
				if (key == "userType") {
					html.append(strongAccountInfoLine(
					    userinfTbl.attrProps[key].desc,
					    Isds::Description::descrSenderType(
					        Isds::variant2SenderType(userEntry.value(key)))));
				} else {
					html.append(strongAccountInfoLine(
					    userinfTbl.attrProps[key].desc,
					    userEntry.value(key).toString()));
				}
				break;
			case DB_DATE:
				html.append(strongAccountInfoLine(
				    userinfTbl.attrProps[key].desc,
				    dateStrFromDbFormat(
				        userEntry.value(key).toString(),
				        dateDisplayFormat)));
				break;
			default:
				Q_ASSERT(0);
				break;
			}
		}
	}

	html.append("</td><td></td><td>");

	accountEntry = GlobInstcs::accntDbPtr->accountEntry(acntDbKey);
	/* Print non-empty entries. */
	for (int i = 0; i < accntinfTbl.knownAttrs.size(); ++i) {
		const QString &key = accntinfTbl.knownAttrs[i].first;
		if (accountEntry.hasValue(key) &&
		    !accntinfTbl.attrProps[key].desc.isEmpty()) {
			switch (accntinfTbl.knownAttrs[i].second) {
			case DB_INTEGER:
				if (key == "dbState") {
					html.append(strongAccountInfoLine(
					    accntinfTbl.attrProps[key].desc,
					    Isds::Description::descrDbState(
					        Isds::variant2DbState(accountEntry.value(key)))));
				} else if (key == "ic") {
					if (accountEntry.value(key).toInt() > 0) {
						html.append(strongAccountInfoLine(
						    accntinfTbl.attrProps[key].desc,
						    QString("%1").arg(
						        accountEntry.value(key).toInt(), 8, 10, QLatin1Char('0'))));
					}
				} else {
					html.append(strongAccountInfoLine(
					    accntinfTbl.attrProps[key].desc,
					    QString::number(accountEntry.
					        value(key).toInt())));
				}
				break;
			case DB_TEXT:
				html.append(strongAccountInfoLine(
				    accntinfTbl.attrProps[key].desc,
				    accountEntry.value(key).toString()));
				break;
			case DB_BOOLEAN:
				html.append(strongAccountInfoLine(
				    accntinfTbl.attrProps[key].desc,
				    accountEntry.value(key).toBool() ?
				        tr("Yes") : tr("No")));
				break;
			case DB_DATETIME:
				html.append(strongAccountInfoLine(
				    accntinfTbl.attrProps[key].desc,
				    dateTimeStrFromDbFormat(
				        accountEntry.value(key).toString(),
				        dateTimeDisplayFormat)));
				break;
			case DB_DATE:
				html.append(strongAccountInfoLine(
				    accntinfTbl.attrProps[key].desc,
				    dateStrFromDbFormat(
				        accountEntry.value(key).toString(),
				        dateDisplayFormat)));
				break;
			default:
				Q_ASSERT(0);
				break;
			}
		}
	}

	html.append("</td></tr></table>");

lastPart:

	QString info(qDateTimeToDbFormat(GlobInstcs::accntDbPtr->getPwdExpirFromDb(acntDbKey)));
	if (info.isEmpty()) {
		info = tr("unknown or without expiration");
	} else {
		info = info.split(".")[0];
	}

	html.append(strongAccountInfoLine(tr("Password expiration date"),
	    info));

	if (dbSet != Q_NULLPTR) {
		QStringList dbFilePaths = dbSet->fileNames();
		if ((dbFilePaths.size() == 1) &&
		    (SQLiteDb::memoryLocation == dbFilePaths.first())) {
			QString dbFilePath = tr("Database is stored in memory. "
			    "Data will be lost on application exit.");
			html.append(strongAccountInfoLine(
			    tr("Local database file location"), dbFilePath));
		} else {
			foreach (const QString &path, dbFilePaths) {
				html.append(strongAccountInfoLine(
				    tr("Local database file location"), path));
			}
		}
	} else {
		html.append(strongAccountInfoLine2(
		    tr("Account has no own message database associated.")));
	}

	html.append(divEnd);

	return html;
}

void DlgCreateAccount::activateContent(int loginMethodIdx)
{
	m_loginMethod = loginMethodIdx;

	switch (m_loginMethod) {
	case CERTIFICATE:
		m_ui->pwdLabel->setEnabled(false);
		m_ui->pwdLine->setEnabled(false);
		m_ui->showHidePwdButton->setEnabled(false);
		m_ui->rememberPwdCheckBox->setEnabled(false);
		m_ui->certLabel->setEnabled(true);
		m_ui->addCertButton->setEnabled(true);
		m_ui->mepLabel->setEnabled(false);
		m_ui->mepLine->setEnabled(false);
		break;
	case USER_CERTIFICATE:
		m_ui->pwdLabel->setEnabled(true);
		m_ui->pwdLine->setEnabled(true);
		m_ui->showHidePwdButton->setEnabled(m_showViewPwd);
		m_ui->rememberPwdCheckBox->setEnabled(true);
		m_ui->certLabel->setEnabled(true);
		m_ui->addCertButton->setEnabled(true);
		m_ui->mepLabel->setEnabled(false);
		m_ui->mepLine->setEnabled(false);
		break;
	case MEP:
		m_ui->pwdLabel->setEnabled(false);
		m_ui->pwdLine->setEnabled(false);
		m_ui->showHidePwdButton->setEnabled(false);
		m_ui->rememberPwdCheckBox->setEnabled(false);
		m_ui->certLabel->setEnabled(false);
		m_ui->addCertButton->setEnabled(false);
		m_ui->mepLabel->setEnabled(true);
		m_ui->mepLine->setEnabled(true);
		break;
	default:
		m_ui->pwdLabel->setEnabled(true);
		m_ui->pwdLine->setEnabled(true);
		m_ui->showHidePwdButton->setEnabled(m_showViewPwd);
		m_ui->rememberPwdCheckBox->setEnabled(true);
		m_ui->certLabel->setEnabled(false);
		m_ui->addCertButton->setEnabled(false);
		m_ui->mepLabel->setEnabled(false);
		m_ui->mepLine->setEnabled(false);
		break;
	}

	checkInputFields();
}

void DlgCreateAccount::checkInputFields(void)
{
	bool enabled = false;

	switch (m_loginMethod) {
	case CERTIFICATE:
		enabled = !m_ui->acntNameLine->text().trimmed().isEmpty()
		    && !m_ui->usernameLine->text().trimmed().isEmpty()
		    && !m_certPath.isEmpty();
		break;
	case USER_CERTIFICATE:
		enabled = !m_ui->acntNameLine->text().trimmed().isEmpty()
		    && !m_ui->usernameLine->text().trimmed().isEmpty()
		    && !m_ui->pwdLine->text().isEmpty()
		    && !m_certPath.isEmpty();
		break;
	case MEP:
		enabled = !m_ui->acntNameLine->text().trimmed().isEmpty()
		    && !m_ui->usernameLine->text().trimmed().isEmpty()
		    && !m_ui->mepLine->text().isEmpty();
		break;
	default:
		enabled = !m_ui->acntNameLine->text().trimmed().isEmpty()
		    && !m_ui->usernameLine->text().trimmed().isEmpty()
		    && !m_ui->pwdLine->text().isEmpty();
		break;
	}

	m_ui->showHidePwdButton->setEnabled(
	     m_showViewPwd && !m_ui->pwdLine->text().isEmpty());
	m_ui->buttonBox->button(QDialogButtonBox::Ok)->setEnabled(enabled);
}

void DlgCreateAccount::togglePwdVisibility(void)
{
	enum QLineEdit::EchoMode newEchoMode = QLineEdit::Password;

	switch (m_ui->pwdLine->echoMode()) {
	case QLineEdit::Normal:
		break;
	case QLineEdit::Password:
		if ((ACT_ADDNEW != m_action) &&
		    !DlgPinInput::queryPin(*GlobInstcs::pinSetPtr, false, this)) {
			return;
		}
		/* Pin was entered correctly or new account creation. */
		newEchoMode = QLineEdit::Normal;
		break;
	default:
		Q_ASSERT(0);
		break;
	}

	setPwdLineEchoMode(newEchoMode);
}

void DlgCreateAccount::updatePwdVisibilityProgress(void)
{
	--m_viewPwdRemainingCycles;

	if (Q_UNLIKELY(m_viewPwdRemainingCycles < 0)) {
		Q_ASSERT(0);
		m_viewPwdRemainingCycles = 0;
	}

	m_ui->viewPwdProgress->setValue(m_viewPwdRemainingCycles);

	if (Q_UNLIKELY(m_viewPwdRemainingCycles == 0)) {
		setPwdLineEchoMode(QLineEdit::Password);
	}
}

void DlgCreateAccount::addCertificateFile(void)
{
	const QString certFileName(QFileDialog::getOpenFileName(this,
	    tr("Open Certificate"), QString(),
	    tr("Certificate Files (*.pfx *.p12 *.pem)")));
	if (!certFileName.isEmpty()) {
		m_ui->addCertButton->setText(QFileInfo(certFileName).fileName());
		m_ui->addCertButton->setIcon(IconContainer::construcIcon(
		    IconContainer::ICON_DATOVKA_STOCK_KEY));
		m_ui->addCertButton->setToolTip(certFileName);
		m_certPath = certFileName;
		checkInputFields();
	}
}

void DlgCreateAccount::collectAccountData(void)
{
	debugSlotCall();

	/* Store the submitted settings. */
	m_accountInfo = getContent();
}

void DlgCreateAccount::accountCurrentChanged(const QModelIndex &current,
    const QModelIndex &previous)
{
	enum AccountSettingsModel::NodeType previousType =
	    AccountSettingsModel::nodeType(previous);

	switch (previousType) {
	case AccountSettingsModel::nodeShadowAccountTop:
		if (m_shadowAccounts != Q_NULLPTR) {
			m_shadowAccounts->updateAccount(getContent());
		}
		break;
	default:
		break;
	}

	enum AccountSettingsModel::NodeType currentType =
	    AccountSettingsModel::nodeType(current);

	switch (currentType) {
	case AccountSettingsModel::nodeUnknown:
	case AccountSettingsModel::nodeRoot:
		Q_ASSERT(0);
		m_ui->verticalStackedWidget->setCurrentIndex(PAGE_DESCRIPTION);
		m_ui->descriptionLabel->setText(QString());
		break;
	case AccountSettingsModel::nodeRegular:
		m_ui->verticalStackedWidget->setCurrentIndex(PAGE_DESCRIPTION);
		setWindowTitle(tr("Regular accounts"));
		m_ui->descriptionLabel->setText(tr(
		    "Regular account are accounts which you can see listed in the main window of the application.\n\n"
		    "Database files containing message data are associated with these accounts."));
		break;
	case AccountSettingsModel::nodeShadow:
		m_ui->verticalStackedWidget->setCurrentIndex(PAGE_DESCRIPTION);
		setWindowTitle(tr("Shadow accounts"));
		m_ui->descriptionLabel->setText(tr(
		    "Shadow accounts are accounts which exist in the background of the application.\n\n"
		    "Shadow accounts have low access privileges and don't have any databases associated to them. "
		    "They become useful when you have access to a data box to which multiple user accounts have access to. "
		    "In this case an account which can only download message lists (and send messages) can be used as a shadow account.\n\n"
		    "Accounts which can only download message lists (and send messages) can't cause the acceptance of delivered messages. "
		    "These accounts can then be used to automatically check whether there are newly delivered messages without accepting them."));
		break;
	case AccountSettingsModel::nodeRegularAccountTop:
		if (m_regularAccounts != Q_NULLPTR) {
			m_ui->verticalStackedWidget->setCurrentIndex(PAGE_CREDENTIALS);
			setContent(
			    m_regularAccounts->acntData(m_accountSettingsModel.acntId(current)),
			    FC_NONE);
		}
		break;
	case AccountSettingsModel::nodeShadowAccountTop:
		if (m_shadowAccounts != Q_NULLPTR) {
			m_ui->verticalStackedWidget->setCurrentIndex(PAGE_CREDENTIALS);
			setContent(
			    m_shadowAccounts->acntData(m_accountSettingsModel.acntId(current)),
			    FC_SHADOW_ACNT | FC_EDITABLE);
			/*
			 * Disable username editing.
			 * TODO -- Make it less crude.
			 */
			m_ui->usernameLine->setEnabled(false);
		}
		break;
	case AccountSettingsModel::nodeRegularAccountInfo:
		{
			m_ui->verticalStackedWidget->setCurrentIndex(PAGE_PRIVILEGES);
			const AcntId acntId(m_accountSettingsModel.acntId(current));
			loadPrivilegesContent(acntId, currentType);
			setWindowTitle(tr("Account info %1").arg(
			    m_regularAccounts->acntData(acntId).accountName()));
		}
		break;
	case AccountSettingsModel::nodeRegularAccountUsers:
		{
			m_ui->verticalStackedWidget->setCurrentIndex(PAGE_USERS);
			const AcntId acntId(m_accountSettingsModel.acntId(current));
			refreshUsersInformation();
			setWindowTitle(tr("Data-box users"));
		}
		break;
	case AccountSettingsModel::nodeShadowAccountInfo:
		if (m_shadowAccounts != Q_NULLPTR) {
			m_ui->verticalStackedWidget->setCurrentIndex(PAGE_PRIVILEGES);
			const AcntId acntId(m_accountSettingsModel.acntId(current));
			loadPrivilegesContent(acntId, currentType);
			setWindowTitle(tr("Account info %1").arg(
			    m_shadowAccounts->acntData(acntId).accountName()));
		}
		break;
	default:
		break;
	}

	m_actionRemoveShadow->setEnabled(
	    currentType == AccountSettingsModel::nodeShadowAccountTop);
}

/*!
 * @brief Download account information from ISDS and update account database.
 *
 * @param[in,out] acntData Account data may be changed when login credentials
 *                         are not sufficient or are wrong.
 * @param[in]     syncAllActionName Synchronise all action name.
 * @param[in]     parent Parent widget.
 */
static
void downloadAccountInformation(AcntData &acntData,
    const QString &syncAllActionName, QWidget *parent)
{
	{
		Gui::LogInCycle login(syncAllActionName, parent);
		if (!login.logIn(*GlobInstcs::isdsSessionsPtr, acntData)) {
			return;
		}
	}

	const AcntId acntId(acntIdFromData(acntData));

	if (!IsdsHelper::getOwnerInfoFromLogin(acntId)) {
		//TODO: return false;
	}
	if (!IsdsHelper::getUserInfoFromLogin(acntId)) {
		//TODO: return false;
	}
	const QString dbID = GlobInstcs::accntDbPtr->dbId(
	    AccountDb::keyFromLogin(acntId.username()));
	if (!IsdsHelper::getDtInfo(acntId, dbID)) {
		//TODO: return false;
	}
	if (!IsdsHelper::getPasswordInfoFromLogin(acntId)) {
		//TODO: return false;
	}
}

void DlgCreateAccount::addShadowAccount(void)
{
	AcntData newShadowSettings;
	if (!DlgCreateAccount::modify(newShadowSettings,
	        DlgCreateAccount::ACT_ADDNEW, m_ui->syncAllCheckBox->text(),
	        FC_SHADOW_ACNT, this)) {
		return;
	}

	const AcntId newShadowAcntId(acntIdFromData(newShadowSettings));
	if (accountExists(newShadowAcntId, *m_regularAccounts, *m_shadowAccounts)) {
		QMessageBox::warning(this, tr("Adding new shadow account failed"),
		    tr("An account using the supplied username already exists. Account has not been added."),
		    QMessageBox::Ok);
		return;
	}

	downloadAccountInformation(newShadowSettings,
	    m_ui->syncAllCheckBox->text(), this);

	m_shadowAccounts->addAccount(newShadowSettings);

	const AcntId newAcntId = acntIdFromData(newShadowSettings);
	QModelIndex index = m_accountSettingsModel.indexShadowAccountTop(newAcntId);
	if (index.isValid()) {
		m_ui->treeView->selectionModel()->setCurrentIndex(index,
		    QItemSelectionModel::ClearAndSelect);
		m_ui->treeView->expand(index);
	}

	if (!ShadowHelper::isUsableBackgroundAccount(newAcntId,
	        *m_shadowAccounts)) {
		QMessageBox::warning(this, tr("Unusable Shadow Account"),
		    tr("This account won't be used to download message lists on background. View the account information for more detail."),
		    QMessageBox::Ok);
	}
}

void DlgCreateAccount::removeSelectedShadowAccount(void)
{
	if (QMessageBox::Yes != QMessageBox::question(this,
	        tr("Remove Shadow Account?"),
		tr("Do you want to remove the shadow account '%1'?").arg(m_ui->acntNameLine->text()),
		QMessageBox::Yes | QMessageBox::No, QMessageBox::No)) {
		return;
	}

	const QModelIndex current(m_ui->treeView->currentIndex());
	if (Q_UNLIKELY(AccountSettingsModel::nodeShadowAccountTop !=
	        AccountSettingsModel::nodeType(current))) {
		Q_ASSERT(0);
		return;
	}

	m_shadowAccounts->removeAccount(m_accountSettingsModel.acntId(current));

	/* Select parent node. */
	m_ui->treeView->setCurrentIndex(m_accountSettingsModel.index(
	    AccountSettingsModel::ROW_SHADOW, 0));
}

void DlgCreateAccount::refreshAccountInformation(void)
{
	m_ui->downloadAccountInfoButton->setEnabled(false);
	QApplication::processEvents();

	const QModelIndex current(m_ui->treeView->currentIndex());
	enum AccountSettingsModel::NodeType currentType =
	    AccountSettingsModel::nodeType(current);

	switch (currentType) {
	case AccountSettingsModel::nodeRegularAccountInfo:
	case AccountSettingsModel::nodeShadowAccountInfo:
		break;
	default:
		Q_ASSERT(0);
		m_ui->downloadAccountInfoButton->setEnabled(true);
		return;
		break;
	}

	const AcntId acntId(m_accountSettingsModel.acntId(current));

	{
		/* Login and download account information. */

		AcntData acntData;
		switch (currentType) {
		case AccountSettingsModel::nodeRegularAccountInfo:
			acntData = m_regularAccounts->acntData(acntId);
			break;
		case AccountSettingsModel::nodeShadowAccountInfo:
			acntData = m_shadowAccounts->acntData(acntId);
			break;
		default:
			Q_ASSERT(0);
			m_ui->downloadAccountInfoButton->setEnabled(true);
			return;
			break;
		}

		{
			AcntData newAcntData = acntData;
			downloadAccountInformation(newAcntData,
			    m_ui->syncAllCheckBox->text(), this);
			/* Store modifications. */
			if (newAcntData != acntData) {
				switch (currentType) {
				case AccountSettingsModel::nodeRegularAccountInfo:
					/* TODO -- Update regular account data. */
					break;
				case AccountSettingsModel::nodeShadowAccountInfo:
					m_shadowAccounts->updateAccount(newAcntData);
					break;
				default:
					break;
				}
			}
		}
	}

	loadPrivilegesContent(acntId, currentType);

	m_ui->downloadAccountInfoButton->setEnabled(true);
}

/*!
 * @brief Download user list from ISDS.
 *
 * @param[in,out] acntData Account data may be changed when login credentials
 *                         are not sufficient or are wrong.
 * @param[in]     syncAllActionName Synchronise all action name.
 * @param[in]     parent Parent widget.
 * @return List of account users.
 */
static
QList<Isds::DbUserInfoExt2> downloadDataBoxUsers2(AcntData &acntData,
    const QString &syncAllActionName, QWidget *parent)
{
	{
		Gui::LogInCycle login(syncAllActionName, parent);
		if (!login.logIn(*GlobInstcs::isdsSessionsPtr, acntData)) {
			return QList<Isds::DbUserInfoExt2>();
		}
	}

	const AcntId acntId(acntIdFromData(acntData));

	TaskGetDataBoxUsers2 *task =
	    new (::std::nothrow) TaskGetDataBoxUsers2(acntId);
	if (Q_UNLIKELY(task == Q_NULLPTR)) {
		return QList<Isds::DbUserInfoExt2>();
	}

	task->setAutoDelete(false);
	GlobInstcs::workPoolPtr->runSingle(task);

	QList<Isds::DbUserInfoExt2> users;

	if (task->m_success) {
		users = macroStdMove(task->m_users);
	} else {
		QMessageBox::warning(parent,
		    DlgCreateAccount::tr("Failed User List Download"),
		    DlgCreateAccount::tr("User list download failed. ISDS returns: %1")
		        .arg(task->m_isdsLongError));
	}

	delete task;

	return users;
}

/*!
 * @brief Download owner info from ISDS;
 *
 * @param[in,out] acntData Account data may be changed when login credentials
 *                         are not sufficient or are wrong.
 * @param[in]     syncAllActionName Synchronise all action name.
 * @param[in]     parent Parent widget.
 * @return Owner information.
 */
static
Isds::DbOwnerInfoExt2 downloadOwnerInfoFromLogin2(AcntData &acntData,
    const QString &syncAllActionName, QWidget *parent)
{
	{
		Gui::LogInCycle login(syncAllActionName, parent);
		if (!login.logIn(*GlobInstcs::isdsSessionsPtr, acntData)) {
			return Isds::DbOwnerInfoExt2();
		}
	}

	const AcntId acntId(acntIdFromData(acntData));

	TaskGetOwnerInfoFromLogin2 *task =
	    new (::std::nothrow) TaskGetOwnerInfoFromLogin2(acntId);
	if (Q_UNLIKELY(task == Q_NULLPTR)) {
		return Isds::DbOwnerInfoExt2();
	}

	task->setAutoDelete(false);
	GlobInstcs::workPoolPtr->runSingle(task);

	Isds::DbOwnerInfoExt2 owner = macroStdMove(task->m_owner);

	delete task;

	return owner;
}

/*!
 * @brief Download user info from ISDS.
 *
 * @param[in,out] acntData Account data may be changed when login credentials
 *                         are not sufficient or are wrong.
 * @param[in]     syncAllActionName Synchronise all action name.
 * @param[in]     parent Parent widget.
 * @return Account user information.
 */
static
Isds::DbUserInfoExt2 downloadUserInfoFromLogin2(AcntData &acntData,
    const QString &syncAllActionName, QWidget *parent)
{
	{
		Gui::LogInCycle login(syncAllActionName, parent);
		if (!login.logIn(*GlobInstcs::isdsSessionsPtr, acntData)) {
			return Isds::DbUserInfoExt2();
		}
	}

	const AcntId acntId(acntIdFromData(acntData));

	TaskGetUserInfoFromLogin2 *task =
	    new (::std::nothrow) TaskGetUserInfoFromLogin2(acntId);
	if (Q_UNLIKELY(task == Q_NULLPTR)) {
		return Isds::DbUserInfoExt2();
	}

	task->setAutoDelete(false);
	GlobInstcs::workPoolPtr->runSingle(task);

	Isds::DbUserInfoExt2 user = macroStdMove(task->m_user);

	delete task;

	return user;
}

/*!
 * @brief Add data-box user.
 *
 * @param[in,out] acntData Account data may be changed when login credentials
 *                         are not sufficient or are wrong.
 * @param[in]     user User data.
 * @param[out]    message Status description message or error message.
 * @param[in]     syncAllActionName Synchronise all action name.
 * @param[in]     parent Parent widget.
 * @return True on success.
 */
static
bool addDataBoxUser2(AcntData &acntData, const Isds::DbUserInfoExt2 &user,
   QString &message, const QString &syncAllActionName, QWidget *parent)
{
	{
		Gui::LogInCycle login(syncAllActionName, parent);
		if (!login.logIn(*GlobInstcs::isdsSessionsPtr, acntData)) {
			return false;
		}
	}

	const AcntId acntId(acntIdFromData(acntData));

	TaskAddDataBoxUser2 *task =
	    new (::std::nothrow) TaskAddDataBoxUser2(acntId, user);
	if (Q_UNLIKELY(task == Q_NULLPTR)) {
		return false;
	}

	task->setAutoDelete(false);
	GlobInstcs::workPoolPtr->runSingle(task);

	bool ret = task->m_success;

	if (ret) {
		message = DlgCreateAccount::tr("New user has been created successfully.");
		message += "\n\n";
		message += DlgCreateAccount::tr("ISDS server returns: %1")
		    .arg(task->m_status.message());
	} else {
		message = DlgCreateAccount::tr("Couldn't create a new user.");
		message += "\n\n";
		message += DlgCreateAccount::tr("ISDS server returns: %1 %2")
		    .arg(task->m_isdsError).arg(task->m_isdsLongError);
	}

	delete task;

	return ret;
}

/*!
 * @brief Delete data-box user.
 *
 * @param[in,out] acntData Account data may be changed when login credentials
 *                         are not sufficient or are wrong.
 * @param[in]     isdsID User ISDS ID.
 * @param[in]     syncAllActionName Synchronise all action name.
 * @param[in]     parent Parent widget.
 * @return True on success.
 */
static
bool deleteDataBoxUser2(AcntData &acntData, const QString &isdsID,
    const QString &syncAllActionName, QWidget *parent)
{
	{
		Gui::LogInCycle login(syncAllActionName, parent);
		if (!login.logIn(*GlobInstcs::isdsSessionsPtr, acntData)) {
			return false;
		}
	}

	const AcntId acntId(acntIdFromData(acntData));

	TaskDeleteDataBoxUser2 *task =
	    new (::std::nothrow) TaskDeleteDataBoxUser2(acntId, isdsID);
	if (Q_UNLIKELY(task == Q_NULLPTR)) {
		return false;
	}

	task->setAutoDelete(false);
	GlobInstcs::workPoolPtr->runSingle(task);

	bool ret = task->m_success;

	delete task;

	return ret;
}

/*!
 * @brief Update data-box user.
 *
 * @param[in,out] acntData Account data may be changed when login credentials
 *                         are not sufficient or are wrong.
 * @param[in]     isdsID User ISDS ID.
 * @param[in]     user User data.
 * @param[out]    message Status description message or error message.
 * @param[in]     syncAllActionName Synchronise all action name.
 * @param[in]     parent Parent widget.
 * @return True on success.
 */
static
bool updateDataBoxUser2(AcntData &acntData, const QString &isdsID,
    const Isds::DbUserInfoExt2 &user, QString &message,
    const QString &syncAllActionName, QWidget *parent)
{
	{
		Gui::LogInCycle login(syncAllActionName, parent);
		if (!login.logIn(*GlobInstcs::isdsSessionsPtr, acntData)) {
			return false;
		}
	}

	const AcntId acntId(acntIdFromData(acntData));

	TaskUpdateDataBoxUser2 *task =
	    new (::std::nothrow) TaskUpdateDataBoxUser2(acntId, isdsID, user);
	if (Q_UNLIKELY(task == Q_NULLPTR)) {
		return false;
	}

	task->setAutoDelete(false);
	GlobInstcs::workPoolPtr->runSingle(task);

	bool ret = task->m_success;

	if (ret) {
		message = DlgCreateAccount::tr("ISDS server returns: %1")
		    .arg(task->m_status.message());
	} else {
		message = DlgCreateAccount::tr("ISDS server returns: %1 %2")
		    .arg(task->m_isdsError).arg(task->m_isdsLongError);
	}

	delete task;

	return ret;
}

/*!
 * @brief Generate user description HTML.
 *
 * @param[in] userInfo Use description.
 * @return HTML text.
 */
static
QString htmlUserInfo(const Isds::DbUserInfoExt2 &userInfo)
{
	if (Q_UNLIKELY(userInfo.isNull())) {
		return QString();
	}

	QString html;

	html.append(indentDivStart);

	html.append("<h3>");
	html.append(DlgCreateAccount::tr("User"));
	html.append("</h3>");

	/* aifoIsds -- Ignored for now. */

	html.append(indentDivStart);
	html.append(strongAccountInfoLineNoEscape(
	    DlgCreateAccount::tr("ISDS ID"), userInfo.isdsID()));
	html.append(divEnd);

	{
		const Isds::PersonName2 &personName = userInfo.personName();
		if (!personName.isNull()) {
			html.append("<div>&nbsp;</div>");
			html.append(indentDivStart);

			if (!personName.givenNames().isEmpty()) {
				html.append(strongAccountInfoLineNoEscape(
				    DlgCreateAccount::tr("Name"), personName.givenNames()));
			}
			if (!personName.lastName().isEmpty()) {
				html.append(strongAccountInfoLineNoEscape(
				    DlgCreateAccount::tr("Surname"), personName.lastName()));
			}
			if (userInfo.biDate().isValid()) {
				html.append(strongAccountInfoLineNoEscape(
				    DlgCreateAccount::tr("Birth date"), userInfo.biDate().toString("dd.MM.yyyy")));
			}

			html.append(divEnd);
		}
	}

	{
		const Isds::AddressExt2 &address = userInfo.address();
		if (!address.isNull()) {
			html.append("<div>&nbsp;</div>");
			html.append(strongAccountInfoLineNoEscape(
			    DlgCreateAccount::tr("Address"), ""));
			html.append(indentDivStart);

			if (!address.city().isEmpty()) { /* CZ: Obec */
				html.append(strongAccountInfoLineNoEscape(
				    DlgCreateAccount::tr("City"), address.city()));
			}
			if (!address.district().isEmpty()) { /* CZ: Cast obce */
				html.append(strongAccountInfoLineNoEscape(
				    DlgCreateAccount::tr("District"), address.district()));
			}
			if (!address.street().isEmpty()) { /* CZ: Ulice */
				html.append(strongAccountInfoLineNoEscape(
				    DlgCreateAccount::tr("Street"), address.street()));
			}
			if (!address.numberInStreet().isEmpty()) { /* CZ: Cislo orientacni */
				html.append(strongAccountInfoLineNoEscape(
				    DlgCreateAccount::tr("Number in street"), address.numberInStreet()));
			}
			if (!address.numberInMunicipality().isEmpty()) { /* CZ: Cislo popisne/evidencni */
				html.append(strongAccountInfoLineNoEscape(
				    DlgCreateAccount::tr("Number in municipality"), address.numberInMunicipality()));
			}
			if (!address.zipCode().isEmpty()) { /* CZ: PSC */
				html.append(strongAccountInfoLineNoEscape(
				    DlgCreateAccount::tr("Postal code"), address.zipCode()));
			}
			if (!address.state().isEmpty()) { /* CZ: Stat */
				html.append(strongAccountInfoLineNoEscape(
				    DlgCreateAccount::tr("State"), address.state()));
			}

			html.append(divEnd);
		}
	}

	{
		const QString &ic = userInfo.ic();
		const QString &firmName = userInfo.firmName();
		if ((!ic.isEmpty()) || (!firmName.isEmpty())) {
			html.append("<div>&nbsp;</div>");
			html.append(strongAccountInfoLineNoEscape(
			    DlgCreateAccount::tr("Firm"), ""));
			html.append(indentDivStart);

			if (!ic.isEmpty()) {
				html.append(strongAccountInfoLineNoEscape(
				    DlgCreateAccount::tr("ic"), ic));
			}
			if (!firmName.isEmpty()) {
				html.append(strongAccountInfoLineNoEscape(
				    DlgCreateAccount::tr("Name"), firmName));
			}

			html.append(divEnd);
		}
	}

	{
		const QString &street = userInfo.caStreet();
		const QString &city = userInfo.caCity();
		const QString &zip = userInfo.caZipCode();
		const QString &state = userInfo.caState();
		if ((!street.isEmpty()) || (!city.isEmpty()) ||
		    (!zip.isEmpty()) || (!state.isEmpty())) {
			html.append("<div>&nbsp;</div>");
			html.append(strongAccountInfoLineNoEscape(
			    DlgCreateAccount::tr("Contact address"), ""));
			html.append(indentDivStart);

			if (!city.isEmpty()) { /* CZ: Obec */
				html.append(strongAccountInfoLineNoEscape(
				    DlgCreateAccount::tr("City"), city));
			}
			if (!street.isEmpty()) { /* CZ: Ulice */
				html.append(strongAccountInfoLineNoEscape(
				    DlgCreateAccount::tr("Street"), street));
			}
			if (!zip.isEmpty()) { /* CZ: PSC */
				html.append(strongAccountInfoLineNoEscape(
				    DlgCreateAccount::tr("Postal code"), zip));
			}
			if (!state.isEmpty()) { /* CZ: Stat */
				html.append(strongAccountInfoLineNoEscape(
				    DlgCreateAccount::tr("State"), state));
			}

			html.append(divEnd);
		}
	}

	html.append("<div>&nbsp;</div>");
	html.append(strongAccountInfoLineNoEscape(
	    DlgCreateAccount::tr("User type"),
	    Isds::Description::descrUserType(userInfo.userType())));
	html.append(strongAccountInfoLineNoEscape(
	    DlgCreateAccount::tr("Privileges"),
	    Isds::Description::htmlDescrPrivileges(userInfo.userPrivils())));

	html.append(divEnd);

	return html;
}

void DlgCreateAccount::userCurrentChanged(const QModelIndex &current,
    const QModelIndex &previous)
{
	Q_UNUSED(previous);

	if (Q_UNLIKELY(!current.isValid())) {
		m_ui->userTextInfo->setText(QString());
		return;
	}

	const QModelIndex userIndex = m_userListProxyModel.mapToSource(current);

	m_ui->userTextInfo->setText(
	    htmlUserInfo(m_usersModel.contentAtRow(userIndex.row())));

	/* Don't allow modifying own permissions or deleting yourself. */
	bool yoursef =
	    m_usersModel.contentAtRow(userIndex.row()).isdsID() == _user.isdsID();

	m_actionUserChangeContactAddress->setEnabled(yoursef);
	m_actionUserChangePrivils->setEnabled(!yoursef);
	m_actionUserDelete->setEnabled(!yoursef);
}

void DlgCreateAccount::refreshUsersInformation(void)
{
	m_ui->downloadUserListButton->setEnabled(false);
	m_actionUserAdd->setEnabled(false);
	m_actionUserChangeContactAddress->setEnabled(false);
	m_actionUserChangePrivils->setEnabled(false);
	m_actionUserDelete->setEnabled(false);
	m_ui->userListInfoLabel->setText(QString());

	QApplication::processEvents();

	const QModelIndex current(m_ui->treeView->currentIndex());
	enum AccountSettingsModel::NodeType currentType =
	    AccountSettingsModel::nodeType(current);

	switch (currentType) {
	case AccountSettingsModel::nodeRegularAccountUsers:
		break;
	default:
		Q_ASSERT(0);
		m_ui->downloadUserListButton->setEnabled(true);
		return;
		break;
	}

	const AcntId acntId(m_accountSettingsModel.acntId(current));

	QList<Isds::DbUserInfoExt2> allUsers;
	Isds::DbOwnerInfoExt2 owner;
	Isds::DbUserInfoExt2 user;

	{
		/* Login and download list of users. */

		AcntData acntData;
		switch (currentType) {
		case AccountSettingsModel::nodeRegularAccountUsers:
			acntData = m_regularAccounts->acntData(acntId);
			break;
		default:
			Q_ASSERT(0);
			m_ui->downloadUserListButton->setEnabled(true);
			return;
			break;
		}

		{
			AcntData newAcntData = acntData;
			owner = downloadOwnerInfoFromLogin2(
			    newAcntData, m_ui->syncAllCheckBox->text(),
			    this);
			if (!owner.isNull()) {
				/*
				 * Don't continue if owner if couldn't be
				 * downloaded.
				 */
				user = downloadUserInfoFromLogin2(
				    newAcntData, m_ui->syncAllCheckBox->text(),
				    this);
				allUsers = downloadDataBoxUsers2(newAcntData,
				    m_ui->syncAllCheckBox->text(), this);
			}
			/* Store modifications. */
			if (newAcntData != acntData) {
				switch (currentType) {
				case AccountSettingsModel::nodeRegularAccountUsers:
					/* TODO -- Update regular account data. */
					break;
				default:
					break;
				}
			}
		}
	}

	if (!allUsers.isEmpty()) {
		m_ui->userListInfoLabel->setText(
		    tr("The list contains users who have access to this data box."));
	} else {
		m_ui->userListInfoLabel->setText(
		    tr("The list of users having access to this data box could not be obtained from the server."));
	}

	if (allUsers.size() > 0) {
		m_usersModel.setContent(allUsers, user.isdsID());
	} else if (!user.isNull()) {
		/* Always show current user if available. */
		QList<Isds::DbUserInfoExt2> list;
		list.append(user);
		m_usersModel.setContent(list, user.isdsID());
	} else {
		/* Clear the model else. */
		m_usersModel.setContent(allUsers, QString());
	}
	_owner = owner;
	_user = user;
	m_ui->userTextInfo->setText(QString());
	m_ui->userTextInfo->setReadOnly(true);

	m_actionUserAdd->setEnabled(allUsers.size() > 0);
	m_ui->downloadUserListButton->setEnabled(true);
}

/*!
 * @brief Construct street house address line.
 *
 * @param[in] addr Address.
 * @return Line containing of street and house number line.
 */
static
QString constructHouseLine(const Isds::AddressExt2 &addr)
{
	bool addEvNoInfo = false;
	QString houseLine;

	if (!addr.street().isEmpty()) {
		houseLine = addr.street() + QChar(' ');
	} else if (!addr.district().isEmpty()) {
		houseLine = addr.district() + QChar(' ');
	} else {
		/* Add the 'ev.no.' string if evidence number supplied. */
		addEvNoInfo = true;
	}

	QString numberInMunicipality = addr.numberInMunicipality();
	const QString &numberInStreet = addr.numberInStreet();
	if (DlgUserExt2::isEvidenceNumber(numberInMunicipality)) {
		numberInMunicipality =
		    DlgUserExt2::stripEvidenceNumber(numberInMunicipality);
		if (addEvNoInfo) {
			/* CZ: "ev. č." */
			numberInMunicipality =
			    DlgCreateAccount::tr("ev. no.") + numberInMunicipality;
		}
	}

	if ((!numberInMunicipality.isEmpty()) && (!numberInStreet.isEmpty())) {
		houseLine += numberInMunicipality + QChar('/') + numberInStreet;
	} else if (!numberInMunicipality.isEmpty()) {
		houseLine += numberInMunicipality;
	} else if (!numberInStreet.isEmpty()) {
		houseLine += numberInStreet;
	}

	return houseLine;
}

/*
 * @brief Construct address to be displayed in a brief summary.
 *
 * @param[in] user User info.
 * @return Address on several lines.
 */
static
QString constructMailAddress(const Isds::DbUserInfoExt2 &user)
{
	QString nameStr;
	{
		const Isds::PersonName2 &person = user.personName();
		if (!person.isNull()) {
			nameStr = person.givenNames() + QChar(' ') + person.lastName();
			/* CZ: Narozen(a) */
			nameStr += QChar(' ') + DlgCreateAccount::tr("Born on") +
			    QChar(' ') + user.biDate().toString("dd.MM.yyyy");
		}
	}

	QString addrStr;
	{
		const QString &street = user.caStreet();
		const QString &city = user.caCity();
		const QString &zip = user.caZipCode();
		const QString &state = user.caState();
		if ((!street.isEmpty()) || (!city.isEmpty()) ||
		    (!zip.isEmpty()) || (!state.isEmpty())) {
			addrStr = nameStr;
			addrStr += QChar('\n') + street;
			addrStr += QChar('\n') + zip + QChar(' ') + city;
			addrStr += QChar('\n') + state;
			return addrStr;
		}
	}
	{
		const Isds::AddressExt2 &addr = user.address();
		if (!addr.isNull()) {
			addrStr = nameStr;
			addrStr += QChar('\n') + constructHouseLine(addr);
			addrStr += QChar('\n') + addr.zipCode() + QChar(' ') + addr.city();
			addrStr += QChar('\n') + addr.state();
			return addrStr;
		}
	}

	return QString();
}

void DlgCreateAccount::addUser(void)
{
	/* View empty user dialogue window. */
	Isds::DbUserInfoExt2 newUser;
again:
	if (!DlgUserExt2::edit(newUser, _owner, DlgUserExt2::EC_ALL, this)) {
		/* Aborted by user. */
		return;
	}

	/*
	 * Message taken from web interface:
	 *
	 * Na Vaši žádost bude zřízena nová pověřená osoba k datové schránce.
	 *
	 * Pokud se podaří tuto osobu ztotožnit s registrem obyvatel a má zpřístupněnou datovou schránku fyzické osoby, budou jí přístupové údaje dodány přímo do této datové schránky.
	 * Jinak budou přístupové údaje zaslány dopisem do vlastních rukou (se žlutým pruhem) na následující adresu:
	 *
	 * adressa
	 *
	 * Prosím ujistěte se, že adresa je správná a daná osoba je na této adrese dohledatelná.
	 * V opačném případě nebude moci být dopis s přístupovými údaji doručen a daná osoba nebude mít do schránky přístup.
	 *
	 * Poznámka: Pokud bude zjištěno, že tato osoba má v Základním registru obyvatel zadanou doručovací adresu, pak budou přístupové údaje zaslány na ni, bez ohledu na výše zadanou adresu.
	 */

	{
		QString message = tr("Because of your request a new %1 is going to be created for the data box.")
		        .arg(Isds::Description::descrUserType(newUser.userType()).toLower());
		message += "\n\n";
		message += tr("If the person can be identified in the Population register and if it owns an accessible natural person data box then the login credentials will be sent directly into this data box.");
		message += QChar(' ') + tr("Otherwise the login credentials will be sent in a letter into own hands to the address:");
		message += "\n\n";
		message += constructMailAddress(newUser);
		message += "\n\n";
		message += tr("Please ensure yourself that the address is correct and the person can be found on this address.");
		message += QChar(' ') + tr("In opposite case the letter containing the login credentials won't be able to be handed in and the given person won't gain access to the data box.");
		message += "\n\n";
		message += tr("Note: If it is found that the given person's delivery address is held in the Basic population register then the login credentials will be sent to the found address regardless to the above mentioned address.");

		if (QMessageBox::Yes != QMessageBox::question(this,
		        tr("Add a New User?"), message,
		        QMessageBox::Yes | QMessageBox::Cancel, QMessageBox::Cancel)) {
			goto again;
		}
	}

	const QModelIndex current(m_ui->treeView->currentIndex());
	enum AccountSettingsModel::NodeType currentType =
	    AccountSettingsModel::nodeType(current);

	switch (currentType) {
	case AccountSettingsModel::nodeRegularAccountUsers:
		break;
	default:
		Q_ASSERT(0);
		return;
		break;
	}

	const AcntId acntId(m_accountSettingsModel.acntId(current));

	AcntData acntData;
	switch (currentType) {
	case AccountSettingsModel::nodeRegularAccountUsers:
		acntData = m_regularAccounts->acntData(acntId);
		break;
	default:
		Q_ASSERT(0);
		return;
		break;
	}

	bool success = false;
	QString message;
	{
		AcntData newAcntData = acntData;
		success = addDataBoxUser2(newAcntData, newUser, message,
		    m_ui->syncAllCheckBox->text(), this);
		/* Store modifications. */
		if (newAcntData != acntData) {
			switch (currentType) {
			case AccountSettingsModel::nodeRegularAccountUsers:
				/* TODO -- Update regular account data. */
				break;
			default:
				break;
			}
		}
	}

	/*
	 * Message taken from web interface:
	 *
	 * Nový uživatel datové schránky byl úspěšně založen a byla mu nastavena požadovaná práva.
	 * Kdykoliv v budoucnu můžete jeho oprávnění změnit nebo zrušit.
	 *
	 * Přihlašovací údaje budou novému uživateli zaslány poštou na adresu uvedenou v této žádosti, nebo, pokud byl ztotožněn v registru obyvatel a má zpřístupněnou datovou schránku fyzické osoby, budou dodány do jeho schránky.
	 * Teprve poté se bude moci do této datové schránky přihlásit a provádět úkony podle přidělených práv.
	 *
	 * Poznámka: První přihlášení uživatele musí proběhnout přes webový portál ISDS, kde si změní přidělené heslo.
	 * Po této operaci pak bude moci používat aplikace třetích stran k přístupu do této schránky.
	 */

	if (success) {
		if (!acntData.isTestAccount()) {
			message += "\n\n" + tr("A new data-box user has successfully been created and the requested privileges have been set.") +
			    QChar(' ') + tr("You can change or withdraw his privileges at any time in future.");
			message += "\n\n" + tr("The login credentials are going delivered to the new user in a letter to the supplied address or if he was identified in the Population register and owns an accessible natural person data box then the login credentials will be sent directly into his data box.") +
			    QChar(' ') + tr("After receiving his login credentials the user will be able to log into the data box and perform actions according to the supplied user privileges.");
			message += "\n\n" + tr("Note: The user must log into the ISDS web interface to activate the created user account and change his assigned password.") +
			    QChar(' ') + tr("After this operation he will be able to use third party applications to access the data box.");
		} else {
			/* Notify the user about the fact the message contains login credentials. */
			message += "\n\n" + tr("You are using a testing environment account.") +
			    QChar(' ') + tr("The server response displayed above directly contains the login credentials.");
			message += "\n\n" + tr("Note: The user must log into the testing instance of the ISDS web interface to activate the created user account and change his assigned password.") +
			    QChar(' ') + tr("After this operation he will be able to use third party applications to access the data box.");
		}

		QMessageBox::information(this, tr("Adding new user succeeded"),
		    message, QMessageBox::Ok);
	} else {
		QMessageBox::warning(this, tr("Adding new user failed"),
		    message, QMessageBox::Ok);
		goto again;
		return;
	}

	/* Reload user list. */
	refreshUsersInformation();
}

void DlgCreateAccount::modifyUserContactAddress(void)
{
	/* Get information about the selected user. */
	Isds::DbUserInfoExt2 user;
	{
		QModelIndexList indexes =
		    m_ui->userTableView->selectionModel()->selectedRows(0);

		if (Q_UNLIKELY(indexes.size() != 1)) {
			return;
		}

		int userRow =
		    m_userListProxyModel.mapToSource(indexes.at(0)).row();
		user = m_usersModel.contentAtRow(userRow);
	}

	Isds::DbUserInfoExt2 modifiedUser = user;
	if (!DlgUserExt2::edit(modifiedUser, _owner, DlgUserExt2::EC_CA_ADDR, this)) {
		/* Aborted by user. */
		return;
	}

	/* Take only contact address. */
	{
		QString caStreet = modifiedUser.caStreet();
		QString caCity = modifiedUser.caCity();
		QString caZipCode = modifiedUser.caZipCode();
		QString caState = modifiedUser.caState();
		modifiedUser = user;
		modifiedUser.setCaStreet(macroStdMove(caStreet));
		modifiedUser.setCaCity(macroStdMove(caCity));
		modifiedUser.setCaZipCode(macroStdMove(caZipCode));
		modifiedUser.setCaState(macroStdMove(caState));
	}

	const QModelIndex current(m_ui->treeView->currentIndex());
	enum AccountSettingsModel::NodeType currentType =
	    AccountSettingsModel::nodeType(current);

	switch (currentType) {
	case AccountSettingsModel::nodeRegularAccountUsers:
		break;
	default:
		Q_ASSERT(0);
		return;
		break;
	}

	const AcntId acntId(m_accountSettingsModel.acntId(current));

	AcntData acntData;
	switch (currentType) {
	case AccountSettingsModel::nodeRegularAccountUsers:
		acntData = m_regularAccounts->acntData(acntId);
		break;
	default:
		Q_ASSERT(0);
		return;
		break;
	}

	bool success = false;
	QString message;
	{
		AcntData newAcntData = acntData;
		success = updateDataBoxUser2(newAcntData, modifiedUser.isdsID(),
		    modifiedUser, message, m_ui->syncAllCheckBox->text(), this);
		/* Store modifications. */
		if (newAcntData != acntData) {
			switch (currentType) {
			case AccountSettingsModel::nodeRegularAccountUsers:
				/* TODO -- Update regular account data. */
				break;
			default:
				break;
			}
		}
	}

	if (success) {
		QMessageBox::information(this, tr("User modification succeeded"),
		    message, QMessageBox::Ok);
	} else {
		QMessageBox::warning(this, tr("User modification failed"),
		    message, QMessageBox::Ok);
		return;
	}

	/* Reload user list. */
	refreshUsersInformation();
}

void DlgCreateAccount::modifyUserPrivileges(void)
{
	/* Get information about the selected user. */
	Isds::DbUserInfoExt2 user;
	{
		QModelIndexList indexes =
		    m_ui->userTableView->selectionModel()->selectedRows(0);

		if (Q_UNLIKELY(indexes.size() != 1)) {
			return;
		}

		int userRow =
		    m_userListProxyModel.mapToSource(indexes.at(0)).row();
		user = m_usersModel.contentAtRow(userRow);
	}

	Isds::DbUserInfoExt2 modifiedUser = user;
	if (!DlgUserExt2::edit(modifiedUser, _owner, DlgUserExt2::EC_PERMISSIONS, this)) {
		/* Aborted by user. */
		return;
	}

	/* Take only privileges. */
	{
		Isds::Type::Privileges privileges = modifiedUser.userPrivils();
		modifiedUser = user;
		modifiedUser.setUserPrivils(privileges);
	}

	const QModelIndex current(m_ui->treeView->currentIndex());
	enum AccountSettingsModel::NodeType currentType =
	    AccountSettingsModel::nodeType(current);

	switch (currentType) {
	case AccountSettingsModel::nodeRegularAccountUsers:
		break;
	default:
		Q_ASSERT(0);
		return;
		break;
	}

	const AcntId acntId(m_accountSettingsModel.acntId(current));

	AcntData acntData;
	switch (currentType) {
	case AccountSettingsModel::nodeRegularAccountUsers:
		acntData = m_regularAccounts->acntData(acntId);
		break;
	default:
		Q_ASSERT(0);
		return;
		break;
	}

	bool success = false;
	QString message;
	{
		AcntData newAcntData = acntData;
		success = updateDataBoxUser2(newAcntData, modifiedUser.isdsID(),
		    modifiedUser, message, m_ui->syncAllCheckBox->text(), this);
		/* Store modifications. */
		if (newAcntData != acntData) {
			switch (currentType) {
			case AccountSettingsModel::nodeRegularAccountUsers:
				/* TODO -- Update regular account data. */
				break;
			default:
				break;
			}
		}
	}

	if (success) {
		QMessageBox::information(this, tr("User modification succeeded"),
		    message, QMessageBox::Ok);
	} else {
		QMessageBox::warning(this, tr("User modification failed"),
		    message, QMessageBox::Ok);
		return;
	}

	/* Reload user list. */
	refreshUsersInformation();
}

void DlgCreateAccount::deleteUser(void)
{
	int userRow;

	/* Get information about the selected user. */
	{
		QModelIndexList indexes =
		    m_ui->userTableView->selectionModel()->selectedRows(0);

		if (Q_UNLIKELY(indexes.size() != 1)) {
			return;
		}

		userRow = m_userListProxyModel.mapToSource(indexes.at(0)).row();
	}

	if (Q_UNLIKELY(userRow < 0)) {
		Q_ASSERT(0);
		return;
	}

	/* Ask whether to delete the selected user. */
	QString isdsID;
	QString name;
	{
		const Isds::DbUserInfoExt2 &user =
		     m_usersModel.contentAtRow(userRow);
		{
			const Isds::PersonName2 &personName = user.personName();

			name = personName.givenNames() +
			    QChar(' ') + personName.lastName();
		}

		if (QMessageBox::Yes != QMessageBox::question(this,
		        tr("Delete User?"),
		        tr("Do you want to delete the user '%1'?").arg(name),
		        QMessageBox::Yes | QMessageBox::No, QMessageBox::No)) {
			return;
		}

		isdsID = user.isdsID();
	}

	const QModelIndex current(m_ui->treeView->currentIndex());
	enum AccountSettingsModel::NodeType currentType =
	    AccountSettingsModel::nodeType(current);

	switch (currentType) {
	case AccountSettingsModel::nodeRegularAccountUsers:
		break;
	default:
		Q_ASSERT(0);
		return;
		break;
	}

	const AcntId acntId(m_accountSettingsModel.acntId(current));

	AcntData acntData;
	switch (currentType) {
	case AccountSettingsModel::nodeRegularAccountUsers:
		acntData = m_regularAccounts->acntData(acntId);
		break;
	default:
		Q_ASSERT(0);
		return;
		break;
	}

	bool success = false;
	QString message;
	{
		AcntData newAcntData = acntData;
		success = deleteDataBoxUser2(newAcntData, isdsID,
		    m_ui->syncAllCheckBox->text(), this);
		/* Store modifications. */
		if (newAcntData != acntData) {
			switch (currentType) {
			case AccountSettingsModel::nodeRegularAccountUsers:
				/* TODO -- Update regular account data. */
				break;
			default:
				break;
			}
		}
	}

	if (!success) {
		QMessageBox::warning(this, tr("Adding new user failed"),
		    tr("User '%1' could not be deleted.").arg(name),
		    QMessageBox::Ok);
		return;
	}

	/* Reload user list. */
	refreshUsersInformation();
}

/*!
 * @brief Conversion function.
 *
 * @param[in] method Login method.
 * @return Combo box index.
 */
static
enum LoginMethodIndex loginMethodToIndex(enum AcntSettings::LoginMethod method)
{
	switch (method) {
	case AcntSettings::LIM_UNAME_PWD:
		return USER_NAME;
		break;
	case AcntSettings::LIM_UNAME_CRT:
		return CERTIFICATE;
		break;
	case AcntSettings::LIM_UNAME_PWD_CRT:
		return USER_CERTIFICATE;
		break;
	case AcntSettings::LIM_UNAME_PWD_HOTP:
		return HOTP;
		break;
	case AcntSettings::LIM_UNAME_PWD_TOTP:
		return TOTP;
		break;
	case AcntSettings::LIM_UNAME_MEP:
		return MEP;
		break;
	default:
		Q_ASSERT(0);
		return USER_NAME;
		break;
	}
}

/*!
 * @brief Conversion function.
 *
 * @param[in] Combo box index.
 * @return Login method.
 */
static
enum AcntSettings::LoginMethod indexToLoginMethod(int index)
{
	switch (index) {
	case USER_NAME:
		return AcntSettings::LIM_UNAME_PWD;
		break;
	case CERTIFICATE:
		return AcntSettings::LIM_UNAME_CRT;
		break;
	case USER_CERTIFICATE:
		return AcntSettings::LIM_UNAME_PWD_CRT;
		break;
	case HOTP:
		return AcntSettings::LIM_UNAME_PWD_HOTP;
		break;
	case TOTP:
		return AcntSettings::LIM_UNAME_PWD_TOTP;
		break;
	case MEP:
		return AcntSettings::LIM_UNAME_MEP;
		break;
	default:
		Q_ASSERT(0);
		return AcntSettings::LIM_UNKNOWN;
		break;
	}
}

void DlgCreateAccount::setContent(const AcntData &acntData, int properties)
{
	m_accountInfo = acntData;

	if (Q_UNLIKELY(acntData.userName().isEmpty())) {
		m_ui->infoLabel->setEnabled(false);
		m_ui->acntNameLine->clear();
		m_ui->usernameLine->clear();
		m_ui->testAcntCheckBox->setCheckState(Qt::Unchecked);
		m_ui->loginMethodComboBox->setCurrentIndex(USER_NAME);
		m_ui->pwdLine->clear();
		m_ui->rememberPwdCheckBox->setCheckState(Qt::Unchecked);
		m_ui->mepLine->clear();
		m_ui->syncAllCheckBox->setEnabled((properties & FC_EDITABLE) && (!(properties & FC_SHADOW_ACNT)));
		m_ui->syncAllCheckBox->setCheckState(Qt::Unchecked);
		return;
	}

	m_ui->infoLabel->setEnabled(true);

	QString windowTitle(this->windowTitle());

	switch (m_action) {
	case ACT_EDIT:
		windowTitle = tr("Update account %1")
		    .arg(acntData.accountName());
		m_ui->infoLabel->setEnabled(true);
		m_ui->acntNameLine->setEnabled(true);
		break;
	case ACT_PWD:
		windowTitle = tr("Enter password for account %1")
		    .arg(acntData.accountName());
		m_ui->infoLabel->setEnabled(false);
		m_ui->acntNameLine->setEnabled(false);
		m_ui->loginMethodComboBox->setEnabled(false);
		m_ui->addCertButton->setEnabled(false);
		break;
	case ACT_CERT:
		windowTitle = tr("Set certificate for account %1")
		    .arg(acntData.accountName());
		m_ui->infoLabel->setEnabled(false);
		m_ui->acntNameLine->setEnabled(false);
		m_ui->loginMethodComboBox->setEnabled(false);
		m_ui->pwdLine->setEnabled(false);
		m_ui->showHidePwdButton->setEnabled(false);
		break;
	case ACT_CERTPWD:
		windowTitle = tr("Enter password/certificate for account %1")
		    .arg(acntData.accountName());
		m_ui->infoLabel->setEnabled(false);
		m_ui->acntNameLine->setEnabled(false);
		m_ui->loginMethodComboBox->setEnabled(false);
		break;
	case ACT_MEP:
		windowTitle = tr("Enter communication code for account %1")
		    .arg(acntData.accountName());
		m_ui->infoLabel->setEnabled(false);
		m_ui->acntNameLine->setEnabled(false);
		m_ui->loginMethodComboBox->setEnabled(false);
		break;
	default:
		Q_ASSERT(0);
		break;
	}

	setWindowTitle(windowTitle);
	m_ui->acntNameLine->setText(acntData.accountName());
	m_ui->usernameLine->setText(acntData.userName());
	m_ui->usernameLine->setEnabled((m_action == ACT_EDIT) || (m_action == ACT_ADDNEW));
	m_ui->testAcntCheckBox->setEnabled(m_action == ACT_ADDNEW);

	enum LoginMethodIndex itemIdx =
	    loginMethodToIndex(acntData.loginMethod());
	m_ui->loginMethodComboBox->setCurrentIndex(itemIdx);
	activateContent(itemIdx);

	m_ui->testAcntCheckBox->setChecked(acntData.isTestAccount());
	m_ui->pwdLine->setText(acntData.password());
	m_ui->rememberPwdCheckBox->setChecked(acntData.rememberPwd());
	m_ui->mepLine->setText(acntData.mepToken());
	m_ui->syncAllCheckBox->setChecked((!(properties & FC_SHADOW_ACNT)) && acntData.syncWithAll());

	if (!acntData.p12File().isEmpty()) {
		QFileInfo fileInfo(acntData.p12File());
		m_ui->addCertButton->setText(fileInfo.fileName());
		if (fileInfo.exists() && fileInfo.isFile() &&
		    fileInfo.isReadable()) {
			m_ui->addCertButton->setIcon(IconContainer::construcIcon(
			    IconContainer::ICON_DATOVKA_STOCK_KEY));
			m_ui->addCertButton->setToolTip(acntData.p12File());
		} else {
			m_ui->addCertButton->setIcon(IconContainer::construcIcon(
			    IconContainer::ICON_WARNING));
			m_ui->addCertButton->setToolTip(tr("File does not exists or cannot be read."));
		}
		m_certPath = QDir::toNativeSeparators(acntData.p12File());
	} else {
		m_ui->addCertButton->setIcon(IconContainer::construcIcon(
		    IconContainer::ICON_PLUS));
		m_ui->addCertButton->setText(tr("Add"));
		m_ui->addCertButton->setToolTip(tr("Select a certificate."));
	}

	if (!(properties & FC_EDITABLE)) {
		m_ui->infoLabel->setEnabled(false);
		m_ui->acntNameLine->setEnabled(false);
		m_ui->usernameLine->setEnabled(false);
		m_ui->testAcntCheckBox->setEnabled(false);
		m_ui->loginMethodComboBox->setEnabled(false);
		m_ui->pwdLine->setEnabled(false);
		m_ui->rememberPwdCheckBox->setEnabled(false);
		m_ui->mepLine->setEnabled(false);
		m_ui->addCertButton->setEnabled(false);
	}
	/* Disable for shadow accounts. */
	m_ui->syncAllCheckBox->setEnabled(
	    (properties & FC_EDITABLE) && (!(properties & FC_SHADOW_ACNT)));

	checkInputFields();
}

void DlgCreateAccount::loadPrivilegesContent(const AcntId &acntId,
    enum AccountSettingsModel::NodeType nodeType)
{
	switch (nodeType) {
	case AccountSettingsModel::nodeRegularAccountInfo:
		m_ui->accountInfoLabel->setVisible(m_shadowAccounts != Q_NULLPTR);
		m_ui->downloadAccountInfoButton->setVisible(true);
		if (m_shadowAccounts != Q_NULLPTR) { /* m_ui->accountInfoLabel->isVisible() */
			QList<AcntId> shadowAcnts =
			    ShadowHelper::usableBackgroundAccounts(
			        acntId, *m_regularAccounts, *m_shadowAccounts);
			if (!shadowAcnts.isEmpty()) {
				QString text = tr("The following shadow accounts can be used by this account:");
				foreach (const AcntId &sAcntId, shadowAcnts) {
					const AcntData acntData = m_shadowAccounts->acntData(sAcntId);
					text += "\n- " + acntData.accountName() + " (" + acntData.userName() + ")";
				}
				m_ui->accountInfoLabel->setText(text);
			} else {
				m_ui->accountInfoLabel->setText(
				    tr("There is no shadow account available for this account."));
			}
		}
		m_ui->accountTextInfo->setHtml(htmlAccountInfo(
		    m_regularAccounts->acntData(acntId),
		    GlobInstcs::msgDbsPtr->accessOpenedDbSet(acntId)));
		break;
	case AccountSettingsModel::nodeShadowAccountInfo:
		m_ui->accountInfoLabel->setVisible(m_shadowAccounts != Q_NULLPTR);
		m_ui->downloadAccountInfoButton->setVisible(true);
		if (m_shadowAccounts != Q_NULLPTR) { /* m_ui->accountInfoLabel->isVisible() */
			if (ShadowHelper::isUsableBackgroundAccount(acntId,
			        *m_shadowAccounts)) {
				QString text = tr(
				    "This account can be used to download message lists in background because:\n"
				    "- It cannot download any received message because of low access privileges and thus won't cause any message to be accepted.\n"
				    "- It uses a login procedure which does not require user interaction (username and password) and all login credentials are remembered by the application."
				);
				QList<AcntId> regularAcnts =
				    ShadowHelper::availableForegroundAccounts(
				        acntId, *m_regularAccounts, *m_shadowAccounts);
				if (!regularAcnts.isEmpty()) {
					text += "\n\n" + tr("The following regular accounts may utilise this account:");
					foreach (const AcntId &rAcntId, regularAcnts) {
						const AcntData acntData = m_regularAccounts->acntData(rAcntId);
						text += "\n- " + acntData.accountName() + " (" + acntData.userName() + ")";
					}
				}
				m_ui->accountInfoLabel->setText(text);
			} else {
				m_ui->accountInfoLabel->setText(
				    tr("This account won't be used to download message lists in background because it doesn't meet the following requirements:\n"
				        "- The account must have low access privileges. It must be able to download message lists but it must be prohibited from downloading received messages.\n"
				        "- It must use a login procedure which does not require user interaction (username and password) and complete login credentials must be remembered by the application."));
			}
		}
		if (m_shadowAccounts != Q_NULLPTR) {
			m_ui->accountTextInfo->setHtml(htmlAccountInfo(
			    m_shadowAccounts->acntData(acntId),
			    GlobInstcs::msgDbsPtr->accessOpenedDbSet(acntId)));
		} else {
			m_ui->accountTextInfo->setHtml(QString());
		}
		break;
	default:
		m_ui->accountInfoLabel->setVisible(false);
		m_ui->downloadAccountInfoButton->setVisible(false);
		m_ui->accountTextInfo->setText(QString());
		m_ui->accountTextInfo->setReadOnly(true);
		return;
		break;
	}

	m_ui->accountTextInfo->setReadOnly(true);
}

void DlgCreateAccount::setPwdLineEchoMode(int echoMode)
{
	enum QLineEdit::EchoMode mode = QLineEdit::Password;
	QString buttonLabel(tr("View"));

	switch (echoMode) {
	case QLineEdit::Normal:
		mode = QLineEdit::Normal;
		buttonLabel = tr("Hide");
		break;
	case QLineEdit::Password:
		break;
	default:
		Q_ASSERT(0);
		break;
	}

	if (mode == QLineEdit::Password) {
		m_hidePwdTimer.stop();
		m_ui->viewPwdProgress->setVisible(false);
	}
	m_ui->pwdLine->setEchoMode(mode);
	m_ui->showHidePwdButton->setText(buttonLabel);
	if (mode == QLineEdit::Normal) {
		m_ui->viewPwdProgress->setVisible(true);
		m_ui->viewPwdProgress->setMinimum(0);
		m_viewPwdRemainingCycles = m_viewPwdDuration / m_viewPwdUpdate;
		m_ui->viewPwdProgress->setMaximum(m_viewPwdRemainingCycles);
		m_ui->viewPwdProgress->setValue(m_viewPwdRemainingCycles);
		m_hidePwdTimer.start(m_viewPwdUpdate);
	}
}

AcntData DlgCreateAccount::getContent(void) const
{
	AcntData newAcntSettings;

	switch (m_action) {
	case ACT_ADDNEW:
		/*
		 * Set to true only for newly created account.
		 * Don't set it to false when account is edited because a
		 * newly created account can also be edited in case the first
		 * account creation attempt failed.
		 */
		newAcntSettings._setCreatedFromScratch(true);
		break;
	case ACT_EDIT:
	case ACT_PWD:
	case ACT_CERT:
	case ACT_CERTPWD:
	case ACT_MEP:
		Q_ASSERT(!m_accountInfo.userName().isEmpty());
		newAcntSettings = m_accountInfo;
		break;
	default:
		Q_ASSERT(0);
		break;
	}

	/* Set account entries. */
	newAcntSettings.setAccountName(m_ui->acntNameLine->text().trimmed());
	newAcntSettings.setUserName(m_ui->usernameLine->text().trimmed());
	newAcntSettings.setTestAccount(m_ui->testAcntCheckBox->isChecked());
	newAcntSettings.setPassword(m_ui->pwdLine->text());
	newAcntSettings.setRememberPwd(m_ui->rememberPwdCheckBox->isChecked());
	newAcntSettings.setSyncWithAll(m_ui->syncAllCheckBox->isChecked());

	newAcntSettings.setLoginMethod(
	    indexToLoginMethod(m_ui->loginMethodComboBox->currentIndex()));
	switch (m_ui->loginMethodComboBox->currentIndex()) {
	case CERTIFICATE:
		newAcntSettings.setPassword(QString());
		newAcntSettings.setP12File(
		    QDir::fromNativeSeparators(m_certPath));
		break;
	case USER_CERTIFICATE:
		newAcntSettings.setP12File(
		    QDir::fromNativeSeparators(m_certPath));
		break;
	case USER_NAME:
	case HOTP:
	case TOTP:
		newAcntSettings.setP12File(QString());
		break;
	case MEP:
		newAcntSettings.setMepToken(m_ui->mepLine->text());
		newAcntSettings.setP12File(QString());
		break;
	default:
		Q_ASSERT(0);
		break;
	}

	return newAcntSettings;
}
