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

#pragma once

#include <QAbstractItemModel>
#include <QVariant>

#include "src/datovka_shared/identifiers/account_id.h"
#include "src/models/account_unique_numid.h"

class AccountsMap; /* Forward declaration. */

/*!
 * @brief Account listing.
 */
class AccountSettingsModel : public QAbstractItemModel {
	Q_OBJECT

public:
	/*
	 * nodeRoot (invisible)
	 * |
	 * +- nodeRegular (regular account listing)
	 * |  |
	 * |  +- nodeRegularAccountTop (account R1)
	 * |  |  |
	 * |  |  +- nodeRegularAccountInfo
	 * |  |  |
	 * |  |  +- nodeRegularAccountUsers
	 * |  |
	 * |  +- nodeRegularAccountTop (account R2)
	 * |  .  |
	 * |  .  +- nodeRegularAccountInfo
	 * |  .  |
	 * |  .  +- nodeRegularAccountUsers
	 * |  .
	 * |
	 * +- nodeShadow (shadow account listing)
	 *    |
	 *    +- nodeShadowAccountTop (account S1)
	 *    |  |
	 *    |  +- nodeShadowAccountInfo
	 *    |
	 *    +- nodeShadowAccountTop (account S2)
	 *    .  |
	 *    .  +- nodeShadowAccountInfo
	 *    .
	 */
	enum NodeType {
		nodeUnknown = 0, /* Must start at 0. */
		nodeRoot,
		nodeRegular,
		nodeShadow,
		nodeRegularAccountTop,
		nodeShadowAccountTop,
		nodeRegularAccountInfo,
		nodeRegularAccountUsers,
		nodeShadowAccountInfo
	};

	/*!
	 * @brief number os fome ot the rows in the model.
	 */
	enum RowNumbers {
		ROW_REGULAR = 0, /*!< Index of nodeRegular. */
		ROW_SHADOW = 1, /*!< Index of nodeShadow. */
		ROW_ACNT_INFO = 0, /*!< Index of node*AccountInfo. */
		ROW_ACNT_USERS = 1 /*!< Index of node*AccountUsers. */
	};

	/*!
	 * @brief Constructor.
	 *
	 * @param[in] parent Pointer to parent object.
	 */
	explicit AccountSettingsModel(QObject *parent = Q_NULLPTR);

	/*!
	 * @brief Return index specified by supplied parameters.
	 *
	 * @param[in] row    Item row.
	 * @param[in] column Parent column.
	 * @param[in] parent Parent index.
	 * @return Index to desired element or invalid index on error.
	 */
	virtual
	QModelIndex index(int row, int column,
	    const QModelIndex &parent = QModelIndex()) const Q_DECL_OVERRIDE;

	/*!
	 * @brief Return parent index of the item with the given index.
	 *
	 * @param[in] index Child node index.
	 * @return Index of the parent node or invalid index on error.
	 */
	virtual
	QModelIndex parent(const QModelIndex &index) const Q_DECL_OVERRIDE;

	/*!
	 * @brief Return number of rows under the given parent.
	 *
	 * @param[in] parent Parent node index.
	 * @return Number of rows.
	 */
	virtual
	int rowCount(const QModelIndex &parent = QModelIndex()) const
	    Q_DECL_OVERRIDE;

	/*!
	 * @brief Return the number of columns for the children of given parent.
	 *
	 * @param[in] parent Parent node index.
	 * @return Number of columns.
	 */
	virtual
	int columnCount(const QModelIndex &parent = QModelIndex()) const
	    Q_DECL_OVERRIDE;

	/*!
	 * @brief Return data stored in given location under given role.
	 *
	 * @param[in] index Index specifying the item.
	 * @param[in] role  Data role.
	 * @return Data from model.
	 */
	virtual
	QVariant data(const QModelIndex &index, int role) const Q_DECL_OVERRIDE;

	/*!
	 * @brief Returns header data in given location under given role.
	 *
	 * @param[in] section     Header position.
	 * @param[in] orientation Header orientation.
	 * @param[in] role        Data role.
	 * @return Header data from model.
	 */
	virtual
	QVariant headerData(int section, Qt::Orientation orientation,
	    int role = Qt::DisplayRole) const Q_DECL_OVERRIDE;

	/*!
	 * @brief Load content.
	 *
	 * @param[in] regularAccounts Regular accounts.
	 * @param[in] shadowAccounts Shadow accounts.
	 */
	void loadContent(AccountsMap *regularAccounts,
	    AccountsMap *shadowAccounts);

	/*!
	 * @brief Returns account identifier for given node.
	 *
	 * @param[in] index Data index.
	 * @return Account identifier which the node belongs to or a null
	 *     identifier on error.
	 */
	const AcntId &acntId(const QModelIndex &index) const;

	/*!
	 * @brief Get shadow account index.
	 *
	 * @param[in] acntId Account identifier.
	 * @return Valid index if account found, invalid on any error.
	 */
	QModelIndex indexShadowAccountTop(const AcntId &acntId) const;

	/*!
	 * @brief Returns node type.
	 *
	 * @param[in] index Data index.
	 * @return Node type related to the supplied index.
	 */
	static
	enum NodeType nodeType(const QModelIndex &index);

private slots:
	/*!
	 * @brief Notify before an account is going to be added.
	 */
	void regularAccountAboutToBeAdded(const AcntId &acntId, int row);

	/*!
	 * @brief Notify before an account is going to be added.
	 */
	void shadowAccountAboutToBeAdded(const AcntId &acntId, int row);

	/*!
	 * @brief Update the model after an account has been added.
	 */
	void regularAccountAdded(const AcntId &acntId, int row);

	/*!
	 * @brief Update the model after an account has been added.
	 */
	void shadowAccountAdded(const AcntId &acntId, int row);

	/*!
	 * @brief Notify before an account is going to be removed.
	 */
	void regularAccountAboutToBeRemoved(const AcntId &acntId, int row);

	/*!
	 * @brief Notify before an account is going to be removed.
	 */
	void shadowAccountAboutToBeRemoved(const AcntId &acntId, int row);

	/*!
	 * @brief Update the model after an account has been removed.
	 */
	void regularAccountRemoved(const AcntId &acntId, int row);

	/*!
	 * @brief Update the model after an account has been removed.
	 */
	void shadowAccountRemoved(const AcntId &acntId, int row);

	/*!
	 * @brief Notify before accounts are going to be moved.
	 */
	void regularAccountsAboutToBeMoved(int srcFrst, int srcLst, int dst);

	/*!
	 * @brief Notify before accounts are going to be moved.
	 */
	void shadowAccountsAboutToBeMoved(int srcFrst, int srcLst, int dst);

	/*!
	 * @brief Update the model after accounts have been moved.
	 */
	void regularAccountsMoved(int srcFrst, int srcLst, int dst);

	/*!
	 * @brief Update the model after accounts have been moved.
	 */
	void shadowAccountsMoved(int srcFrst, int srcLst, int dst);

	/*!
	 * @brief This slot handles changes of username.
	 */
	void regularUsernameChanged(const AcntId &oldId, const AcntId &newId,
	    int index);

	/*!
	 * @brief This slot handles changes of username.
	 */
	void shadowUsernameChanged(const AcntId &oldId, const AcntId &newId,
	    int index);

	/*!
	 * @brief Update the model after an account has been modified.
	 */
	void regularAcountChanged(const AcntId &acntId);

	/*!
	 * @brief Update the model after an account has been modified.
	 */
	void shadowAcountChanged(const AcntId &acntId);

	/*!
	 * @brief Handles start of the reset of underlying data.
	 */
	void contentAboutToBeReset(void);

	/*!
	 * @brief Handles end of the reset of underlying data.
	 */
	void contentReset(void);

private:
	/*!
	 * @brief Load list content.
	 *
	 * @param[in] parent Top list index.
	 * @param[in] accounts List of accounts.
	 * @param[in] shadow True for shadow accounts.
	 */
	void loadListContent(AccountsMap *accounts, bool shadow);

	/*!
	 * @brief Returns child node type for given row.
	 *
	 * @param[in] parentType Parent node type.
	 * @param[in] childRow Child row.
	 * @return Child node type; unknown type on error.
	 */
	static
	enum NodeType childNodeType(enum NodeType parentType, int childRow);

	/*!
	 * @brief Returns parent node type for given child type.
	 *
	 * @note The row is set when it can be clearly determined from the node
	 *     type. If it cannot be determined then it is set to -1.
	 *
	 * @param[in]  childType Child node type.
	 * @param[out] parentRow Pointer to row value that should be set.
	 * @return Parent node type; unknown type on error.
	 */
	static
	enum NodeType parentNodeType(enum NodeType childType, int *parentRow);

	AccountsMap *m_regularAccounts; /*!< Pointer to regular account container. */
	AcntNumId m_regNumIdMap; /*!< Provides int <-> AcntId mapping for regular account identifiers. */
	AccountsMap *m_shadowAccounts; /*!< Pointer to shadow account container. */
	AcntNumId m_shNumIdMap; /*!< Provides int <-> AcntId mapping for shadow account identifiers. */
};
