/*
 * <one line to give the program's name and a brief idea of what it does.>
 * Copyright (C) 2019  camilo <chiguitar@unal.edu.co>
 *
 * 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/>.
 */

#pragma once

#include <QObject>
 #include <QQmlEngine>
#include "fmh.h"

#include "mauikit_export.h"

#include <QQmlParserStatus>

/**
 * @brief MauiList class.
 * 
 * A helper class for easily setting up a list model to be feed into MauiModel::list, and to be used by the browsing views in Mauikit controls.
 * 
 * @warning This method of setting up a data model is very limited by the lingo supported by the FMH::MODEL_KEY dictionary.
 * So only consider using this class if your data model structure is based on only string text and can be represented by the FMH::MODEL_KEY dictionary entries. 
 * @see FMH::MODEL_KEY
 * 
 * This inherits from the QQmlParserStatus class, so t can be aware of its creation status in the QML engine. This is useful to lazy-loading parts when needed, as when the component presenting the data model is ready and loaded.
 * 
 * The list generated by sub-classing MauiList is meant to be used as the list for the MauiModel class, exposed to QML as the `BaseModel` type. The MauiModel supports features, such as filtering and sorting.
 * @see MauiModel
 * 
 * @image html Misc/listbrowser_data.png
 * 
 * @section example Example
 * This is a simple example of a MauiList based data model.
 * 
 * First wee need to setup the MauiList data. In the example below the data is manually added. The data must be modeled using the FMH::MODEL_LIST, which is an array list of FMH::MODEL, which is a map of key pairs, where the key must be a FMH::MODEL_KEY enum type, and the value a string text.
 * 
 * @code 
 * #pragma once
 * 
 * #include <QObject>
 * #include <MauiKit4/Core/mauilist.h>
 * 
 * class PlantsList : public MauiList
 * {
 *    Q_OBJECT
 * public:
 *    PlantsList(QObject *parent = nullptr);
 * 
 *    // QQmlParserStatus interface
 *    void componentComplete();
 * 
 *    // MauiList interface
 *    const FMH::MODEL_LIST &items() const;
 * 
 * private:
 *    FMH::MODEL_LIST m_list;
 * 
 *    //Here we have our own custom raw data and turn it into a FMH::MODEL_LIST
 *    FMH::MODEL_LIST getData() const;
 * };
 * @endcode
 * 
 * 
 * In this example the data is only retrieved once the QML engine has completely loaded the component using the MauiList, for this we override the `componentComplete()` virtual method, and there we inform the MauiModel with the signals that the list data is ready.
 * 
 * @code
 * #include "plantslist.h"
 * 
 * PlantsList::PlantsList(QObject *parent) : MauiList(parent)
 * {
 * 
 * }
 * 
 * void PlantsList::componentComplete()
 * {
 *    Q_EMIT preListChanged();
 *    m_list = getData();
 *    Q_EMIT postListChanged();
 *    Q_EMIT countChanged();
 * }
 * 
 * const FMH::MODEL_LIST &PlantsList::items() const
 * {
 *    return m_list;
 * }
 * 
 * FMH::MODEL_LIST PlantsList::getData() const
 * {
 *    FMH::MODEL_LIST data;
 * 
 *    data << FMH::MODEL {{FMH::MODEL_KEY::TITLE, QStringLiteral("Acanthaceae")}, {FMH::MODEL_KEY::CATEGORY, QStringLiteral("Acanthus")}};
 * 
 *    data << FMH::MODEL {{FMH::MODEL_KEY::TITLE, QStringLiteral("Agavaceae")}, {FMH::MODEL_KEY::CATEGORY, QStringLiteral("Agave")}};
 * 
 *    data << FMH::MODEL {{FMH::MODEL_KEY::TITLE, QStringLiteral("Bixaceae")}, {FMH::MODEL_KEY::CATEGORY, QStringLiteral("Annatto")}};
 * 
 *    data << FMH::MODEL {{FMH::MODEL_KEY::TITLE, QStringLiteral("Asteraceae")}, {FMH::MODEL_KEY::CATEGORY, QStringLiteral("Aster")}};
 * 
 *    data << FMH::MODEL {{FMH::MODEL_KEY::TITLE, QStringLiteral("Leguminaceae")}, {FMH::MODEL_KEY::CATEGORY, QStringLiteral("Bean")}};
 * 
 *    return data;
 * }
 * 
 * @endcode
 * 
 * Now we register our own custom PlanstList class to the QML engine as a type.
 * 
 * @code
 * #include <MauiKit3/Core/mauiapp.h>
 * #include "plantslist.h"
 * 
 * int main(int argc, char *argv[])
 * {
 *    ...
 *    
 *    qmlRegisterType<PlantsList>("org.maui.demo", 1, 0, "PlantsList"); //Here we register it.
 * 
 *    engine.load(url);
 * 
 *    return app.exec();
 * }
 * @endcode
 * 
 * And finally, we can consume the data list by hooking it up to the MauiModel exposed type `BaseModel`.
 * 
 * @code
 * import org.mauikit.controls as Maui
 * 
 * import org.maui.demo as Demo
 * 
 * Maui.ApplicationWindow
 * {
 *    id: root
 * 
 *    Maui.Page
 *    {
 *        id: _page
 *        anchors.fill: parent
 *        Maui.Controls.showCSD: true
 *        headBar.forceCenterMiddleContent: true
 * 
 *        Maui.ListBrowser
 *        {
 *            anchors.fill: parent
 *            model: Maui.BaseModel
 *            {
 *                list: Demo.PlantsList
 *                {
 * 
 *                }
 *            }
 * 
 *            delegate: Maui.ListBrowserDelegate
 *            {
 *                width: ListView.view.width
 *                label1.text: model.title
 *                label2.text: model.category
 *            }
 *        }
 *    }
 * }
 * @endcode
 * 
 * <a href="https://invent.kde.org/maui/mauikit/-/blob/qt6-2/examples/mauilist/">You can find a more complete example at this link.</a>
 */
class MAUIKIT_EXPORT MauiList : public QObject, public QQmlParserStatus
{
    
    Q_OBJECT
    QML_ANONYMOUS
    Q_INTERFACES(QQmlParserStatus)

    Q_DISABLE_COPY(MauiList)
    
    /**
     * The total amount of elements in the list.
     * @note This needs to be setup manually, as in emitting the signal when new items are appended or removed, etc.
     */
    Q_PROPERTY(int count READ getCount NOTIFY countChanged FINAL)
    
public:
    /**
     * @brief Default constructor. The usage of this class is meant to be via inheritance by sub-classing it.
     */
    explicit MauiList(QObject *parent = nullptr);
    
    /**
     * @brief The modeled data represented by a FMH::MODEL_LIST.
     * @note  The data must be modeled using the FMH::MODEL_LIST, which is an array list of FMH::MODEL elements, which is a map of key pairs, where the key must be a FMH::MODEL_KEY enum type, and the value a string text.
     * 
     * @return The data model.
     */
    virtual const FMH::MODEL_LIST &items() const = 0;
    
    /**
     * @brief See the Qt documentation on the QQmlParserStatus.
     */
    virtual void classBegin() override {}
    
    /**
     * @brief See the Qt documentation on the QQmlParserStatus.
     */
    virtual void componentComplete() override  {}
    
    /**
     * @brief This function is called once the MauiList has been hooked to the MauiModel, using the MauiModel::list property.
     */
    virtual void modelHooked() {}
    
    int getCount() const;
    
    /**
     * @brief Request to get an item in the list, the item is represented as a FMH::MODEL key pair value.
     * @param index the position of the item to retrieve
     * @return The found item/map in the list at the index. If not item is found at the given index, then an empty map is returned.
     */
    FMH::MODEL getItem(const int &index) const;
    
public Q_SLOTS:    
    /**
     * @brief Request to get an item in the list, the item is represented as a QVariantMap for easy consumption within the QML scope. This function is exposed to be invoked from QML.
     * @param index the position of the item to retrieve
     * @return The found item/map in the list at the index. If not item is found at the given index, then an empty map is returned.
     */
    QVariantMap get(const int &index) const;
    
protected:    
    /**
     * @brief Whether an item with a given key-par value exists in the list.
     * @param key the FMH::MODEL_KEY to look for
     * @param value the value associated with the key to look for
     * @return an exact match exists or not
     */
    bool exists(const FMH::MODEL_KEY &key, const QString &value) const;
    
    /**
     * @brief The index number of an item in the list of a given key-par value.
     * @param key the FMH::MODEL_KEY to look for
     * @param value the value associated with the key to look for
     * @return the found index or `-1` if not found
     */
    int indexOf(const FMH::MODEL_KEY &key, const QString &value) const;
    
Q_SIGNALS:    
    /**
     * @brief This signal should be emitted by the implementation before appending a new item to the list data model.
     */
    void preItemAppended();
    
     /**
     * @brief This signal should be emitted by the implementation before appending a multiple new items to the list data model.
     * @param count the total amount of new items that will be added
     */
    void preItemsAppended(uint count);
    
    /**
     * @brief This signal should be emitted by the implementation after one or multiple new items have finished being added into the list data model.
     */
    void postItemAppended();
    
    /**
     * @brief This signal should be emitted by the implementation before a new item has been inserted at a given index to the list data model.
     * @param index the position index where the new item was inserted at
     */
    void preItemAppendedAt(int index);
    
    /**
     * @brief This signal should be emitted by the implementation before an item has been removed at the given index position.
     * @param index the index position of the element that will be removed
     */
    void preItemRemoved(int index);
    
    /**
     * @brief This signal should be emitted by the implementation after an item has been successfully removed from the list data model. 
     */
    void postItemRemoved();
    
    /**
     * @brief This signal should be emitted by the implementation when changes have been done in the list data model.
     * @param index the index position of the item that was modified in the list data model
     * @param roles the keys that were modified, the key values can be FMH::MODEL_KEY
     */
    void updateModel(int index, QVector<int> roles);
    
    /**
     * @brief This signal should be emitted by the implementation before the list data model has been assigned or populated.
     */
    void preListChanged();
    
    /**
     * @brief This signal should be emitted by the implementation after the list data model is set and done.
     */
    void postListChanged();
    
    /**
     * @brief This signal should be emitted by the implementation when an item has been moved from one index position to another.
     * @param index the original index position of the item in the list data model
     * @param to the new index destination of the item
     */
    void itemMoved(int index, int to);
    
    /**
     * @brief This signal should be emitted by the implementation when the number of elements in the list data model varies. For example when an item is removed or added.
     */
    void countChanged();
};
