/*
 * Copyright (C) 2014-2024 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 <QAbstractListModel>
#include <QList>
#include <QStringList>
#include <QVariant>

/*!
 * @brief Custom actions model class.
 */
class ActionListModel : public QAbstractListModel {
	Q_OBJECT

public:
	/*!
	 * @brief Describes model entries.
	 */
	class Entry {
	public:
		enum Type {
			TYPE_UNKNOWN = 0, /*!< Invalid entry. */
			TYPE_ACTION,
			TYPE_WIDGET
		};

		Entry(void)
		    : type(TYPE_UNKNOWN), checked(false),
		    controlObject(Q_NULLPTR)
		{ }

		Entry(Type t, bool ch, QObject *co)
		    : type(t), checked(ch), controlObject(co)
		{ }

		bool isValid(void) const;

		enum Type type;
		bool checked;
		QObject *controlObject;
	};

	/*!
	 * @brief Column which this model holds.
	 */
	enum Columns {
		COL_ACTION_NAME = 0, /*!< Action name. */
		MAX_COLNUM /* Maximal number of columns (convenience value). */
	};
#if (QT_VERSION >= QT_VERSION_CHECK(5, 5, 0))
	Q_ENUM(Columns)
#else /* < Qt-5.5 */
	Q_ENUMS(Columns)
#endif /* >= Qt-5.5 */

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

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

	/*!
	 * @brief Returns the data stored under the given role.
	 *
	 * @param[in] index Position.
	 * @param[in] role  Role if the position.
	 * @return Data or invalid QVariant if no matching data found.
	 */
	virtual
	QVariant data(const QModelIndex &index,
	    int role = Qt::DisplayRole) const Q_DECL_OVERRIDE;

	/*!
	 * @brief Returns the drop actions supported by this model.
	 *
	 * @return Supported drop actions.
	 */
	virtual
	Qt::DropActions supportedDropActions(void) const Q_DECL_OVERRIDE;

	/*!
	 * @brief Used for checkable elements.
	 *
	 * @param[in] index Index which to obtain flags for.
	 */
	virtual
	Qt::ItemFlags flags(const QModelIndex &index) const Q_DECL_OVERRIDE;

	/*!
	 * @brief Returns the list of allowed MIME types.
	 *
	 * @return List of MIME types.
	 */
	virtual
	QStringList mimeTypes(void) const Q_DECL_OVERRIDE;

	/*!
	 * @brief Returns object containing serialised attachment data.
	 *
	 * @param[in] indexes List of indexes.
	 * @return Pointer to newly allocated MIME data object, Q_NULLPTR on error.
	 */
	virtual
	QMimeData *mimeData(
	    const QModelIndexList &indexes) const Q_DECL_OVERRIDE;

	/*!
	 * @brief Returns whether the model accepts drops of given MIME data.
	 *
	 * @param[in] data Data to be dropped.
	 * @param[in] action Type of drop action.
	 * @param[in] row Target row.
	 * @param[in] column Target column.
	 * @param[in] parent Parent index.
	 * @return True if drop is accepted.
	 */
	virtual
	bool canDropMimeData(const QMimeData *data, Qt::DropAction action,
	    int row, int column,
	    const QModelIndex &parent) const Q_DECL_OVERRIDE;

	/*!
	 * @brief Handles data supplied by drop operation.
	 *
	 * @param[in] data Data to be dropped.
	 * @param[in] action Type of drop action.
	 * @param[in] row Target row.
	 * @param[in] column Target column.
	 * @param[in] parent Parent index.
	 * @return True if data are handled by the model.
	 */
	virtual
	bool dropMimeData(const QMimeData *data, Qt::DropAction action,
	    int row, int column,
	    const QModelIndex &parent) Q_DECL_OVERRIDE;

	/*!
	 * @brief Used for changing the check state.
	 *
	 * @note Emits dataChanged signal.
	 *
	 * @param[in] index Index specifying the element.
	 * @param[in] value Value to be set.
	 * @param[in] role Specifies role of the modified data.
	 * @return True if check state was changed.
	 */
	virtual
	bool setData(const QModelIndex &index, const QVariant &value,
	    int role = Qt::EditRole) Q_DECL_OVERRIDE;

	/*!
	 * @brief Obtains header data.
	 *
	 * @param[in] section Position.
	 * @param[in] orientation Orientation of the header.
	 * @param[in] role Role of the data.
	 * @return Data or invalid QVariant in no matching data found.
	 */
	virtual
	QVariant headerData(int section, Qt::Orientation orientation,
	    int role = Qt::DisplayRole) const Q_DECL_OVERRIDE;

	/*!
	 * @brief Move rows.
	 *
	 * @param[in] sourceParent Source parent.
	 * @param[in] sourceRow Source row.
	 * @param[in] count Number of rows to be moved.
	 * @param[in] destinationParent Destination parent.
	 * @param[in] destinationChild Row to move data into.
	 * @return If move performed.
	 */
	virtual
	bool moveRows(const QModelIndex &sourceParent, int sourceRow,
	    int count, const QModelIndex &destinationParent,
	    int destinationChild) Q_DECL_OVERRIDE;

	/*!
	 * @brief Set whether entries should be checkable.
	 *
	 * @param[in] checking Whether to enable entry checking.
	 */
	void setChecking(bool checking);

	/*!
	 * @brief Insert action entry.
	 *
	 * @param[in] entry Entry to be added.
	 */
	void insertAction(const Entry &entry);

	/*!
	 * @brief Insert action entries.
	 *
	 * @param[in] entryList Entries to be added.
	 */
	void insertActions(const QList<Entry> &entryList);

	/*!
	 * @brief Insert action entry.
	 *
	 * @param[in] pos Row at which to place the start of the list.
	 * @param[in] entry Entry to be inserted.
	 */
	void insertAction(int pos, const Entry &entry);

	/*!
	 * @brief Insert action entries.
	 *
	 * @param[in] pos Row at which to place the start of the list.
	 * @param[in] entryList Entries to be inserted.
	 */
	void insertActions(int pos, const QList<Entry> &entryList);

	/*!
	 * @brief Remove action entries at given rows.
	 *
	 * @param[in] rows List of rows to be removed.
	 */
	void deleteActions(const QList<int> &rows);

	/*!
	 * @brief Model content at row.
	 *
	 * @param[in] row Selected row.
	 * @return Held data at given row.
	 */
	Entry entryAt(int row) const;

	/*!
	 * @brief Model content.
	 *
	 * @return Held data.
	 */
	const QList<Entry> &entries(void) const;

	/*!
	 * @brief Clears the model content.
	 */
	void removeAllRows(void);

	/*!
	 * @brief Set new separator pointer to action at given position.
	 *
	 * @param[in] pos Row at which to set the new pointer.
	 * @param[in] controlObject Pointer to the separator.
	 */
	void setSeparatorPointer(int pos, QObject *controlObject);

private:
	bool m_checking; /*!< Whether to enable checking. */

	QList<Entry> m_actionList; /*!< List of model entries. */
};
