/////////////////////////////////////////////////////////////////////////////////
//
//  Copyright (C) 2015-     Statoil ASA
//  Copyright (C) 2015-     Ceetron Solutions AS
//
//  ResInsight 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.
//
//  ResInsight 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 at <http://www.gnu.org/licenses/gpl.html>
//  for more details.
//
/////////////////////////////////////////////////////////////////////////////////

#include "RiuMultiPlotPage.h"

#include "RiaApplication.h"
#include "RiaGuiApplication.h"
#include "RiaPlotDefines.h"
#include "RiaPlotWindowRedrawScheduler.h"
#include "RiaPreferences.h"

#include "RimContextCommandBuilder.h"
#include "RimMultiPlot.h"
#include "RimPlotCurve.h"
#include "RimPlotWindow.h"

#include "RiuDraggableOverlayFrame.h"
#include "RiuMainWindow.h"
#include "RiuPlotMainWindow.h"
#include "RiuPlotObjectPicker.h"
#include "RiuPlotWidget.h"
#include "RiuQwtLegendOverlayContentFrame.h"
#include "RiuQwtPlotLegend.h"
#include "RiuQwtPlotTools.h"
#include "RiuQwtPlotWidget.h"
#ifdef USE_QTCHARTS
#include "RiuQtChartsPlotWidget.h"
#endif

#include "cafCmdFeatureMenuBuilder.h"
#include "cafSelectionManager.h"

#include "cvfAssert.h"

#include "qwt_axis.h"
#include "qwt_legend.h"
#include "qwt_legend_label.h"
#include "qwt_plot_layout.h"
#include "qwt_plot_renderer.h"
#include "qwt_scale_draw.h"
#include "qwt_scale_widget.h"

#include <QDebug>
#include <QDesktopWidget>
#include <QFocusEvent>
#include <QFontMetrics>
#include <QGraphicsDropShadowEffect>
#include <QHBoxLayout>
#include <QMdiSubWindow>
#include <QMenu>
#include <QPainter>
#include <QScrollBar>
#include <QTimer>
#include <QWidget>

#include <cmath>

//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
RiuMultiPlotPage::RiuMultiPlotPage( RimPlotWindow* plotDefinition, QWidget* parent )
    : QWidget( parent )
    , m_plotDefinition( plotDefinition )
    , m_previewMode( false )
    , m_showSubTitles( false )
    , m_autoAlignAxes( true )
    , m_titleFontPixelSize( 12 )
    , m_subTitleFontPixelSize( 11 )
    , m_legendFontPixelSize( 8 )
    , m_axisTitleFontSize( 8 )
    , m_axisValueFontSize( 8 )

{
    CAF_ASSERT( m_plotDefinition );

    m_layout = new QVBoxLayout( this );
    m_layout->setMargin( 0 );
    m_layout->setSpacing( 4 );

    m_plotTitle = createTitleLabel();
    m_layout->addWidget( m_plotTitle );
    m_plotTitle->setVisible( true );

    m_plotLayout = new QHBoxLayout;
    m_layout->addLayout( m_plotLayout );

    m_plotWidgetFrame = new QFrame;
    m_plotLayout->addWidget( m_plotWidgetFrame, 1 );
    m_plotWidgetFrame->setVisible( true );

    m_gridLayout = new QGridLayout( m_plotWidgetFrame );
    m_gridLayout->setContentsMargins( 0, 0, 0, 0 );
    m_gridLayout->setSpacing( 0 );

    new RiuPlotObjectPicker( m_plotTitle, m_plotDefinition );

    setFocusPolicy( Qt::StrongFocus );
    setAcceptDrops( true );

    this->setObjectName( QString( "%1" ).arg( reinterpret_cast<uint64_t>( this ) ) );

    applyLook();

    updateMarginsFromPageLayout();
}

//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
RiuMultiPlotPage::~RiuMultiPlotPage()
{
}

//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
RimViewWindow* RiuMultiPlotPage::ownerViewWindow() const
{
    return m_plotDefinition;
}

//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
RimPlotWindow* RiuMultiPlotPage::ownerPlotDefinition()
{
    return m_plotDefinition;
}

//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
void RiuMultiPlotPage::addPlot( RiuPlotWidget* plotWidget )
{
    // Insert the plot to the left of the scroll bar
    insertPlot( plotWidget, m_plotWidgets.size() );
}

//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
void RiuMultiPlotPage::insertPlot( RiuPlotWidget* plotWidget, size_t index )
{
    m_plotWidgets.insert( static_cast<int>( index ), plotWidget );
    plotWidget->setVisible( false );

    QString subTitleText = plotWidget->plotTitle();
    QLabel* subTitle     = new QLabel( subTitleText );
    subTitle->setAlignment( Qt::AlignHCenter | Qt::AlignBottom );
    subTitle->setWordWrap( true );
    subTitle->setVisible( false );
    m_subTitles.insert( static_cast<int>( index ), subTitle );

    // Remove legend overlays already attached to the plot widget
    auto matcher = []( RiuDraggableOverlayFrame* p )
    { return dynamic_cast<RiuQwtLegendOverlayContentFrame*>( p->contentFrame() ) != nullptr; };
    plotWidget->clearOverlayFrames( matcher );

    RiuQwtPlotWidget* qwtPlotWidget = dynamic_cast<RiuQwtPlotWidget*>( plotWidget );

    RiuQwtPlotLegend*         legend      = nullptr;
    RiuDraggableOverlayFrame* legendFrame = new RiuDraggableOverlayFrame( plotWidget->getParentForOverlay(), plotWidget->overlayMargins() );

    if ( m_plotDefinition->legendsVisible() && plotWidget->plotDefinition()->legendsVisible() )
    {
        legend            = new RiuQwtPlotLegend( this );
        int legendColumns = 1;
        if ( m_plotDefinition->legendsHorizontal() )
        {
            legendColumns = 0; // unlimited
        }
        legend->setMaxColumns( legendColumns );
        legend->horizontalScrollBar()->setVisible( false );
        legend->verticalScrollBar()->setVisible( false );

        // The legend item mode must be set before the widget is created
        // See https://qwt.sourceforge.io/class_qwt_legend.html#af977ff3e749f8281ee8ad4b926542b50
        auto legendItemMode = m_plotDefinition->legendItemsClickable() ? QwtLegendData::Clickable : QwtLegendData::ReadOnly;
        legend->setDefaultItemMode( legendItemMode );

        if ( qwtPlotWidget )
        {
            legend->connect( qwtPlotWidget->qwtPlot(),
                             SIGNAL( legendDataChanged( const QVariant&, const QList<QwtLegendData>& ) ),
                             SLOT( updateLegend( const QVariant&, const QList<QwtLegendData>& ) ) );
            qwtPlotWidget->connect( legend, SIGNAL( clicked( const QVariant&, int ) ), SLOT( onLegendClicked( const QVariant&, int ) ) );
        }
        else
        {
#ifdef USE_QTCHARTS
            auto qtchartPlotWidget = dynamic_cast<RiuQtChartsPlotWidget*>( plotWidget );
            legend->connect( qtchartPlotWidget,
                             SIGNAL( legendDataChanged( const QList<QwtLegendData>& ) ),
                             SLOT( updateLegend( const QList<QwtLegendData>& ) ) );
#endif
        }

        QObject::connect( legend, SIGNAL( legendUpdated() ), this, SLOT( onLegendUpdated() ) );

        legend->contentsWidget()->layout()->setAlignment( Qt::AlignBottom | Qt::AlignHCenter );
        legend->setVisible( false );

        legendFrame->setVisible( false );

        plotWidget->updateLegend();
    }
    m_legends.insert( static_cast<int>( index ), legend );
    m_legendFrames.insert( static_cast<int>( index ), legendFrame );

    scheduleUpdate();
}

//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
void RiuMultiPlotPage::removePlot( RiuPlotWidget* plotWidget )
{
    if ( !plotWidget ) return;

    int plotWidgetIdx = m_plotWidgets.indexOf( plotWidget );
    if ( plotWidgetIdx < 0 ) return;

    m_plotWidgets.removeAt( plotWidgetIdx );
    plotWidget->setParent( nullptr );

    RiuQwtPlotLegend* legend = m_legends[plotWidgetIdx];
    if ( legend )
    {
        legend->setParent( nullptr );
        delete legend;
    }
    m_legends.removeAt( plotWidgetIdx );

    QLabel* subTitle = m_subTitles[plotWidgetIdx];
    subTitle->setParent( nullptr );
    m_subTitles.removeAt( plotWidgetIdx );
    delete subTitle;

    m_childCountForAdjustSizeOperation.clear();

    scheduleUpdate();
}

//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
void RiuMultiPlotPage::removeAllPlots()
{
    auto plotWidgets = m_plotWidgets;
    for ( RiuPlotWidget* plotWidget : plotWidgets )
    {
        removePlot( plotWidget );
    }
}

//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
void RiuMultiPlotPage::setPlotTitle( const QString& plotTitle )
{
    m_plotTitle->setText( plotTitle );
}

//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
void RiuMultiPlotPage::setTitleVisible( bool visible )
{
    m_plotTitle->setVisible( visible );
}

//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
void RiuMultiPlotPage::setTitleFontSizes( int titleFontSize, int subTitleFontSize )
{
    m_titleFontPixelSize    = caf::FontTools::pointSizeToPixelSize( titleFontSize );
    m_subTitleFontPixelSize = caf::FontTools::pointSizeToPixelSize( subTitleFontSize );

    scheduleUpdate();
}

//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
void RiuMultiPlotPage::setLegendFontSize( int legendFontSize )
{
    m_legendFontPixelSize = caf::FontTools::pointSizeToPixelSize( legendFontSize );

    scheduleUpdate();
}

//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
void RiuMultiPlotPage::setAxisFontSizes( int axisTitleFontSize, int axisValueFontSize )
{
    m_axisTitleFontSize = axisTitleFontSize;
    m_axisValueFontSize = axisValueFontSize;

    scheduleUpdate();
}

//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
void RiuMultiPlotPage::setSubTitlesVisible( bool visible )
{
    m_showSubTitles = visible;
}

//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
bool RiuMultiPlotPage::previewModeEnabled() const
{
    return m_previewMode;
}

//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
void RiuMultiPlotPage::setPagePreviewModeEnabled( bool previewMode )
{
    m_previewMode = previewMode;
}

//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
int RiuMultiPlotPage::indexOfPlotWidget( RiuPlotWidget* plotWidget )
{
    return m_plotWidgets.indexOf( plotWidget );
}

//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
void RiuMultiPlotPage::scheduleUpdate( RiaDefines::MultiPlotPageUpdateType whatToUpdate )
{
    CAF_ASSERT( m_plotDefinition );
    RiaPlotWindowRedrawScheduler::instance()->scheduleMultiPlotPageUpdate( this, whatToUpdate );
}

//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
void RiuMultiPlotPage::scheduleReplotOfAllPlots()
{
    for ( RiuPlotWidget* plotWidget : visiblePlotWidgets() )
    {
        plotWidget->scheduleReplot();
    }
}

//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
void RiuMultiPlotPage::updateSubTitles()
{
    for ( int i = 0; i < m_plotWidgets.size(); ++i )
    {
        if ( m_plotWidgets[i]->isChecked() )
        {
            m_subTitles[i]->setText( m_plotWidgets[i]->plotTitle() );
        }
    }
}

//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
void RiuMultiPlotPage::renderTo( QPaintDevice* paintDevice )
{
    auto scaling = RiaDefines::scalingFactor( paintDevice );

    QPainter painter( paintDevice );
    renderTo( &painter, scaling );
}

//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
void RiuMultiPlotPage::renderTo( QPainter* painter, double scalingFactor )
{
    painter->fillRect( painter->viewport(), Qt::white );
    m_plotTitle->render( painter );

    // Subtract margins because we are rendering into part inside the margins
    QPoint marginOffset( m_layout->contentsMargins().left(), m_layout->contentsMargins().top() );

    for ( auto subTitle : subTitlesForVisiblePlots() )
    {
        if ( subTitle->isVisible() )
        {
            QPoint renderOffset = m_plotWidgetFrame->mapToParent( subTitle->frameGeometry().topLeft() ) - marginOffset;
            subTitle->render( painter, renderOffset );
        }
    }

    for ( auto legend : legendsForVisiblePlots() )
    {
        if ( legend )
        {
            QPoint renderOffset = m_plotWidgetFrame->mapToParent( legend->frameGeometry().topLeft() ) - marginOffset;
            legend->render( painter, renderOffset );
        }
    }

    for ( auto plotWidget : visiblePlotWidgets() )
    {
        QRect  plotWidgetGeometry     = plotWidget->frameGeometry();
        QPoint plotWidgetTopLeft      = plotWidgetGeometry.topLeft();
        QPoint plotWidgetFrameTopLeft = m_plotWidgetFrame->frameGeometry().topLeft();
        plotWidgetGeometry.moveTo( plotWidgetTopLeft + plotWidgetFrameTopLeft - marginOffset );

        plotWidget->renderTo( painter, plotWidgetGeometry, scalingFactor );
    }
}

//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
void RiuMultiPlotPage::contextMenuEvent( QContextMenuEvent* event )
{
    QMenu                      menu;
    caf::CmdFeatureMenuBuilder menuBuilder;

    caf::SelectionManager::instance()->setSelectedItem( ownerPlotDefinition() );

    menuBuilder << "RicShowPlotDataFeature";
    menuBuilder << "RicShowContributingWellsFromPlotFeature";
    menuBuilder << "RicNewDefaultSummaryPlotFeature";

    menuBuilder.appendToMenu( &menu );

    if ( menu.actions().size() > 0 )
    {
        menu.exec( event->globalPos() );
    }
}

//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
QLabel* RiuMultiPlotPage::createTitleLabel() const
{
    QLabel* plotTitle = new QLabel( "", nullptr );
    plotTitle->setAlignment( Qt::AlignHCenter );
    plotTitle->setWordWrap( true );
    plotTitle->setSizePolicy( QSizePolicy::Preferred, QSizePolicy::Preferred );
    return plotTitle;
}

//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
void RiuMultiPlotPage::showEvent( QShowEvent* event )
{
    QWidget::showEvent( event );
    performUpdate( RiaDefines::MultiPlotPageUpdateType::ALL );
}

//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
bool RiuMultiPlotPage::hasHeightForWidth() const
{
    return true;
}

//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
int RiuMultiPlotPage::heightForWidth( int width ) const
{
    QPageLayout pageLayout  = m_plotDefinition->pageLayout();
    QRectF      rect        = pageLayout.fullRectPoints();
    double      aspectRatio = rect.height() / rect.width();
    int         height      = static_cast<int>( width * aspectRatio );
    return height;
}

//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
void RiuMultiPlotPage::updateMarginsFromPageLayout()
{
    if ( m_previewMode )
    {
        QPageLayout pageLayout = m_plotDefinition->pageLayout();
        const int   resolution = RiaGuiApplication::applicationResolution();
        QMargins    margins    = pageLayout.marginsPixels( resolution );
        m_layout->setContentsMargins( margins );
    }
    else
    {
        m_layout->setContentsMargins( 0, 0, 0, 0 );
    }
}

//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
QSize RiuMultiPlotPage::sizeHint() const
{
    if ( m_previewMode )
    {
        QPageLayout pageLayout = RiaPreferences::current()->defaultPageLayout();
        if ( m_plotDefinition )
        {
            pageLayout = m_plotDefinition->pageLayout();
        }

        const int resolution = RiaGuiApplication::applicationResolution();
        QRect     rect       = pageLayout.fullRectPixels( resolution );
        return rect.size();
    }
    return QWidget::sizeHint();
}
//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
QSize RiuMultiPlotPage::minimumSizeHint() const
{
    QSize fullSizeHint = sizeHint();
    return QSize( fullSizeHint.width() / 2, fullSizeHint.height() / 2 );
}

//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
std::pair<int, int> RiuMultiPlotPage::rowAndColumnCount( int plotWidgetCount ) const
{
    if ( plotWidgetCount == 0 )
    {
        return std::make_pair( 0, 0 );
    }

    int columnCount = std::max( 1, m_plotDefinition->columnCount() );
    int rowCount    = static_cast<int>( std::ceil( plotWidgetCount / static_cast<double>( columnCount ) ) );
    return std::make_pair( rowCount, columnCount );
}

//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
void RiuMultiPlotPage::onSelectionManagerSelectionChanged( const std::set<int>& changedSelectionLevels )
{
    if ( !m_plotDefinition ) return;

    for ( RiuPlotWidget* plotWidget : m_plotWidgets )
    {
        if ( !plotWidget ) continue;
        RimPlot* plot = plotWidget->plotDefinition();
        if ( !plot ) continue;

        bool isSelected = false;
        for ( int changedLevel : changedSelectionLevels )
        {
            isSelected = isSelected || caf::SelectionManager::instance()->isSelected( plot, changedLevel );
        }
    }
}

//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
bool RiuMultiPlotPage::showYAxis( int row, int column ) const
{
    return true;
}

//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
void RiuMultiPlotPage::performUpdate( RiaDefines::MultiPlotPageUpdateType whatToUpdate )
{
    auto multiPlot = dynamic_cast<RimMultiPlot*>( m_plotDefinition.p() );
    if ( multiPlot && !multiPlot->isValid() ) return;

    if ( whatToUpdate == RiaDefines::MultiPlotPageUpdateType::ALL )
    {
        applyLook();
        updateTitleFont();
        updateMarginsFromPageLayout();

        reinsertPlotWidgets();
        alignCanvasTops();
        if ( m_autoAlignAxes ) alignAxes();
        updatePlotLayouts();
        return;
    }

    if ( ( whatToUpdate & RiaDefines::MultiPlotPageUpdateType::LEGEND ) == RiaDefines::MultiPlotPageUpdateType::LEGEND )
    {
        refreshLegends();
        alignCanvasTops();
        if ( m_autoAlignAxes ) alignAxes();
    }

    if ( ( whatToUpdate & RiaDefines::MultiPlotPageUpdateType::TITLE ) == RiaDefines::MultiPlotPageUpdateType::TITLE )
    {
        updateTitleFont();
        updateSubTitles();
    }
}

//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
void RiuMultiPlotPage::onLegendUpdated()
{
    scheduleUpdate( RiaDefines::MultiPlotPageUpdateType::LEGEND );
}

//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
void RiuMultiPlotPage::refreshLegends()
{
    QList<QPointer<RiuQwtPlotLegend>> legends = this->legendsForVisiblePlots();
    for ( const auto& l : legends )
    {
        l->setVisible( !l->isEmpty() );
        updateLegendFont( l );
    }
}

//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
void RiuMultiPlotPage::updatePlotLayouts()
{
    QList<QPointer<RiuPlotWidget>> plotWidgets = this->visiblePlotWidgets();
    for ( auto p : plotWidgets )
        p->updateLayout();
}

//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
void RiuMultiPlotPage::reinsertPlotWidgets()
{
    clearGridLayout();

    QList<QPointer<QLabel>>                   subTitles    = this->subTitlesForVisiblePlots();
    QList<QPointer<RiuQwtPlotLegend>>         legends      = this->legendsForVisiblePlots();
    QList<QPointer<RiuDraggableOverlayFrame>> legendFrames = this->legendFramesForVisiblePlots();
    QList<QPointer<RiuPlotWidget>>            plotWidgets  = this->visiblePlotWidgets();

    m_visibleIndexToPositionMapping.clear();

    if ( !plotWidgets.empty() )
    {
        auto [rowCount, columnCount] = this->rowAndColumnCount( plotWidgets.size() );

        int row    = 0;
        int column = 0;
        for ( int visibleIndex = 0; visibleIndex < plotWidgets.size(); ++visibleIndex )
        {
            auto plotWidget  = plotWidgets[visibleIndex];
            auto legend      = legends[visibleIndex];
            auto legendFrame = legendFrames[visibleIndex];
            auto subTitle    = subTitles[visibleIndex];

            int expectedColSpan = plotWidget->colSpan();
            int colSpan         = std::min( expectedColSpan, columnCount );
            int rowSpan         = plotWidget->rowSpan();

            auto position           = findAvailableRowAndColumn( row, column, colSpan, columnCount );
            std::tie( row, column ) = position;

            m_visibleIndexToPositionMapping[visibleIndex] = position;

            reinsertPlotWidget( plotWidget, legend, legendFrame, subTitle, row, column, rowSpan, colSpan );
        }
    }
}

//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
void RiuMultiPlotPage::reinsertPlotWidget( RiuPlotWidget*            plotWidget,
                                           RiuQwtPlotLegend*         legend,
                                           RiuDraggableOverlayFrame* legendFrame,
                                           QLabel*                   subTitle,
                                           int                       row,
                                           int                       column,
                                           int                       rowSpan,
                                           int                       colSpan )
{
    m_gridLayout->addWidget( subTitle, 3 * row, column, 1, colSpan );

    addLegendWidget( plotWidget, legend, legendFrame, 3 * row + 1, column, colSpan );

    m_gridLayout->addWidget( plotWidget, 3 * row + 2, column, 1 + ( rowSpan - 1 ) * 3, colSpan );

    updateSubTitleVisibility( subTitle );

    setDefaultAxisProperties( plotWidget, row, column );

    adjustHeadingSpacing( plotWidget );

    plotWidget->show();

    updateLegendVisibility( plotWidget, legend, legendFrame );

    setRowAndColumnStretches( row, column, rowSpan, colSpan );
}

//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
void RiuMultiPlotPage::addLegendWidget( RiuPlotWidget*            plotWidget,
                                        RiuQwtPlotLegend*         legend,
                                        RiuDraggableOverlayFrame* legendFrame,
                                        int                       row,
                                        int                       column,
                                        int                       colSpan )
{
    if ( !legend ) return;

    if ( m_plotDefinition->legendPosition() == RimPlotWindow::LegendPosition::ABOVE )
    {
        m_gridLayout->addWidget( legend, row, column, 1, colSpan, Qt::AlignHCenter | Qt::AlignBottom );
    }
    else
    {
        auto anchor = RiuDraggableOverlayFrame::AnchorCorner::TopRight;

        if ( m_plotDefinition->legendPosition() == RimPlotWindow::LegendPosition::INSIDE_UPPER_RIGHT )
            anchor = RiuDraggableOverlayFrame::AnchorCorner::TopRight;
        else if ( m_plotDefinition->legendPosition() == RimPlotWindow::LegendPosition::INSIDE_UPPER_LEFT )
            anchor = RiuDraggableOverlayFrame::AnchorCorner::TopLeft;

        auto overlayFrame = new RiuQwtLegendOverlayContentFrame;
        overlayFrame->setLegend( legend );
        legendFrame->setContentFrame( overlayFrame );
        legendFrame->setAnchorCorner( anchor );
        plotWidget->removeOverlayFrame( legendFrame );
        plotWidget->addOverlayFrame( legendFrame );
    }
}

//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
void RiuMultiPlotPage::updateLegendVisibility( RiuPlotWidget* plotWidget, RiuQwtPlotLegend* legend, RiuDraggableOverlayFrame* legendFrame )
{
    if ( !legend ) return;

    if ( m_plotDefinition->legendsVisible() && !legend->isEmpty() )
    {
        updateLegendColumns( legend );
        updateLegendFont( legend );
        legend->show();

        if ( m_plotDefinition->legendPosition() == RimPlotWindow::LegendPosition::INSIDE_UPPER_LEFT ||
             m_plotDefinition->legendPosition() == RimPlotWindow::LegendPosition::INSIDE_UPPER_RIGHT )
        {
            plotWidget->addOverlayFrame( legendFrame );
            legendFrame->show();
        }
        else
        {
            plotWidget->removeOverlayFrame( legendFrame );
            legendFrame->hide();
        }
    }
    else
    {
        legend->hide();
        legendFrame->hide();
        plotWidget->removeOverlayFrame( legendFrame );
    }
}

//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
void RiuMultiPlotPage::updateSubTitleVisibility( QLabel* subTitle )
{
    bool isSubTitleVisible = m_showSubTitles && !subTitle->text().isEmpty();
    subTitle->setVisible( isSubTitleVisible );
    QFont subTitleFont = subTitle->font();
    subTitleFont.setPixelSize( m_subTitleFontPixelSize );
    subTitle->setFont( subTitleFont );
}

//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
void RiuMultiPlotPage::setDefaultAxisProperties( RiuPlotWidget* plotWidget, int row, int column )
{
    plotWidget->setAxisLabelsAndTicksEnabled( RiuPlotAxis::defaultLeft(), showYAxis( row, column ), showYAxis( row, column ) );
    plotWidget->setAxisTitleEnabled( RiuPlotAxis::defaultLeft(), showYAxis( row, column ) );
}

//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
void RiuMultiPlotPage::adjustHeadingSpacing( RiuPlotWidget* plotWidget )
{
    // Adjust the space below a graph to make sure the heading of the row below is closest to the
    // corresponding graph

    if ( !m_plotDefinition ) return;

    int bottomMargin = m_plotDefinition->bottomMargin();
    if ( bottomMargin < 0 ) return;

    auto margins = plotWidget->contentsMargins();

    margins.setBottom( bottomMargin );
    plotWidget->setContentsMargins( margins );
}

//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
void RiuMultiPlotPage::setRowAndColumnStretches( int row, int column, int rowSpan, int colSpan )
{
    // Set basic row and column stretches
    for ( int r = row; r < row + rowSpan; ++r )
    {
        m_gridLayout->setRowStretch( 3 * r + 2, 1 );
    }

    for ( int c = column; c < column + colSpan; ++c )
    {
        int colStretch = 6; // Empirically chosen to try to counter the width of the axis on the first track
        if ( showYAxis( row, column ) ) colStretch += 1;
        m_gridLayout->setColumnStretch( c, std::max( colStretch, m_gridLayout->columnStretch( c ) ) );
    }
}

//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
void RiuMultiPlotPage::updateLegendFont( RiuQwtPlotLegend* legend )
{
    QFont legendFont = legend->font();
    legendFont.setPixelSize( m_legendFontPixelSize );

    // Set font size for all existing labels
    QList<QwtLegendLabel*> labels = legend->findChildren<QwtLegendLabel*>();
    for ( QwtLegendLabel* label : labels )
    {
        label->setFont( legendFont );
        label->setMargin( 0 );
        label->setSpacing( 1 );
    }

    legend->setFont( legendFont );
    legend->setContentsMargins( 0, 0, 0, 0 );
}

//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
void RiuMultiPlotPage::updateLegendColumns( RiuQwtPlotLegend* legend )
{
    int legendColumns = 1;
    if ( m_plotDefinition->legendsHorizontal() )
    {
        legendColumns = 0; // unlimited
    }

    legend->setMaxColumns( legendColumns );
}

//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
int RiuMultiPlotPage::alignCanvasTops()
{
    CVF_ASSERT( m_legends.size() == m_plotWidgets.size() );

    QList<QPointer<RiuPlotWidget>>    plotWidgets = visiblePlotWidgets();
    QList<QPointer<RiuQwtPlotLegend>> legends     = legendsForVisiblePlots();
    if ( plotWidgets.empty() ) return 0;

    auto rowAndColumnCount = this->rowAndColumnCount( plotWidgets.size() );

    std::vector<double> maxExtents( rowAndColumnCount.first, 0.0 );

    for ( int visibleIndex = 0; visibleIndex < plotWidgets.size(); ++visibleIndex )
    {
        RiuQwtPlotWidget* qwtPlotWidget = dynamic_cast<RiuQwtPlotWidget*>( plotWidgets[visibleIndex].data() );
        if ( qwtPlotWidget )
        {
            int row = visibleIndex / rowAndColumnCount.second;
            if ( plotWidgets[visibleIndex]->axisEnabled( RiuPlotAxis::defaultTop() ) )
            {
                QFont font      = qwtPlotWidget->qwtPlot()->axisFont( QwtAxis::XTop );
                maxExtents[row] = std::max( maxExtents[row], qwtPlotWidget->qwtPlot()->axisScaleDraw( QwtAxis::XTop )->extent( font ) );
            }
        }
    }

    for ( int visibleIndex = 0; visibleIndex < plotWidgets.size(); ++visibleIndex )
    {
        RiuQwtPlotWidget* qwtPlotWidget = dynamic_cast<RiuQwtPlotWidget*>( plotWidgets[visibleIndex].data() );
        if ( qwtPlotWidget )
        {
            int row = visibleIndex / rowAndColumnCount.second;
            qwtPlotWidget->qwtPlot()->axisScaleDraw( QwtAxis::XTop )->setMinimumExtent( maxExtents[row] );
            auto legend = legends[visibleIndex];
            if ( legend )
            {
                int previousChildCount = -1;

                auto it = m_childCountForAdjustSizeOperation.find( legend );
                if ( it != m_childCountForAdjustSizeOperation.end() )
                {
                    previousChildCount = it->second;
                }

                auto legendItemCount = legend->contentsWidget()->children().size();
                if ( previousChildCount != legendItemCount )
                {
                    legends[visibleIndex]->adjustSize();
                    m_childCountForAdjustSizeOperation[legend] = legendItemCount;
                }
            }
        }
    }
    return maxExtents[0];
}

//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
void RiuMultiPlotPage::clearGridLayout()
{
    if ( m_gridLayout )
    {
        for ( int tIdx = 0; tIdx < m_plotWidgets.size(); ++tIdx )
        {
            m_gridLayout->removeWidget( m_subTitles[tIdx] );
            if ( m_legends[tIdx] )
            {
                m_gridLayout->removeWidget( m_legends[tIdx] );
            }
            m_gridLayout->removeWidget( m_plotWidgets[tIdx] );
        }

        QLayoutItem* item;
        while ( ( item = m_gridLayout->takeAt( 0 ) ) != nullptr )
        {
        }
        QWidget().setLayout( m_gridLayout );
        delete m_gridLayout;
        m_gridLayout = new QGridLayout( m_plotWidgetFrame );
    }

    for ( int tIdx = 0; tIdx < m_plotWidgets.size(); ++tIdx )
    {
        if ( m_plotWidgets[tIdx] )
        {
            m_plotWidgets[tIdx]->removeOverlayFrame( m_legendFrames[tIdx] );
            m_plotWidgets[tIdx]->hide();
        }
        if ( m_legends[tIdx] )
        {
            m_legends[tIdx]->hide();
        }
        if ( m_subTitles[tIdx] )
        {
            m_subTitles[tIdx]->hide();
        }
    }
}

//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
QList<QPointer<RiuPlotWidget>> RiuMultiPlotPage::visiblePlotWidgets() const
{
    QList<QPointer<RiuPlotWidget>> plotWidgets;
    for ( QPointer<RiuPlotWidget> plotWidget : m_plotWidgets )
    {
        if ( plotWidget->isChecked() )
        {
            plotWidgets.push_back( plotWidget );
        }
    }
    return plotWidgets;
}

//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
QList<QPointer<RiuQwtPlotLegend>> RiuMultiPlotPage::legendsForVisiblePlots() const
{
    QList<QPointer<RiuQwtPlotLegend>> legends;
    for ( int i = 0; i < m_plotWidgets.size(); ++i )
    {
        if ( m_plotWidgets[i]->isChecked() )
        {
            legends.push_back( m_legends[i] );
        }
    }
    return legends;
}

//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
QList<QPointer<RiuDraggableOverlayFrame>> RiuMultiPlotPage::legendFramesForVisiblePlots() const
{
    QList<QPointer<RiuDraggableOverlayFrame>> legendFrames;
    for ( int i = 0; i < m_plotWidgets.size(); ++i )
    {
        if ( m_plotWidgets[i]->isChecked() )
        {
            legendFrames.push_back( m_legendFrames[i] );
        }
    }
    return legendFrames;
}

//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
QList<QPointer<QLabel>> RiuMultiPlotPage::subTitlesForVisiblePlots() const
{
    QList<QPointer<QLabel>> subTitles;
    for ( int i = 0; i < m_plotWidgets.size(); ++i )
    {
        if ( m_plotWidgets[i]->isChecked() )
        {
            m_subTitles[i]->setText( m_plotWidgets[i]->plotTitle() );
            subTitles.push_back( m_subTitles[i] );
        }
    }
    return subTitles;
}

//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
std::pair<int, int> RiuMultiPlotPage::findAvailableRowAndColumn( int startRow, int startColumn, int columnSpan, int columnCount ) const
{
    int availableRow    = startRow;
    int availableColumn = startColumn;
    while ( true )
    {
        for ( ; availableColumn < columnCount; ++availableColumn )
        {
            bool fits = true;
            for ( int c = availableColumn; ( c < availableColumn + columnSpan ) && fits; ++c )
            {
                if ( c >= columnCount )
                {
                    fits = false;
                }

                if ( m_gridLayout->itemAtPosition( 3 * availableRow, c ) != nullptr )
                {
                    fits = false;
                }
            }
            if ( fits )
            {
                return std::make_pair( availableRow, availableColumn );
            }
        }
        availableColumn = 0;
        availableRow++;
    }
    return std::make_pair( availableRow, availableColumn );
}

//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
void RiuMultiPlotPage::applyLook()
{
    QPalette newPalette( palette() );
    newPalette.setColor( QPalette::Window, Qt::white );
    setPalette( newPalette );

    setAutoFillBackground( true );
    setBackgroundRole( QPalette::Window );

    if ( m_previewMode )
    {
        setSizePolicy( QSizePolicy::MinimumExpanding, QSizePolicy::MinimumExpanding );

        QGraphicsDropShadowEffect* dropShadowEffect = new QGraphicsDropShadowEffect( this );
        dropShadowEffect->setOffset( 4.0, 4.0 );
        dropShadowEffect->setBlurRadius( 4.0 );
        dropShadowEffect->setColor( QColor( 40, 40, 40, 40 ) );
        setGraphicsEffect( dropShadowEffect );
    }
    else
    {
        setSizePolicy( QSizePolicy::Preferred, QSizePolicy::Preferred );
        setGraphicsEffect( nullptr );
    }
}

//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
void RiuMultiPlotPage::updateTitleFont()
{
    auto titleFont = m_plotTitle->font();
    titleFont.setPixelSize( m_titleFontPixelSize );
    m_plotTitle->setFont( titleFont );
}

//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
void RiuMultiPlotPage::alignAxes()
{
    auto [rowCount, columnCount] = rowAndColumnCount( visiblePlotWidgets().size() );

    auto matchRow = []( int row, int column, int targetRow ) { return row == targetRow; };

    for ( int row = 0; row < rowCount; row++ )
    {
        alignAxis( QwtAxisId( QwtAxis::Position::XTop, 0 ), row, matchRow );
        alignAxis( QwtAxisId( QwtAxis::Position::XBottom, 0 ), row, matchRow );
    }

    auto matchColumn = []( int row, int column, int targetColumn ) { return column == targetColumn; };

    for ( int column = 0; column < columnCount; column++ )
    {
        alignAxis( QwtAxisId( QwtAxis::Position::YLeft, 0 ), column, matchColumn );
        alignAxis( QwtAxisId( QwtAxis::Position::YRight, 0 ), column, matchColumn );
    }
}

//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
void RiuMultiPlotPage::alignAxis( QwtAxisId axis, int targetRowOrColumn, std::function<bool( int, int, int )> matchPosition )
{
    auto rowAndColumnFromIdx = [this]( int idx )
    {
        auto hit = m_visibleIndexToPositionMapping.find( idx );
        CAF_ASSERT( hit != m_visibleIndexToPositionMapping.end() );
        return hit->second;
    };

    QList<QPointer<RiuPlotWidget>> plotWidgets = visiblePlotWidgets();

    // Find the max extent of the "scale draws" for the given axis
    double maxExtent = 0;
    for ( int tIdx = 0; tIdx < plotWidgets.size(); ++tIdx )
    {
        RiuPlotWidget* plotWidget = plotWidgets[tIdx];
        auto [row, column]        = rowAndColumnFromIdx( tIdx );
        bool matchesRowOrColumn   = matchPosition( row, column, targetRowOrColumn );
        if ( plotWidget && matchesRowOrColumn )
        {
            RiuQwtPlotWidget* riuQwtPlotWidget = dynamic_cast<RiuQwtPlotWidget*>( plotWidget );

            if ( riuQwtPlotWidget )
            {
                QwtPlot* p = riuQwtPlotWidget->qwtPlot();
                if ( p )
                {
                    QwtScaleWidget* scaleWidget = p->axisWidget( axis );
                    QwtScaleDraw*   sd          = scaleWidget->scaleDraw();
                    sd->setMinimumExtent( 0.0 );
                    maxExtent = std::max( sd->extent( scaleWidget->font() ), maxExtent );
                }
            }
        }
    }

    // Set minimum extent for all "scale draws" for the given axis.
    for ( int tIdx = 0; tIdx < plotWidgets.size(); ++tIdx )
    {
        RiuPlotWidget* plotWidget = plotWidgets[tIdx];
        auto [row, column]        = rowAndColumnFromIdx( tIdx );
        bool matchesRowOrColumn   = matchPosition( row, column, targetRowOrColumn );
        if ( plotWidget && matchesRowOrColumn )
        {
            RiuQwtPlotWidget* riuQwtPlotWidget = dynamic_cast<RiuQwtPlotWidget*>( plotWidget );
            if ( riuQwtPlotWidget )
            {
                QwtPlot* p = riuQwtPlotWidget->qwtPlot();
                if ( p )
                {
                    QwtScaleWidget* scaleWidget = p->axisWidget( axis );
                    scaleWidget->scaleDraw()->setMinimumExtent( maxExtent );
                }
            }
        }
    }
}

//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
void RiuMultiPlotPage::setAutoAlignAxes( bool autoAlignAxes )
{
    m_autoAlignAxes = autoAlignAxes;
}
