/////////////////////////////////////////////////////////////////////////////////
//
//  Copyright (C) 2021- Equinor ASA
//
//  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 "RimEnsembleWellLogCurveSet.h"

#include "RiaColorTools.h"
#include "RiaLogging.h"
#include "RiaOptionItemFactory.h"
#include "RiaResultNames.h"

#include "RimEnsembleCurveFilter.h"
#include "RimEnsembleCurveFilterCollection.h"
#include "RimEnsembleCurveSet.h"
#include "RimEnsembleStatistics.h"
#include "RimEnsembleWellLogStatistics.h"
#include "RimEnsembleWellLogStatisticsCurve.h"
#include "RimEnsembleWellLogs.h"
#include "RimEnsembleWellLogsCollection.h"
#include "RimFormationNames.h"
#include "RimFormationNamesCollection.h"
#include "RimMainPlotCollection.h"
#include "RimOilField.h"
#include "RimProject.h"
#include "RimSummaryCase.h"
#include "RimSummaryCaseCollection.h"
#include "RimWellLogCurve.h"
#include "RimWellLogFileChannel.h"
#include "RimWellLogLasFile.h"
#include "RimWellLogLasFileCurve.h"
#include "RimWellLogPlot.h"
#include "RimWellLogTrack.h"
#include "RimWellPath.h"

#include "RigFormationNames.h"
#include "RigWellLogIndexDepthOffset.h"

#include "RiuAbstractLegendFrame.h"
#include "RiuDraggableOverlayFrame.h"
#include "RiuPlotMainWindow.h"
#include "RiuQwtPlotCurveDefines.h"
#include "RiuQwtPlotWidget.h"
#include "RiuQwtSymbol.h"
#include "RiuTextContentFrame.h"

#include "cafPdmObject.h"
#include "cafPdmUiItem.h"
#include "cafPdmUiTreeOrdering.h"

#include "cvfObject.h"

#include "qwt_plot.h"
#include "qwt_plot_curve.h"
#include "qwt_symbol.h"

#include <algorithm>
#include <vector>

//--------------------------------------------------------------------------------------------------
/// Internal functions
//--------------------------------------------------------------------------------------------------
int statisticsCurveSymbolSize( RiuPlotCurveSymbol::PointSymbolEnum symbol );

namespace caf
{
template <>
void AppEnum<RimEnsembleWellLogCurveSet::ColorMode>::setUp()
{
    addItem( RimEnsembleWellLogCurveSet::ColorMode::SINGLE_COLOR, "SINGLE_COLOR", "Single Color" );
    addItem( RimEnsembleWellLogCurveSet::ColorMode::COLOR_BY_ENSEMBLE_CURVE_SET, "BY_ENSEMBLE_CURVE_SET", "Color by Ensemble Curve Set" );
    setDefault( RimEnsembleWellLogCurveSet::ColorMode::SINGLE_COLOR );
}
}; // namespace caf

CAF_PDM_SOURCE_INIT( RimEnsembleWellLogCurveSet, "RimEnsembleWellLogCurveSet" );

//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
RimEnsembleWellLogCurveSet::RimEnsembleWellLogCurveSet()
    : filterChanged( this )

{
    CAF_PDM_InitObject( "Ensemble Curve Set", ":/EnsembleCurveSet16x16.png" );

    CAF_PDM_InitFieldNoDefault( &m_curves, "EnsembleCurveSet", "Ensemble Curve Set" );
    m_curves.uiCapability()->setUiHidden( true );
    m_curves.uiCapability()->setUiTreeChildrenHidden( false );

    CAF_PDM_InitField( &m_showCurves, "IsActive", true, "Show Curves" );
    m_showCurves.uiCapability()->setUiHidden( true );

    CAF_PDM_InitFieldNoDefault( &m_ensembleWellLogs, "EnsembleWellLogs", "Ensemble Well Logs" );
    m_ensembleWellLogs.uiCapability()->setUiTreeChildrenHidden( true );

    CAF_PDM_InitField( &m_wellLogChannelName, "WellLogChannelName", QString( "None" ), "Well Log Channel Name" );

    CAF_PDM_InitFieldNoDefault( &m_ensembleCurveSet, "FilterEnsembleCurveSet", "Filter by Ensemble Curve Set" );
    CAF_PDM_InitFieldNoDefault( &m_depthEqualization, "DepthEqualization", "Depth Equalization" );

    CAF_PDM_InitField( &m_colorMode, "ColorMode", caf::AppEnum<ColorMode>( ColorMode::SINGLE_COLOR ), "Coloring Mode" );

    CAF_PDM_InitField( &m_color, "Color", RiaColorTools::textColor3f(), "Color" );

    CAF_PDM_InitFieldNoDefault( &m_statistics, "Statistics", "Statistics" );
    m_statistics = new RimEnsembleStatistics( this );

    CAF_PDM_InitField( &m_userDefinedName, "UserDefinedName", QString( "Ensemble Curve Set" ), "Curve Set Name" );

    CAF_PDM_InitFieldNoDefault( &m_autoGeneratedName, "AutoGeneratedName", "Curve Set Name" );
    m_autoGeneratedName.registerGetMethod( this, &RimEnsembleWellLogCurveSet::createAutoName );
    m_autoGeneratedName.uiCapability()->setUiReadOnly( true );
    m_autoGeneratedName.xmlCapability()->disableIO();

    CAF_PDM_InitField( &m_isUsingAutoName, "AutoName", true, "Auto Name" );

    CAF_PDM_InitFieldNoDefault( &m_curveAppearance, "PlotCurveAppearance", "PlotCurveAppearance" );
    m_curveAppearance = new RimPlotCurveAppearance;
    m_curveAppearance->setInterpolationVisible( false );
    m_curveAppearance->setColorVisible( false );
    m_curveAppearance->setFillOptionsVisible( false );

    m_curveAppearance->setSymbol( RiuPlotCurveSymbol::PointSymbolEnum::SYMBOL_ELLIPSE );
    m_curveAppearance->setSymbolSize( 5 );
    m_curveAppearance->setLineStyle( RiuQwtPlotCurveDefines::LineStyleEnum::STYLE_NONE );
    m_curveAppearance->setSymbolEdgeColor( cvf::Color3f::BLACK );
    m_curveAppearance->appearanceChanged.connect( this, &RimEnsembleWellLogCurveSet::onEnsembleCurvesAppearanceChanged );

    m_qwtPlotCurveForLegendText = new QwtPlotCurve;
    m_qwtPlotCurveForLegendText->setLegendAttribute( QwtPlotCurve::LegendShowSymbol, true );

    m_ensembleWellLogStatistics.reset( new RimEnsembleWellLogStatistics );

    m_disableStatisticCurves = false;
    m_isCurveSetFiltered     = false;

    setDeletable( true );
}

//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
RimEnsembleWellLogCurveSet::~RimEnsembleWellLogCurveSet()
{
    auto plotTrack = firstAncestorOrThisOfType<RimWellLogTrack>();
    if ( plotTrack && plotTrack->viewer() )
    {
        if ( m_legendOverlayFrame )
        {
            plotTrack->viewer()->removeOverlayFrame( m_legendOverlayFrame );
        }
    }

    if ( m_legendOverlayFrame )
    {
        m_legendOverlayFrame->setParent( nullptr );
        delete m_legendOverlayFrame;
    }

    if ( m_filterOverlayFrame )
    {
        m_filterOverlayFrame->setParent( nullptr );
        delete m_filterOverlayFrame;
    }

    delete m_qwtPlotCurveForLegendText;
}

//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
bool RimEnsembleWellLogCurveSet::isCurvesVisible()
{
    return m_showCurves();
}

//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
void RimEnsembleWellLogCurveSet::setColor( cvf::Color3f color )
{
    m_color = color;
}

//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
void RimEnsembleWellLogCurveSet::loadDataAndUpdate( bool updateParentPlot )
{
    updateAllCurves();
    updateFilterLegend();
    updateAllTextInPlot();

    if ( updateParentPlot )
    {
        auto parentPlot = firstAncestorOrThisOfTypeAsserted<RimWellLogTrack>();
        parentPlot->viewer()->scheduleReplot();
    }
}

//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
void RimEnsembleWellLogCurveSet::setParentPlotNoReplot( RiuPlotWidget* plot )
{
    for ( RimWellLogCurve* curve : m_curves )
    {
        curve->setParentPlotNoReplot( plot );
    }
}

//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
void RimEnsembleWellLogCurveSet::detachPlotCurves()
{
    for ( RimWellLogCurve* curve : m_curves )
    {
        curve->detach();
    }

    m_qwtPlotCurveForLegendText->detach();
}

//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
void RimEnsembleWellLogCurveSet::reattachPlotCurves()
{
    for ( RimWellLogCurve* curve : m_curves )
    {
        curve->reattach();
    }

    m_qwtPlotCurveForLegendText->detach();

    auto plot = firstAncestorOrThisOfType<RimWellLogTrack>();
    if ( plot )
    {
        m_qwtPlotCurveForLegendText->attach( plot->viewer()->qwtPlot() );
    }
}

//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
std::vector<RimWellLogCurve*> RimEnsembleWellLogCurveSet::curves() const
{
    return m_curves.ptrReferencedObjectsByType();
}

//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
void RimEnsembleWellLogCurveSet::deleteEnsembleCurves()
{
    auto plotTrack = firstAncestorOrThisOfTypeAsserted<RimWellLogTrack>();

    std::vector<size_t> curvesIndexesToDelete;
    for ( size_t c = 0; c < m_curves.size(); c++ )
    {
        RimWellLogCurve* curve = m_curves[c];

        if ( dynamic_cast<RimEnsembleWellLogStatisticsCurve*>( curve ) == nullptr )
        {
            plotTrack->removeCurve( m_curves[c] );
            curvesIndexesToDelete.push_back( c );
        }
    }

    while ( !curvesIndexesToDelete.empty() )
    {
        size_t currIndex = curvesIndexesToDelete.back();
        m_curves.erase( currIndex );
        curvesIndexesToDelete.pop_back();
    }
}

//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
void RimEnsembleWellLogCurveSet::deleteStatisticsCurves()
{
    auto plotTrack = firstAncestorOrThisOfTypeAsserted<RimWellLogTrack>();

    std::vector<size_t> curvesIndexesToDelete;
    for ( size_t c = 0; c < m_curves.size(); c++ )
    {
        RimWellLogCurve* curve = m_curves[c];

        if ( dynamic_cast<RimEnsembleWellLogStatisticsCurve*>( curve ) != nullptr )
        {
            plotTrack->removeCurve( m_curves[c] );
            curvesIndexesToDelete.push_back( c );
        }
    }

    while ( !curvesIndexesToDelete.empty() )
    {
        size_t currIndex = curvesIndexesToDelete.back();
        m_curves.erase( currIndex );
        curvesIndexesToDelete.pop_back();
    }
}

//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
RiuDraggableOverlayFrame* RimEnsembleWellLogCurveSet::legendFrame() const
{
    return m_legendOverlayFrame;
}

//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
RimEnsembleWellLogCurveSet::ColorMode RimEnsembleWellLogCurveSet::colorMode() const
{
    return m_colorMode();
}

//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
void RimEnsembleWellLogCurveSet::setColorMode( ColorMode mode )
{
    m_colorMode = mode;
}

//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
void RimEnsembleWellLogCurveSet::onEnsembleCurvesAppearanceChanged( const caf::SignalEmitter* emitter )
{
    loadDataAndUpdate( true );
}

//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
void RimEnsembleWellLogCurveSet::updateAllCurves()
{
    RimEnsembleWellLogs* group = m_ensembleWellLogs;

    if ( group )
    {
        std::vector<RimWellLogLasFile*> allWellLogFiles = group->wellLogFiles();
        std::vector<RimWellLogLasFile*> filteredCases   = filterEnsembleCases( allWellLogFiles );

        m_isCurveSetFiltered = filteredCases.size() < allWellLogFiles.size();

        updateEnsembleCurves( filteredCases );
        updateStatisticsCurves( m_statistics->basedOnFilteredCases() ? filteredCases : allWellLogFiles );
    }

    filterChanged.send();
}

//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
void RimEnsembleWellLogCurveSet::updateEditors()
{
    updateConnectedEditors();
}

//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
void RimEnsembleWellLogCurveSet::fieldChangedByUi( const caf::PdmFieldHandle* changedField, const QVariant& oldValue, const QVariant& newValue )
{
    bool updateTextInPlot = false;

    if ( changedField == &m_showCurves )
    {
        loadDataAndUpdate( true );

        updateConnectedEditors();

        auto plot = firstAncestorOrThisOfTypeAsserted<RimWellLogPlot>();
        plot->updateConnectedEditors();

        updateTextInPlot = true;
    }
    else if ( changedField == &m_ensembleWellLogs )
    {
        updateAllCurves();

        updateTextInPlot = true;
    }
    else if ( changedField == &m_ensembleCurveSet )
    {
        connectEnsembleCurveSetFilterSignals();
        updateAllCurves();
        loadDataAndUpdate( true );
        updateTextInPlot = true;
    }
    else if ( changedField == &m_wellLogChannelName )
    {
        updateAllCurves();
        updateTextInPlot = true;
    }
    else if ( changedField == &m_depthEqualization )
    {
        updateAllCurves();
        updateTextInPlot = true;
    }
    else if ( changedField == &m_color )
    {
        updateCurveColors();

        updateTextInPlot = true;
    }
    else if ( changedField == &m_colorMode )
    {
        updateCurveColors();

        updateTextInPlot = true;
    }
    else if ( changedField == &m_isUsingAutoName )
    {
        if ( !m_isUsingAutoName )
        {
            m_userDefinedName = createAutoName();
        }

        updateTextInPlot = true;
    }
    else if ( changedField == &m_userDefinedName )
    {
        updateTextInPlot = true;
    }

    if ( updateTextInPlot )
    {
        updateAllTextInPlot();
    }
}

//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
void RimEnsembleWellLogCurveSet::defineUiOrdering( QString uiConfigName, caf::PdmUiOrdering& uiOrdering )
{
    uiOrdering.add( &m_ensembleWellLogs );
    uiOrdering.add( &m_wellLogChannelName );
    uiOrdering.add( &m_ensembleCurveSet );
    uiOrdering.add( &m_depthEqualization );

    bool hasKLayerIndex = hasPropertyInFile( RiaResultNames::indexKResultName() );
    m_depthEqualization.uiCapability()->setUiReadOnly( !hasKLayerIndex );

    appendColorGroup( uiOrdering );

    {
        caf::PdmUiGroup* nameGroup = uiOrdering.addNewGroup( "Curve Name" );
        nameGroup->setCollapsedByDefault();
        nameGroup->add( &m_isUsingAutoName );
        if ( m_isUsingAutoName )
        {
            nameGroup->add( &m_autoGeneratedName );
        }
        else
        {
            nameGroup->add( &m_userDefinedName );
        }
    }

    caf::PdmUiGroup* statGroup          = uiOrdering.addNewGroup( "Statistics" );
    bool             showCrossPlotGroup = false;
    m_statistics->defaultUiOrdering( showCrossPlotGroup, *statGroup );

    caf::PdmUiGroup* curveAppearanceGroup = uiOrdering.addNewGroup( "Curve Appearance" );
    QString          configName           = "AppearanceOrdering";
    m_curveAppearance->uiOrdering( configName, *curveAppearanceGroup );

    uiOrdering.skipRemainingFields( true );
}

//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
void RimEnsembleWellLogCurveSet::appendColorGroup( caf::PdmUiOrdering& uiOrdering )
{
    caf::PdmUiGroup* colorsGroup = uiOrdering.addNewGroup( "Colors" );
    colorsGroup->add( &m_colorMode );

    if ( m_colorMode == ColorMode::SINGLE_COLOR )
    {
        colorsGroup->add( &m_color );
    }
}

//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
void RimEnsembleWellLogCurveSet::defineUiTreeOrdering( caf::PdmUiTreeOrdering& uiTreeOrdering, QString uiConfigName /*= ""*/ )
{
    uiTreeOrdering.skipRemainingChildren( true );

    caf::IconProvider iconProvider = uiIconProvider();
    if ( !iconProvider.valid() ) return;

    setUiIcon( iconProvider );
}

//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
caf::PdmFieldHandle* RimEnsembleWellLogCurveSet::userDescriptionField()
{
    if ( m_isUsingAutoName )
    {
        return &m_autoGeneratedName;
    }
    else
    {
        return &m_userDefinedName;
    }
}

//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
caf::PdmFieldHandle* RimEnsembleWellLogCurveSet::objectToggleField()
{
    return &m_showCurves;
}

//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
QList<caf::PdmOptionItemInfo> RimEnsembleWellLogCurveSet::calculateValueOptions( const caf::PdmFieldHandle* fieldNeedingOptions )
{
    QList<caf::PdmOptionItemInfo> options;

    if ( fieldNeedingOptions == &m_ensembleWellLogs )
    {
        RimProject*                       proj   = RimProject::current();
        std::vector<RimEnsembleWellLogs*> groups = proj->activeOilField()->ensembleWellLogsCollection()->ensembleWellLogs();

        for ( RimEnsembleWellLogs* ensemble : groups )
        {
            options.push_back( caf::PdmOptionItemInfo( ensemble->name(), ensemble ) );
        }

        options.push_front( caf::PdmOptionItemInfo( "None", nullptr ) );
    }
    else if ( fieldNeedingOptions == &m_wellLogChannelName )
    {
        if ( m_ensembleWellLogs )
        {
            std::set<QString> wellLogChannelNames;
            for ( auto wellLogFile : m_ensembleWellLogs->wellLogFiles() )
            {
                std::vector<RimWellLogFileChannel*> fileLogs = wellLogFile->wellLogChannels();
                for ( size_t i = 0; i < fileLogs.size(); i++ )
                {
                    QString wellLogChannelName = fileLogs[i]->name();
                    wellLogChannelNames.insert( wellLogChannelName );
                }
            }

            for ( auto wellLogChannelName : wellLogChannelNames )
            {
                options.push_back( caf::PdmOptionItemInfo( wellLogChannelName, wellLogChannelName ) );
            }
        }

        if ( options.empty() )
        {
            options.push_back( caf::PdmOptionItemInfo( "None", "None" ) );
        }
    }
    else if ( fieldNeedingOptions == &m_ensembleCurveSet )
    {
        RiaOptionItemFactory::appendOptionItemsForEnsembleCurveSets( &options );
    }
    else if ( fieldNeedingOptions == &m_colorMode )
    {
        auto singleColorOption = caf::AppEnum<ColorMode>( ColorMode::SINGLE_COLOR );

        options.push_back( caf::PdmOptionItemInfo( singleColorOption.uiText(), ColorMode::SINGLE_COLOR ) );

        if ( m_ensembleWellLogs && m_ensembleCurveSet )
        {
            auto byEnsembleOption = caf::AppEnum<ColorMode>( ColorMode::COLOR_BY_ENSEMBLE_CURVE_SET );
            options.push_back( caf::PdmOptionItemInfo( byEnsembleOption.uiText(), ColorMode::COLOR_BY_ENSEMBLE_CURVE_SET ) );
        }
    }

    return options;
}

//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
void RimEnsembleWellLogCurveSet::updateFilterLegend()
{
    auto plotTrack = firstAncestorOrThisOfType<RimWellLogTrack>();

    if ( plotTrack && plotTrack->viewer() )
    {
        if ( m_ensembleCurveSet != nullptr && m_ensembleCurveSet->isFiltered() )
        {
            if ( !m_filterOverlayFrame )
            {
                m_filterOverlayFrame =
                    new RiuDraggableOverlayFrame( plotTrack->viewer()->qwtPlot()->canvas(), plotTrack->viewer()->overlayMargins() );
            }
            m_filterOverlayFrame->setContentFrame( m_ensembleCurveSet->curveFilters()->makeFilterDescriptionFrame() );
            plotTrack->viewer()->addOverlayFrame( m_filterOverlayFrame );
        }
        else
        {
            if ( m_filterOverlayFrame )
            {
                plotTrack->viewer()->removeOverlayFrame( m_filterOverlayFrame );
            }
        }
        plotTrack->viewer()->scheduleReplot();
    }
}

//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
RimSummaryCase* RimEnsembleWellLogCurveSet::findMatchingSummaryCase( RimWellLogLasFileCurve* wellLogCurve ) const
{
    RimSummaryCaseCollection*    summaryCaseCollection = m_ensembleCurveSet->summaryCaseCollection();
    std::vector<RimSummaryCase*> sumCases              = summaryCaseCollection->allSummaryCases();
    for ( auto sumCase : sumCases )
    {
        if ( isSameRealization( sumCase, wellLogCurve->wellLogFile() ) )
        {
            return sumCase;
        }
    }

    return nullptr;
}

//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
void RimEnsembleWellLogCurveSet::updateCurveColors()
{
    if ( m_colorMode == ColorMode::COLOR_BY_ENSEMBLE_CURVE_SET )
    {
        if ( m_ensembleCurveSet != nullptr )
        {
            // Fint the curves to color (skip the statistics)
            std::vector<RimWellLogCurve*> curvesToColor;
            std::vector<RimSummaryCase*>  summaryCases;

            for ( auto& curve : m_curves )
            {
                // Statistics curves have separate color settings
                if ( dynamic_cast<RimEnsembleWellLogStatisticsCurve*>( curve.p() ) == nullptr )
                {
                    // Look for a matching summary case
                    RimSummaryCase* summaryCase = findMatchingSummaryCase( dynamic_cast<RimWellLogLasFileCurve*>( curve.p() ) );
                    if ( summaryCase )
                    {
                        summaryCases.push_back( summaryCase );
                        curvesToColor.push_back( curve.p() );
                    }
                }
            }

            // Get the colors
            std::vector<cvf::Color3f> caseColors = m_ensembleCurveSet->generateColorsForCases( summaryCases );

            // Apply the colors
            if ( caseColors.size() != curvesToColor.size() ) return;
            for ( size_t i = 0; i < curvesToColor.size(); i++ )
            {
                curvesToColor[i]->setColor( caseColors[i] );
                curvesToColor[i]->updateCurveAppearance();
            }
        }
    }
    else if ( m_colorMode == ColorMode::SINGLE_COLOR )
    {
        for ( auto& curve : m_curves )
        {
            // Statistics curves have separate color settings
            if ( dynamic_cast<RimEnsembleWellLogStatisticsCurve*>( curve.p() ) == nullptr )
            {
                curve->setColor( m_color );
                curve->updateCurveAppearance();
            }
        }
    }

    auto plotTrack = firstAncestorOrThisOfType<RimWellLogTrack>();
    if ( plotTrack && plotTrack->viewer() )
    {
        if ( m_colorMode != ColorMode::SINGLE_COLOR && m_ensembleCurveSet != nullptr &&
             m_ensembleCurveSet->colorMode() != RimEnsembleCurveSet::ColorMode::SINGLE_COLOR )
        {
            if ( !m_legendOverlayFrame )
            {
                m_legendOverlayFrame =
                    new RiuDraggableOverlayFrame( plotTrack->viewer()->getParentForOverlay(), plotTrack->viewer()->overlayMargins() );
            }
            m_legendOverlayFrame->setContentFrame( m_ensembleCurveSet->legendConfig()->makeLegendFrame() );
            plotTrack->viewer()->addOverlayFrame( m_legendOverlayFrame );
        }
        else
        {
            if ( m_legendOverlayFrame )
            {
                plotTrack->viewer()->removeOverlayFrame( m_legendOverlayFrame );
            }
        }
        plotTrack->viewer()->scheduleReplot();
    }
}

//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
void RimEnsembleWellLogCurveSet::updateEnsembleCurves( const std::vector<RimWellLogLasFile*>& sumCases )
{
    auto plotTrack   = firstAncestorOrThisOfTypeAsserted<RimWellLogTrack>();
    auto wellLogPlot = firstAncestorOrThisOfTypeAsserted<RimWellLogPlot>();

    deleteEnsembleCurves();
    m_qwtPlotCurveForLegendText->detach();
    deleteStatisticsCurves();

    if ( m_statistics->hideEnsembleCurves() ) return;

    std::shared_ptr<RigWellLogIndexDepthOffset> offsets;
    cvf::ref<RigWellPathFormations>             wellPathFormations;

    if ( m_depthEqualization() == RimEnsembleWellLogStatistics::DepthEqualization::K_LAYER )
    {
        offsets            = RimEnsembleWellLogStatistics::calculateIndexDepthOffset( sumCases );
        wellPathFormations = createWellPathFormations( offsets );
    }

    m_qwtPlotCurveForLegendText->attach( plotTrack->viewer()->qwtPlot() );

    QString wellLogChannelName = m_wellLogChannelName();
    if ( plotTrack && wellLogChannelName != "None" )
    {
        if ( isCurvesVisible() )
        {
            bool isFirst = true;

            for ( auto& wellLogFile : sumCases )
            {
                RimWellLogLasFileCurve* curve = new RimWellLogLasFileCurve;
                curve->setUiTreeHidden( true );
                curve->setUiTreeChildrenHidden( true );
                plotTrack->addCurve( curve );

                QString errorMessage;
                if ( wellLogFile->readFile( &errorMessage ) )
                {
                    RigWellLogLasFile* wellLogDataFile = wellLogFile->wellLogFileData();
                    CVF_ASSERT( wellLogDataFile );

                    if ( isFirst )
                    {
                        // Initialize plot with depth unit from the first log file
                        wellLogPlot->setDepthUnit( wellLogDataFile->depthUnit() );
                        isFirst = false;
                    }
                }
                else
                {
                    RiaLogging::warning( errorMessage );
                }

                RimWellPath* wellPath = RimProject::current()->wellPathByName( wellLogFile->wellName() );
                if ( wellPathFormations.notNull() ) wellPath->setFormationsGeometry( wellPathFormations );

                curve->setWellPath( wellPath );
                curve->setWellLogChannelName( wellLogChannelName );
                curve->setWellLogFile( wellLogFile );

                curve->setSymbol( m_curveAppearance->symbol() );
                curve->setSymbolSize( m_curveAppearance->symbolSize() );
                curve->setSymbolSkipDistance( m_curveAppearance->symbolSkipDistance() );
                curve->setSymbolEdgeColor( m_curveAppearance->symbolEdgeColor() );
                curve->setLineStyle( m_curveAppearance->lineStyle() );
                curve->setLineThickness( m_curveAppearance->lineThickness() );

                if ( offsets ) curve->setIndexDepthOffsets( offsets );
                curve->loadDataAndUpdate( true );

                curve->updateCurveVisibility();
                curve->setShowInLegend( false );

                m_curves.push_back( curve );
            }

            updateCurveColors();
            setLogScaleFromSelectedResult( wellLogChannelName );
        }
    }

    plotTrack->updateLegend();
}

//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
void RimEnsembleWellLogCurveSet::setLogScaleFromSelectedResult( const QString resVar )
{
    if ( RiaResultNames::isLogarithmicResult( resVar ) )
    {
        auto track = firstAncestorOrThisOfType<RimWellLogTrack>();
        if ( track ) track->setLogarithmicScale( true );
    }
}

//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
bool RimEnsembleWellLogCurveSet::updateStatistics()
{
    return updateStatistics( std::vector<RimWellLogLasFile*>() );
}

//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
bool RimEnsembleWellLogCurveSet::updateStatistics( const std::vector<RimWellLogLasFile*>& sumCases )
{
    RimEnsembleWellLogs* ensembleWellLogs   = m_ensembleWellLogs;
    QString              wellLogChannelName = m_wellLogChannelName();

    if ( !isCurvesVisible() || m_disableStatisticCurves || !ensembleWellLogs || wellLogChannelName == "None" )
    {
        m_ensembleWellLogStatistics->clearData();
        return false;
    }
    // Calculate
    std::vector<RimWellLogLasFile*> wellLogFiles = sumCases;
    if ( wellLogFiles.empty() )
    {
        if ( m_statistics->basedOnFilteredCases() )
            wellLogFiles = filterEnsembleCases( ensembleWellLogs->wellLogFiles() );
        else
            wellLogFiles = ensembleWellLogs->wellLogFiles();
    }

    m_ensembleWellLogStatistics->calculate( wellLogFiles, wellLogChannelName, m_depthEqualization() );
    return true;
}

//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
void RimEnsembleWellLogCurveSet::updateStatisticsCurves( const std::vector<RimWellLogLasFile*>& sumCases )
{
    deleteStatisticsCurves();

    if ( !updateStatistics( sumCases ) ) return;

    std::vector<RimEnsembleWellLogStatistics::StatisticsType> statisticsTypes;
    if ( m_statistics->isActive() )
    {
        if ( m_statistics->showP10Curve() && m_ensembleWellLogStatistics->hasP10Data() )
            statisticsTypes.push_back( RimEnsembleWellLogStatistics::StatisticsType::P10 );
        if ( m_statistics->showP50Curve() && m_ensembleWellLogStatistics->hasP50Data() )
            statisticsTypes.push_back( RimEnsembleWellLogStatistics::StatisticsType::P50 );
        if ( m_statistics->showP90Curve() && m_ensembleWellLogStatistics->hasP90Data() )
            statisticsTypes.push_back( RimEnsembleWellLogStatistics::StatisticsType::P90 );
        if ( m_statistics->showMeanCurve() && m_ensembleWellLogStatistics->hasMeanData() )
            statisticsTypes.push_back( RimEnsembleWellLogStatistics::StatisticsType::MEAN );
    }

    auto statisticsCurveSymbolFromStatistics = []( RimEnsembleWellLogStatistics::StatisticsType statisticsType )
    {
        if ( statisticsType == RimEnsembleWellLogStatistics::StatisticsType::P10 ) return RiuPlotCurveSymbol::SYMBOL_DOWN_TRIANGLE;
        if ( statisticsType == RimEnsembleWellLogStatistics::StatisticsType::P90 ) return RiuPlotCurveSymbol::SYMBOL_TRIANGLE;
        if ( statisticsType == RimEnsembleWellLogStatistics::StatisticsType::P50 ) return RiuPlotCurveSymbol::SYMBOL_DIAMOND;
        return RiuPlotCurveSymbol::SYMBOL_ELLIPSE;
    };

    auto plotTrack = firstAncestorOrThisOfTypeAsserted<RimWellLogTrack>();

    for ( auto statisticsType : statisticsTypes )
    {
        auto curve = new RimEnsembleWellLogStatisticsCurve();
        curve->setUiTreeHidden( true );
        curve->setUiTreeChildrenHidden( true );
        curve->setEnsembleWellLogCurveSet( this );
        curve->setStatisticsType( statisticsType );

        m_curves.push_back( curve );
        curve->setColor( m_statistics->color() );

        auto symbol = statisticsCurveSymbolFromStatistics( statisticsType );
        curve->setSymbol( symbol );
        curve->setSymbolSize( statisticsCurveSymbolSize( symbol ) );
        curve->setSymbolSkipDistance( 150 );
        if ( m_statistics->showCurveLabels() )
        {
            curve->setSymbolLabel( caf::AppEnum<RimEnsembleWellLogStatistics::StatisticsType>::uiText( statisticsType ) );
        }
        curve->setLineStyle( RiuQwtPlotCurveDefines::LineStyleEnum::STYLE_SOLID );
        curve->setShowInLegend( m_statistics->showStatisticsCurveLegends() );

        plotTrack->addCurve( curve );

        curve->updateCurveVisibility();
        curve->loadDataAndUpdate( false );
    }

    updateCurveColors();
}

//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
void RimEnsembleWellLogCurveSet::updateStatisticsCurves()
{
    updateStatisticsCurves( std::vector<RimWellLogLasFile*>() );
}

//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
void RimEnsembleWellLogCurveSet::showCurves( bool show )
{
    m_showCurves = show;
}

//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
RimEnsembleWellLogStatistics::DepthEqualization RimEnsembleWellLogCurveSet::depthEqualization() const
{
    return m_depthEqualization();
}

//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
void RimEnsembleWellLogCurveSet::setDepthEqualization( RimEnsembleWellLogStatistics::DepthEqualization depthEqualization )
{
    m_depthEqualization = depthEqualization;
}

//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
void RimEnsembleWellLogCurveSet::setFilterByEnsembleCurveSet( RimEnsembleCurveSet* ensembleCurveSet )
{
    m_ensembleCurveSet = ensembleCurveSet;
}

//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
void RimEnsembleWellLogCurveSet::updateAllTextInPlot()
{
    auto plotTrack = firstAncestorOrThisOfTypeAsserted<RimWellLogTrack>();
    plotTrack->viewer()->setPlotTitle( name() );

    updateEnsembleLegendItem();

    plotTrack->updateLegend();
}

//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
std::vector<RimWellLogLasFile*> RimEnsembleWellLogCurveSet::filterEnsembleCases( const std::vector<RimWellLogLasFile*>& wellLogFiles )
{
    std::vector<RimWellLogLasFile*> filteredCases;

    if ( m_ensembleCurveSet != nullptr && m_statistics->basedOnFilteredCases() )
    {
        // Get the summary cases from the related ensemble summary curve set.
        RimSummaryCaseCollection* summaryCaseCollection = m_ensembleCurveSet->summaryCaseCollection();

        //
        std::vector<RimSummaryCase*> sumCases = m_ensembleCurveSet->filterEnsembleCases( summaryCaseCollection->allSummaryCases() );
        for ( auto sumCase : sumCases )
        {
            for ( auto wellLogFile : wellLogFiles )
            {
                if ( isSameRealization( sumCase, wellLogFile ) )
                {
                    filteredCases.push_back( wellLogFile );
                }
            }
        }
    }
    else
    {
        filteredCases = wellLogFiles;
    }

    return filteredCases;
}

//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
bool RimEnsembleWellLogCurveSet::isSameRealization( RimSummaryCase* summaryCase, RimWellLogFile* wellLogFile ) const
{
    QString wellLogFileName = wellLogFile->fileName();

    //
    if ( summaryCase->hasCaseRealizationParameters() )
    {
        // TODO: make less naive..
        int     realizationNumber   = summaryCase->caseRealizationParameters()->realizationNumber();
        QString summaryCaseFileName = summaryCase->summaryHeaderFilename();

        if ( wellLogFileName.contains( QString( "realization-%1" ).arg( realizationNumber ) ) )
        {
            return true;
        }
    }

    return false;
}

//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
void RimEnsembleWellLogCurveSet::disableStatisticCurves()
{
    m_disableStatisticCurves = true;
}

//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
bool RimEnsembleWellLogCurveSet::isFiltered() const
{
    return m_isCurveSetFiltered;
}

//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
bool RimEnsembleWellLogCurveSet::hasP10Data() const
{
    return m_ensembleWellLogStatistics->hasP10Data();
}

//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
bool RimEnsembleWellLogCurveSet::hasP50Data() const
{
    return m_ensembleWellLogStatistics->hasP50Data();
}

//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
bool RimEnsembleWellLogCurveSet::hasP90Data() const
{
    return m_ensembleWellLogStatistics->hasP90Data();
}

//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
bool RimEnsembleWellLogCurveSet::hasMeanData() const
{
    return m_ensembleWellLogStatistics->hasMeanData();
}

//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
const RimEnsembleWellLogStatistics* RimEnsembleWellLogCurveSet::ensembleWellLogStatistics() const
{
    return m_ensembleWellLogStatistics.get();
}

//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
void RimEnsembleWellLogCurveSet::updateEnsembleLegendItem()
{
    m_qwtPlotCurveForLegendText->setTitle( name() );

    {
        QwtSymbol* symbol = nullptr;

        if ( m_colorMode == ColorMode::SINGLE_COLOR )
        {
            symbol = new QwtSymbol( QwtSymbol::HLine );

            QColor curveColor( m_color.value().rByte(), m_color.value().gByte(), m_color.value().bByte() );
            QPen   curvePen( curveColor );
            curvePen.setWidth( 2 );

            symbol->setPen( curvePen );
            symbol->setSize( 6, 6 );
        }
        else if ( m_colorMode == ColorMode::COLOR_BY_ENSEMBLE_CURVE_SET )
        {
            QPixmap p = QPixmap( ":/Legend.png" );

            symbol = new QwtSymbol;
            symbol->setPixmap( p );
            symbol->setSize( 8, 8 );
        }

        m_qwtPlotCurveForLegendText->setSymbol( symbol );
    }

    bool showLegendItem = isCurvesVisible();
    m_qwtPlotCurveForLegendText->setItemAttribute( QwtPlotItem::Legend, showLegendItem );
}

//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
QString RimEnsembleWellLogCurveSet::name() const
{
    QString curveSetName;
    if ( m_isUsingAutoName )
    {
        curveSetName = m_autoGeneratedName();
    }
    else
    {
        curveSetName += m_userDefinedName();
    }

    return curveSetName;
}

//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
QString RimEnsembleWellLogCurveSet::createAutoName() const
{
    QStringList nameParts;

    if ( m_ensembleWellLogs() )
    {
        nameParts.append( m_ensembleWellLogs->name() );
        nameParts.append( m_wellLogChannelName() );
    }

    if ( !nameParts.isEmpty() )
        return nameParts.join( " - " );
    else
        return "Ensemble Well Log Curve Set";
}

//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
void RimEnsembleWellLogCurveSet::connectEnsembleCurveSetFilterSignals()
{
    if ( m_ensembleCurveSet() )
    {
        m_ensembleCurveSet()->filterChanged.connect( this, &RimEnsembleWellLogCurveSet::onFilterSourceChanged );
    }
}

//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
void RimEnsembleWellLogCurveSet::onFilterSourceChanged( const caf::SignalEmitter* emitter )
{
    if ( m_ensembleCurveSet() ) loadDataAndUpdate( true );
}

//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
void RimEnsembleWellLogCurveSet::initAfterRead()
{
    connectEnsembleCurveSetFilterSignals();

    // Hide the ensemble and statistics curves in the project tree on reload
    for ( auto c : m_curves() )
    {
        c->setUiTreeHidden( true );
    }
}

//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
void RimEnsembleWellLogCurveSet::setEnsembleWellLogs( RimEnsembleWellLogs* ensembleWellLogs )
{
    m_ensembleWellLogs = ensembleWellLogs;
}

//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
void RimEnsembleWellLogCurveSet::setWellLogChannelName( const QString& wellLogChannelName )
{
    m_wellLogChannelName = wellLogChannelName;
}

//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
cvf::ref<RigWellPathFormations> RimEnsembleWellLogCurveSet::createWellPathFormations( std::shared_ptr<RigWellLogIndexDepthOffset> offsets )
{
    RimFormationNamesCollection* formationNamesCollection = RimProject::current()->activeOilField()->formationNamesCollection.v();
    if ( !formationNamesCollection ) return nullptr;

    if ( formationNamesCollection->formationNamesList().empty() ) return nullptr;

    RimFormationNames* rimFormationNames = formationNamesCollection->formationNamesList()[0];
    if ( !rimFormationNames ) return nullptr;

    RigFormationNames* formationNames = rimFormationNames->formationNamesData();
    if ( !formationNames ) return nullptr;

    std::vector<RigWellPathFormation> wellPathFormationItems;
    for ( int kLayer : offsets->sortedIndexes() )
    {
        RigWellPathFormation wellPathFormation;
        wellPathFormation.mdTop         = offsets->getTopMd( kLayer );
        wellPathFormation.mdBase        = offsets->getBottomMd( kLayer );
        wellPathFormation.formationName = formationNames->formationNameFromKLayerIdx( kLayer - 1 );
        wellPathFormationItems.push_back( wellPathFormation );
    }

    QString                         unusedFilePath = "";
    cvf::ref<RigWellPathFormations> wellPathFormations =
        new RigWellPathFormations( wellPathFormationItems, unusedFilePath, "Ensemble formation" );
    return wellPathFormations;
}

//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
bool RimEnsembleWellLogCurveSet::hasPropertyInFile( const QString& property ) const
{
    RimEnsembleWellLogs* group = m_ensembleWellLogs;
    if ( !group ) return false;

    std::vector<RimWellLogLasFile*> allWellLogFiles = group->wellLogFiles();
    for ( auto& wellLogFile : allWellLogFiles )
    {
        QString errorMessage;
        if ( !wellLogFile->readFile( &errorMessage ) ) return false;

        RigWellLogLasFile* wellLogDataFile = wellLogFile->wellLogFileData();
        CVF_ASSERT( wellLogDataFile );

        std::vector<double> values = wellLogDataFile->values( RiaResultNames::indexKResultName() );
        if ( values.empty() ) return false;
    }

    return true;
}
