/***************************************************************************
  qgslayertree.h
  --------------------------------------
  Date                 : May 2014
  Copyright            : (C) 2014 by Martin Dobias
  Email                : wonder dot sk at gmail dot com
 ***************************************************************************
 *                                                                         *
 *   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 2 of the License, or     *
 *   (at your option) any later version.                                   *
 *                                                                         *
 ***************************************************************************/

#ifndef QGSLAYERTREE_H
#define QGSLAYERTREE_H

#include "qgslayertreecustomnode.h"
#include "qgslayertreegroup.h"
#include "qgslayertreelayer.h"
#include "qgslayertreenode.h"

#include <QString>

using namespace Qt::StringLiterals;

/**
 * \ingroup core
 * \brief Namespace with helper functions for layer tree operations.
 *
 * Only generally useful routines should be here. Miscellaneous utility functions for work
 * with the layer tree are in QgsLayerTreeUtils class.
 *
 */
class CORE_EXPORT QgsLayerTree : public QgsLayerTreeGroup
{
    Q_OBJECT

  public:
    /**
     * Check whether the node is a valid group node
     *
     */
    static inline bool isGroup( QgsLayerTreeNode *node ) { return node && node->nodeType() == QgsLayerTreeNode::NodeGroup; }

    /**
     * Check whether the node is a valid layer node
     *
     */
    static inline bool isLayer( const QgsLayerTreeNode *node ) { return node && node->nodeType() == QgsLayerTreeNode::NodeLayer; }

    /**
     * Check whether the node is a valid custom node
     *
     * \since QGIS 4.0
     */
    static inline bool isCustomNode( const QgsLayerTreeNode *node ) { return node && node->nodeType() == QgsLayerTreeNode::NodeCustom; }

    /**
     * Cast node to a group.
     *
     * \note Not available in Python bindings, because cast is automatic.
     */
    static inline QgsLayerTreeGroup *toGroup( QgsLayerTreeNode *node ) SIP_SKIP { return qobject_cast<QgsLayerTreeGroup *>( node ); }

    /**
     * Cast node to a layer.
     *
     * \note Not available in Python bindings, because cast is automatic.
     */
    static inline QgsLayerTreeLayer *toLayer( QgsLayerTreeNode *node ) SIP_SKIP { return qobject_cast<QgsLayerTreeLayer *>( node ); }

    /**
     * Cast node to a layer.
     *
     * \note Not available in Python bindings, because cast is automatic.
     */
    static inline const QgsLayerTreeLayer *toLayer( const QgsLayerTreeNode *node ) SIP_SKIP { return qobject_cast< const QgsLayerTreeLayer *>( node ); }

    /**
     * Cast node to a custom node.
     *
     * \note Not available in Python bindings, because cast is automatic.
     * \since QGIS 4.0
     */
    static inline QgsLayerTreeCustomNode *toCustomNode( QgsLayerTreeNode *node ) SIP_SKIP { return qobject_cast<QgsLayerTreeCustomNode *>( node ); }

    /**
     * Create a new empty layer tree
     */
    QgsLayerTree();

#ifdef SIP_RUN
    // clang-format off
    SIP_PYOBJECT __repr__();
    % MethodCode
    // override parent QgsLayerTreeGroup __repr__ and resort back to default repr for QgsLayerTree -- there's no extra useful context we can show
    QString str = u"<qgis._core.QgsLayerTree object at 0x%1>"_s.arg( reinterpret_cast<quintptr>( sipCpp ), 2 * QT_POINTER_SIZE, 16, '0'_L1 );
    sipRes = PyUnicode_FromString( str.toUtf8().constData() );
    % End
// clang-format on
#endif

        /**
     * The order in which layers will be rendered on the canvas.
     * Will only be used if the property hasCustomLayerOrder is TRUE.
     * If you need the current layer order that is active, prefer using layerOrder().
     *
     * \see setCustomLayerOrder
     * \see layerOrder
     * \see hasCustomLayerOrder
     *
     */
        QList<QgsMapLayer *> customLayerOrder() const;

    /**
     * The order in which layers will be rendered on the canvas.
     * Will only be used if the property hasCustomLayerOrder is TRUE.
     * If you need the current layer order that is active, prefer using layerOrder().
     *
     * \see customLayerOrder
     * \see layerOrder
     * \see hasCustomLayerOrder
     *
     */
    void setCustomLayerOrder( const QList<QgsMapLayer *> &customLayerOrder );

    /**
     * The order in which layers will be rendered on the canvas.
     * Will only be used if the property hasCustomLayerOrder is TRUE.
     * If you need the current layer order that is active, prefer using layerOrder().
     *
     * \see customLayerOrder
     * \see layerOrder
     * \see hasCustomLayerOrder
     *
     */
    void setCustomLayerOrder( const QStringList &customLayerOrder ) SIP_PYNAME( setCustomLayerOrderByIds );

    /**
     * The order in which layers will be rendered on the canvas.
     * Depending on hasCustomLayerOrder, this will return either the override
     * customLayerOrder or the layer order derived from the tree.
     * This property is read only.
     *
     * \see customLayerOrder
     * \see layerAndCustomNodeOrder()
     */
    QList<QgsMapLayer *> layerOrder() const;

    /**
     * Determines if the layer order should be derived from the layer tree
     * or if a custom override order shall be used instead.
     *
     * \see customLayerOrder
     *
     */
    bool hasCustomLayerOrder() const;

    /**
     * Determines if the layer order should be derived from the layer tree
     * or if a custom override order shall be used instead.
     *
     * \see setCustomLayerOrder
     *
     */
    void setHasCustomLayerOrder( bool hasCustomLayerOrder );

    /**
     * The order in which layers and custom nodes will be rendered on the canvas.
     *
     * Since custom nodes don't support custom layer order, this
     * returns the node order derived from the tree.
     *
     * Depending on the use case, not all custom nodes are to be rendered on the
     * canvas, so callers of this method will probably need another layer of
     * logic to use the returned order in a meaningful way.
     *
     * This property is read only.
     *
     * \see layerOrder()
     * \since QGIS 4.0
     */
    QList<QgsLayerTreeNode *> layerAndCustomNodeOrder() const;

    /**
     * Load the layer tree from an XML element.
     * It is not required that layers are loaded at this point.
     * resolveReferences() needs to be called after loading the layers and
     * before using the tree.
     *
     */
    static std::unique_ptr< QgsLayerTree > readXml( const QDomElement &element, const QgsReadWriteContext &context ); // cppcheck-suppress duplInheritedMember

    /**
     * Load the layer order from an XML element.
     * Make sure that this is only called after the layers are loaded.
     *
     */
    void readLayerOrderFromXml( const QDomElement &doc );

    void writeXml( QDomElement &parentElement, const QgsReadWriteContext &context ) override;

    QgsLayerTree *clone() const override SIP_FACTORY;

    /**
     * Clear any information from this layer tree.
     *
     */
    void clear();

  signals:

    /**
     * Emitted when the custom layer order has changed.
     *
     */
    void customLayerOrderChanged();

    /**
     * Emitted when the layer order has changed.
     *
     */
    void layerOrderChanged();

    /**
     * Emitted when the hasCustomLayerOrder flag changes.
     *
     * \see hasCustomLayerOrder
     *
     */
    void hasCustomLayerOrderChanged( bool hasCustomLayerOrder );

  private slots:
    void nodeAddedChildren( QgsLayerTreeNode *node, int indexFrom, int indexTo );
    void nodeRemovedChildren();

  private:
    QgsLayerTree( const QgsLayerTree &other );

    void init();

    void addMissingLayers();
    QgsWeakMapLayerPointerList mCustomLayerOrder;
    bool mHasCustomLayerOrder = false;

    QgsLayerTree &operator=( const QgsLayerTree & ) = delete;
};

#endif // QGSLAYERTREE_H
