/***************************************************************************
  qgslegendrenderer.h
  --------------------------------------
  Date                 : July 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 QGSLEGENDRENDERER_H
#define QGSLEGENDRENDERER_H

#include "qgis_core.h"
#include <QPointF>

class QRectF;
class QStandardItem;
class QJsonObject;

class QgsLayerTreeGroup;
class QgsLayerTreeLayer;
class QgsLayerTreeModel;
class QgsLayerTreeModelLegendNode;
class QgsLayerTreeNode;
class QgsSymbol;
class QgsRenderContext;

#include "qgslegendsettings.h"

/**
 * \ingroup core
 * \brief The QgsLegendRenderer class handles automatic layout and rendering of legend.
 * The content is given by QgsLayerTreeModel instance. Various layout properties can be configured
 * within QgsLegendRenderer.
 *
 * All spacing and sizes are in millimeters.
 *
 * \since QGIS 2.6
 */
class CORE_EXPORT QgsLegendRenderer
{
  public:

    /**
     * Constructor for QgsLegendRenderer. The ownership of the legend model is not changed,
     * and the model must exist for the lifetime of this renderer.
     */
    QgsLegendRenderer( QgsLayerTreeModel *legendModel, const QgsLegendSettings &settings );

    /**
     * Runs the layout algorithm and returns the minimum size required for the legend.
     * \see setLegendSize()
     * \see legendSize()
     */
    QSizeF minimumSize( QgsRenderContext *renderContext = nullptr );

    /**
     * Sets the preferred resulting legend size.
     *
     * If the size is null, the legend will be drawn with the minimum possible size to fit its content.
     *
     * \see legendSize()
     * \see minimumSize()
     */
    void setLegendSize( QSizeF s ) { mLegendSize = s; }

    /**
     * Returns the preferred legend size set by the client.
     *
     * If the returned size is null, the legend will be drawn with the minimum possible size to fit its content.
     *
     * \see minimumSize()
     * \see setLegendSize()
     */
    QSizeF legendSize() const { return mLegendSize; }

    /**
     * Draws the legend with given \a painter. The legend will occupy the area reported in legendSize().
     * The \a painter should be scaled beforehand so that units correspond to millimeters.
     */
    void drawLegend( QPainter *painter );

    /**
     * Draws the legend using a given render \a context. The legend will occupy the area reported in legendSize().
     *
     * \since QGIS 3.6
     */
    void drawLegend( QgsRenderContext &context );

    /**
     * Renders the legend in a \a json object.
     *
     * \since QGIS 3.8
     */
    void exportLegendToJson( const QgsRenderContext &context, QJsonObject &json );

    /**
     * Sets the \a style of a \a node.
     *
     * \see nodeLegendStyle()
     */
    static void setNodeLegendStyle( QgsLayerTreeNode *node, QgsLegendStyle::Style style );

    /**
     * Returns the style for the given \a node, within the specified \a model.
     *
     * \see setNodeLegendStyle()
     */
    static QgsLegendStyle::Style nodeLegendStyle( QgsLayerTreeNode *node, QgsLayerTreeModel *model );

  private:

#ifndef SIP_RUN

    /**
     * A legend component is either a group title, a layer title or a layer child item.
     *
     * E.g. a layer title component is just the layer's title, it does not
     * include all of that layer's subitems. Similarly, a group's title component is just
     * the group title, and does not include the actual content of that group.
     */
    class LegendComponent
    {
      public:

        LegendComponent() = default;

        QObject *item = nullptr;

        //! Symbol size, not including any preset padding space around the symbol
        QSizeF symbolSize;

        //! Label size, not including any preset padding space around the label
        QSizeF labelSize;

        //! Component size
        QSizeF size;

        /**
         * Horizontal offset for the symbol label.
         *
         * This offset is the same for all symbol labels belonging to the same layer,
         * within the same legend column.
         */
        double labelXOffset = 0.0;

        /**
         * Largest symbol width, considering all other sibling components associated with
         * this component.
         */
        double maxSiblingSymbolWidth = 0.0;
    };

    /**
     * An component group is an indivisible set of legend components (i.e. it is indivisible into more columns).
     *
     * A group may consist of one or more component(s), depending on the layer splitting mode:
     *
     *  1) no layer split: [group_title ...] layer_title layer_item [layer_item ...]
     *  2) layer split:    [group_title ...] layer_title layer_item
     *              or:    layer_item
     *
     * This means that group titles must not be split from layer titles and layer titles
     * must not be split from the first layer item, because this results in a poor layout
     * and it would not be logical to leave a group or layer title at the bottom of a column,
     * separated from the actual content of that group or layer.
     */
    class LegendComponentGroup
    {
      public:

        //! List of child components belonging to this group.
        QList<LegendComponent> components;

        //! Group size, including internal spacing between components, but excluding any padding space around the group itself.
        QSizeF size = QSizeF( 0, 0 );

        //! Corresponding column index
        int column = 0;
    };

    /**
     * Contains contextual information about the current column being rendered
     */
    class ColumnContext
    {
      public:

        ColumnContext()
          : left( 0 )
          , right( 0 )
        {}

        //! Left edge of column
        double left = 0;
        //! Right edge of column
        double right = 0;
    };

    /**
     * Draws the legend and returns the actual size of the legend.
     *
     * If \a painter is NULLPTR, only the size of the legend will be calculated and no
     * painting will be attempted.
     */
    QSizeF paintAndDetermineSize( QPainter *painter = nullptr );

    /**
     * Returns a list of component groups for the specified \a parentGroup, respecting the current layer's
     * splitting settings.
     */
    QList<LegendComponentGroup> createComponentGroupList( QgsLayerTreeGroup *parentGroup, bool splitLayer, QgsRenderContext *context );

    /**
     * Divides a list of component groups into columns, and sets the column index for each group in the list.
     */
    void setColumns( QList<LegendComponentGroup> &groupList );

    /**
     * Draws a title in the legend using the title font and the specified alignment settings.
     *
     * Returns the size required to draw the complete title.
     *
     * If \a painter is NULLPTR, no painting will be attempted, but the required size will still be calculated and returned.
     */
    QSizeF drawTitle( QPainter *painter = nullptr, double top = 0, Qt::AlignmentFlag halignment = Qt::AlignLeft, double legendWidth = 0 );

    /**
     * Returns the calculated padding space required above the given component \a group.
     */
    double spaceAboveGroup( const LegendComponentGroup &group );

    /**
     * Draws a component \a group and return its actual size.
     *
     * The \a group is drawn with the space above it, so that the first groups in a column are all
     * aligned to the same line regardless of their style top spacing.
    */
    QSizeF drawGroup( const LegendComponentGroup &group, ColumnContext columnContext, QPainter *painter = nullptr, double top = 0 );

    /**
     * Draws the symbol of a given symbol QgsLayerTreeModelLegendNode.
     */
    LegendComponent drawSymbolItem( QgsLayerTreeModelLegendNode *symbolItem, ColumnContext columnContext = ColumnContext(), QPainter *painter = nullptr, double top = 0, double maxSiblingSymbolWidth = 0 );

    /**
     * Draws the title of a layer, given a QgsLayerTreeLayer, and a destination \a painter.
     *
     * Returns the size of the title.
     *
     * The \a painter may be NULLPTR, in which case on the size is calculated and no painting is attempted.
     */
    QSizeF drawLayerTitle( QgsLayerTreeLayer *nodeLayer, ColumnContext columnContext = ColumnContext(), QPainter *painter = nullptr, double top = 0 );

    /**
     * Draws a group item.
     * Returns the size of the title.
     */
    QSizeF drawGroupTitle( QgsLayerTreeGroup *nodeGroup, ColumnContext columnContext = ColumnContext(), QPainter *painter = nullptr, double top = 0 );

    /**
     * Renders a group item in a \a json object.
     *
     * \since QGIS 3.8
     */
    void exportLegendToJson( const QgsRenderContext &context, QgsLayerTreeGroup *nodeGroup, QJsonObject &json );

    /**
     * Draws the legend using the specified render \a context, and returns the actual size of the legend.
     *
     * If \a context is NULLPTR, only the size of the legend will be calculated and no
     * painting will be attempted.
     */
    QSizeF paintAndDetermineSize( QgsRenderContext *context );

    /**
     * Draws a title in the legend using the specified render \a context, with the title font and the specified alignment settings.
     *
     * Returns the size required to draw the complete title.
     *
     * If \a context is NULLPTR, no painting will be attempted, but the required size will still be calculated and returned.
     */
    QSizeF drawTitle( QgsRenderContext *context, double top, Qt::AlignmentFlag halignment = Qt::AlignLeft, double legendWidth = 0 );

    /**
     * Draws an \a group and return its actual size, using the specified render \a context.
     *
     * The \a group is drawn with the space above it, so that the first groups in a column are all
     * aligned to the same line regardless of their style top spacing.
     *
     * If \a context is NULLPTR, no painting will be attempted, but the required size will still be calculated and returned.
    */
    QSizeF drawGroup( const LegendComponentGroup &group, QgsRenderContext *rendercontext, ColumnContext columnContext, double top = 0 );

    /**
     * Draws the symbol of a given symbol QgsLayerTreeModelLegendNode, using the specified render \a context.
     */
    LegendComponent drawSymbolItem( QgsLayerTreeModelLegendNode *symbolItem, QgsRenderContext *context, ColumnContext columnContext, double top, double maxSiblingSymbolWidth = 0 );

    /**
     * Draws the title of a layer, given a QgsLayerTreeLayer, and a destination render \a context.
     *
     * Returns the size of the title.
     *
     * The \a context may be NULLPTR, in which case on the size is calculated and no painting is attempted.
     */
    QSizeF drawLayerTitle( QgsLayerTreeLayer *nodeLayer, QgsRenderContext *context, ColumnContext columnContext, double top = 0 );

    /**
     * Draws a group's title, using the specified render \a context.
     *
     * Returns the size of the title.
     */
    QSizeF drawGroupTitle( QgsLayerTreeGroup *nodeGroup, QgsRenderContext *context, ColumnContext columnContext = ColumnContext(), double top = 0 );

    /**
     * Returns the style of the given \a node.
     */
    QgsLegendStyle::Style nodeLegendStyle( QgsLayerTreeNode *node );

    QgsLayerTreeModel *mLegendModel = nullptr;

    QgsLegendSettings mSettings;

    QSizeF mLegendSize;

#endif
    QSizeF drawTitleInternal( QgsRenderContext *context, QPainter *painter, double top, Qt::AlignmentFlag halignment, double legendWidth );
    QSizeF drawGroupInternal( const LegendComponentGroup &group, QgsRenderContext *context, ColumnContext columnContext, QPainter *painter, double top );
    QgsLegendRenderer::LegendComponent drawSymbolItemInternal( QgsLayerTreeModelLegendNode *symbolItem, ColumnContext columnContext, QgsRenderContext *context, QPainter *painter, double top, double maxSiblingSymbolWidth );
    QSizeF drawLayerTitleInternal( QgsLayerTreeLayer *nodeLayer, ColumnContext columnContext, QgsRenderContext *context, QPainter *painter, double top );
    QSizeF drawGroupTitleInternal( QgsLayerTreeGroup *nodeGroup, ColumnContext columnContext,  QgsRenderContext *context, QPainter *painter, double top );
    QSizeF paintAndDetermineSizeInternal( QgsRenderContext *context, QPainter *painter );

    void widthAndOffsetForTitleText( const Qt::AlignmentFlag halignment, double legendWidth, double &width, double &offset );
};

#endif // QGSLEGENDRENDERER_H