/////////////////////////////////////////////////////////////////////////////////
//
//  Copyright (C) 2017- Statoil 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 "RimEnsembleCurveSet.h"

#include "RiaColorTools.h"
#include "RiaGuiApplication.h"
#include "RiaPreferences.h"
#include "RiaQDateTimeTools.h"
#include "RiaResultNames.h"
#include "RiaSummaryAddressAnalyzer.h"
#include "RiaSummaryCurveDefinition.h"
#include "RiaTimeTTools.h"

#include "RimSummaryCalculationCollection.h"
#include "SummaryPlotCommands/RicSummaryPlotEditorUi.h"

#include "RifEnsembleStatisticsReader.h"

#include "RimCustomObjectiveFunction.h"
#include "RimCustomObjectiveFunctionCollection.h"
#include "RimDerivedEnsembleCaseCollection.h"
#include "RimEnsembleCurveFilter.h"
#include "RimEnsembleCurveFilterCollection.h"
#include "RimEnsembleCurveSetCollection.h"
#include "RimEnsembleCurveSetColorManager.h"
#include "RimEnsembleStatistics.h"
#include "RimEnsembleStatisticsCase.h"
#include "RimObjectiveFunction.h"
#include "RimObjectiveFunctionTools.h"
#include "RimPlotAxisPropertiesInterface.h"
#include "RimProject.h"
#include "RimRegularLegendConfig.h"
#include "RimSummaryAddress.h"
#include "RimSummaryCalculationCollection.h"
#include "RimSummaryCase.h"
#include "RimSummaryCaseCollection.h"
#include "RimSummaryCurve.h"
#include "RimSummaryCurveAutoName.h"
#include "RimSummaryPlot.h"
#include "RimTimeStepFilter.h"

#include "RiuAbstractLegendFrame.h"
#include "RiuDraggableOverlayFrame.h"
#include "RiuGuiTheme.h"
#include "RiuPlotCurve.h"
#include "RiuPlotCurveSymbol.h"
#include "RiuPlotMainWindow.h"
#include "RiuSummaryVectorSelectionDialog.h"
#include "RiuTextContentFrame.h"

#include "cafPdmObject.h"
#include "cafPdmUiColorEditor.h"
#include "cafPdmUiDateEditor.h"
#include "cafPdmUiDoubleSliderEditor.h"
#include "cafPdmUiItem.h"
#include "cafPdmUiLineEditor.h"
#include "cafPdmUiListEditor.h"
#include "cafPdmUiPushButtonEditor.h"
#include "cafPdmUiSliderEditor.h"
#include "cafPdmUiTreeAttributes.h"
#include "cafPdmUiTreeOrdering.h"
#include "cafPdmUiTreeSelectionEditor.h"
#include "cafTitledOverlayFrame.h"

#include <algorithm>
#include <utility>

//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
namespace caf
{
template <>
void AppEnum<RimEnsembleCurveSet::ParameterSorting>::setUp()
{
    addItem( RimEnsembleCurveSet::ParameterSorting::ABSOLUTE_VALUE, "ABSOLUTE_VALUE", "Absolute Correlation" );
    addItem( RimEnsembleCurveSet::ParameterSorting::ALPHABETICALLY, "ALPHABETICALLY", "Alphabetically" );
    setDefault( RimEnsembleCurveSet::ParameterSorting::ABSOLUTE_VALUE );
}
} // namespace caf

//--------------------------------------------------------------------------------------------------
/// Internal functions
//--------------------------------------------------------------------------------------------------
RiuPlotCurveSymbol::PointSymbolEnum statisticsCurveSymbolFromAddress( const RifEclipseSummaryAddress& address );
int                                 statisticsCurveSymbolSize( RiuPlotCurveSymbol::PointSymbolEnum symbol );

CAF_PDM_SOURCE_INIT( RimEnsembleCurveSet, "RimEnsembleCurveSet" );

//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
RimEnsembleCurveSet::RimEnsembleCurveSet()
    : filterChanged( this )

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

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

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

    // Y Values
    CAF_PDM_InitFieldNoDefault( &m_yValuesSummaryCaseCollection, "SummaryGroup", "Ensemble" );
    m_yValuesSummaryCaseCollection.uiCapability()->setUiTreeChildrenHidden( true );
    m_yValuesSummaryCaseCollection.uiCapability()->setAutoAddingOptionFromValue( false );

    CAF_PDM_InitFieldNoDefault( &m_yValuesSummaryAddressUiField, "SelectedVariableDisplayVar", "Vector" );
    m_yValuesSummaryAddressUiField.xmlCapability()->disableIO();
    m_yValuesSummaryAddressUiField.uiCapability()->setUiEditorTypeName( caf::PdmUiLineEditor::uiEditorTypeName() );

    CAF_PDM_InitFieldNoDefault( &m_yValuesSummaryAddress, "SummaryAddress", "Summary Address" );
    m_yValuesSummaryAddress.uiCapability()->setUiTreeHidden( true );
    m_yValuesSummaryAddress.uiCapability()->setUiTreeChildrenHidden( true );
    m_yValuesSummaryAddress = new RimSummaryAddress;

    CAF_PDM_InitFieldNoDefault( &m_yPushButtonSelectSummaryAddress, "SelectAddress", "" );
    caf::PdmUiPushButtonEditor::configureEditorForField( &m_yPushButtonSelectSummaryAddress );
    m_yPushButtonSelectSummaryAddress.uiCapability()->setUiLabelPosition( caf::PdmUiItemInfo::HIDDEN );
    m_yPushButtonSelectSummaryAddress = false;

    CAF_PDM_InitFieldNoDefault( &m_resampling, "Resampling", "Resampling" );

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

    CAF_PDM_InitField( &m_colorForRealizations, "Color", RiaColorTools::textColor3f(), "Color" );
    CAF_PDM_InitField( &m_mainEnsembleColor, "MainEnsembleColor", RiaColorTools::textColor3f(), "Color" );
    CAF_PDM_InitField( &m_colorTransparency, "ColorTransparency", 0.3, "Transparency" );
    m_colorTransparency.uiCapability()->setUiEditorTypeName( caf::PdmUiDoubleSliderEditor::uiEditorTypeName() );

    CAF_PDM_InitField( &m_ensembleParameter, "EnsembleParameter", QString( "" ), "Parameter" );
    m_ensembleParameter.uiCapability()->setUiEditorTypeName( caf::PdmUiTreeSelectionEditor::uiEditorTypeName() );

    CAF_PDM_InitFieldNoDefault( &m_ensembleParameterSorting, "EnsembleParameterSorting", "Parameter Sorting" );

    CAF_PDM_InitFieldNoDefault( &m_objectiveValuesSummaryAddressesUiField, "SelectedObjectiveSummaryVar", "Vector" );
    m_objectiveValuesSummaryAddressesUiField.xmlCapability()->disableIO();
    m_objectiveValuesSummaryAddressesUiField.uiCapability()->setUiEditorTypeName( caf::PdmUiLineEditor::uiEditorTypeName() );

    CAF_PDM_InitFieldNoDefault( &m_objectiveValuesSummaryAddresses, "ObjectiveSummaryAddress", "Summary Address" );
    m_objectiveValuesSummaryAddresses.uiCapability()->setUiTreeHidden( true );
    m_objectiveValuesSummaryAddresses.uiCapability()->setUiTreeChildrenHidden( true );

    CAF_PDM_InitFieldNoDefault( &m_objectiveValuesSelectSummaryAddressPushButton, "SelectObjectiveSummaryAddress", "" );
    caf::PdmUiPushButtonEditor::configureEditorForField( &m_objectiveValuesSelectSummaryAddressPushButton );
    m_objectiveValuesSelectSummaryAddressPushButton.uiCapability()->setUiLabelPosition( caf::PdmUiItemInfo::HIDDEN );
    m_objectiveValuesSelectSummaryAddressPushButton = false;

    CAF_PDM_InitFieldNoDefault( &m_customObjectiveFunction, "CustomObjectiveFunction", "Objective Function" );
    m_customObjectiveFunction.uiCapability()->setUiEditorTypeName( caf::PdmUiListEditor::uiEditorTypeName() );

    CAF_PDM_InitField( &m_showObjectiveFunctionFormula, "ShowObjectiveFunctionFormula", true, "Show Text Box in Plot" );

    CAF_PDM_InitFieldNoDefault( &m_minDateRange, "MinDateRange", "From" );
    m_minDateRange.uiCapability()->setUiEditorTypeName( caf::PdmUiDateEditor::uiEditorTypeName() );

    CAF_PDM_InitFieldNoDefault( &m_minTimeStep, "MinTimeStep", "" );
    m_minTimeStep.uiCapability()->setUiEditorTypeName( caf::PdmUiSliderEditor::uiEditorTypeName() );

    CAF_PDM_InitFieldNoDefault( &m_maxDateRange, "MaxDateRange", "To" );
    m_maxDateRange.uiCapability()->setUiEditorTypeName( caf::PdmUiDateEditor::uiEditorTypeName() );

    CAF_PDM_InitFieldNoDefault( &m_maxTimeStep, "MaxTimeStep", "" );
    m_maxTimeStep.uiCapability()->setUiEditorTypeName( caf::PdmUiSliderEditor::uiEditorTypeName() );

    // Time Step Selection
    CAF_PDM_InitFieldNoDefault( &m_timeStepFilter, "TimeStepFilter", "Available Time Steps" );
    CAF_PDM_InitFieldNoDefault( &m_selectedTimeSteps, "TimeSteps", "Select Time Steps" );
    m_selectedTimeSteps.uiCapability()->setUiEditorTypeName( caf::PdmUiTreeSelectionEditor::uiEditorTypeName() );
    m_selectedTimeSteps.uiCapability()->setUiLabelPosition( caf::PdmUiItemInfo::TOP );

    CAF_PDM_InitFieldNoDefault( &m_plotAxis_OBSOLETE, "PlotAxis", "Axis" );
    m_plotAxis_OBSOLETE.xmlCapability()->setIOWritable( false );

    CAF_PDM_InitFieldNoDefault( &m_plotAxisProperties, "Axis", "Axis" );

    CAF_PDM_InitFieldNoDefault( &m_legendConfig, "LegendConfig", "" );
    m_legendConfig = new RimRegularLegendConfig();
    m_legendConfig->setColorLegend( RimRegularLegendConfig::mapToColorLegend( RimEnsembleCurveSetColorManager::DEFAULT_ENSEMBLE_COLOR_RANGE ) );

    CAF_PDM_InitFieldNoDefault( &m_curveFilters, "CurveFilters", "Curve Filters" );
    m_curveFilters = new RimEnsembleCurveFilterCollection();
    m_curveFilters->setUiTreeHidden( true );
    m_curveFilters->uiCapability()->setUiTreeHidden( true );

    CAF_PDM_InitFieldNoDefault( &m_customObjectiveFunctions, "CustomObjectiveFunctions", "Custom Objective Functions" );
    m_customObjectiveFunctions = new RimCustomObjectiveFunctionCollection();
    m_customObjectiveFunctions->objectiveFunctionChanged.connect( this, &RimEnsembleCurveSet::onCustomObjectiveFunctionChanged );

    CAF_PDM_InitFieldNoDefault( &m_objectiveFunction, "ObjectiveFunction", "Objective Function" );
    m_objectiveFunction = new RimObjectiveFunction();
    m_objectiveFunction.uiCapability()->setUiTreeHidden( true );
    m_objectiveFunction->changed.connect( this, &RimEnsembleCurveSet::onObjectiveFunctionChanged );

    CAF_PDM_InitFieldNoDefault( &m_statistics, "Statistics", "Statistics" );
    m_statistics = new RimEnsembleStatistics( this );
    m_statistics.uiCapability()->setUiTreeHidden( true );

    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, &RimEnsembleCurveSet::createAutoName );
    m_autoGeneratedName.uiCapability()->setUiReadOnly( true );
    m_autoGeneratedName.xmlCapability()->disableIO();

    CAF_PDM_InitField( &m_isUsingAutoName, "AutoName", true, "Auto Name" );
    CAF_PDM_InitFieldNoDefault( &m_summaryAddressNameTools, "SummaryAddressNameTools", "SummaryAddressNameTools" );
    m_summaryAddressNameTools.uiCapability()->setUiTreeHidden( true );
    m_summaryAddressNameTools.uiCapability()->setUiTreeChildrenHidden( true );

    m_summaryAddressNameTools = new RimSummaryCurveAutoName;

    m_ensembleStatCase.reset( new RimEnsembleStatisticsCase( this ) );
    m_ensembleStatCase->createSummaryReaderInterface();
    m_ensembleStatCase->createRftReaderInterface();

    m_disableStatisticCurves = false;
    m_isCurveSetFiltered     = false;

    setDeletable( true );
}

//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
RimEnsembleCurveSet::~RimEnsembleCurveSet()
{
    m_curves.deleteChildren();

    RimSummaryPlot* parentPlot;
    firstAncestorOrThisOfType( parentPlot );
    if ( parentPlot && parentPlot->plotWidget() )
    {
        if ( m_plotCurveForLegendText ) m_plotCurveForLegendText->detach();
        if ( m_legendOverlayFrame )
        {
            parentPlot->plotWidget()->removeOverlayFrame( m_legendOverlayFrame );
        }
    }
    if ( m_legendOverlayFrame )
    {
        m_legendOverlayFrame->setParent( nullptr );
        delete m_legendOverlayFrame;
    }
    if ( m_filterOverlayFrame )
    {
        m_filterOverlayFrame->setParent( nullptr );
        delete m_filterOverlayFrame;
    }
    if ( m_objectiveFunctionOverlayFrame )
    {
        m_objectiveFunctionOverlayFrame->setParent( nullptr );
        delete m_objectiveFunctionOverlayFrame;
    }
}

//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
bool RimEnsembleCurveSet::isCurvesVisible() const
{
    RimEnsembleCurveSetCollection* coll = nullptr;
    firstAncestorOrThisOfType( coll );
    return m_showCurves() && ( coll ? coll->isCurveSetsVisible() : true );
}

//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
void RimEnsembleCurveSet::setColor( cvf::Color3f color )
{
    m_mainEnsembleColor = color;

    setTransparentCurveColor();
}

//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
void RimEnsembleCurveSet::setStatisticsColor( const cvf::Color3f& color )
{
    m_statistics->setColor( color );
}

//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
void RimEnsembleCurveSet::enableStatisticsLables( bool enable )
{
    m_statistics->enableCurveLabels( enable );
}

//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
void RimEnsembleCurveSet::loadDataAndUpdate( bool updateParentPlot )
{
    m_yValuesSummaryAddressUiField = m_yValuesSummaryAddress->address();

    m_curveFilters->loadDataAndUpdate();

    updateAddressesUiField();

    updateAllCurves();
    updateFilterLegend();
    updateObjectiveFunctionLegend();
    updateTimeAnnotations();

    if ( updateParentPlot )
    {
        RimSummaryPlot* parentPlot;
        firstAncestorOrThisOfTypeAsserted( parentPlot );
        parentPlot->updateAll();
    }
}

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

//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
void RimEnsembleCurveSet::deletePlotCurves()
{
    for ( RimSummaryCurve* curve : m_curves )
    {
        curve->deletePlotCurve();
    }

    if ( m_plotCurveForLegendText )
    {
        m_plotCurveForLegendText->detach();
        m_plotCurveForLegendText = nullptr;
    }
}

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

//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
void RimEnsembleCurveSet::addCurve( RimSummaryCurve* curve )
{
    if ( curve )
    {
        RimSummaryPlot* plot;
        firstAncestorOrThisOfType( plot );
        if ( plot && plot->plotWidget() ) curve->setParentPlotNoReplot( plot->plotWidget() );

        curve->setColor( m_colorForRealizations );
        m_curves.push_back( curve );
    }
}

//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
void RimEnsembleCurveSet::deleteCurve( RimSummaryCurve* curve )
{
    if ( curve )
    {
        m_curves.removeChild( curve );
        delete curve;
    }
}

//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
void RimEnsembleCurveSet::setSummaryAddress( RifEclipseSummaryAddress address )
{
    m_yValuesSummaryAddress->setAddress( address );
    RimSummaryAddress* summaryAddress = new RimSummaryAddress();
    summaryAddress->setAddress( address );
    m_objectiveValuesSummaryAddresses.push_back( summaryAddress );
}

//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
void RimEnsembleCurveSet::setSummaryAddressAndStatisticsFlag( RifEclipseSummaryAddress address )
{
    setSummaryAddress( address );
    m_statistics->setShowStatisticsCurves( !address.isHistoryVector() );
    m_statistics->updateAllRequiredEditors();
}

//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
RifEclipseSummaryAddress RimEnsembleCurveSet::summaryAddress() const
{
    return m_yValuesSummaryAddress->address();
}

//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
std::vector<RimSummaryCurve*> RimEnsembleCurveSet::curves() const
{
    return m_curves.children();
}

//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
RimCustomObjectiveFunctionCollection* RimEnsembleCurveSet::customObjectiveFunctionCollection()
{
    return m_customObjectiveFunctions;
}

//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
void RimEnsembleCurveSet::deleteEnsembleCurves()
{
    std::vector<size_t> curvesIndexesToDelete;
    for ( size_t c = 0; c < m_curves.size(); c++ )
    {
        RimSummaryCurve* curve = m_curves[c];
        if ( curve->summaryAddressY().category() != RifEclipseSummaryAddress::SUMMARY_ENSEMBLE_STATISTICS )
            curvesIndexesToDelete.push_back( c );
    }

    while ( curvesIndexesToDelete.size() > 0 )
    {
        size_t currIndex = curvesIndexesToDelete.back();
        delete m_curves[currIndex];
        m_curves.erase( currIndex );
        curvesIndexesToDelete.pop_back();
    }
}

//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
void RimEnsembleCurveSet::deleteStatisticsCurves()
{
    std::vector<size_t> curvesIndexesToDelete;
    for ( size_t c = 0; c < m_curves.size(); c++ )
    {
        RimSummaryCurve* curve = m_curves[c];
        if ( curve->summaryAddressY().category() == RifEclipseSummaryAddress::SUMMARY_ENSEMBLE_STATISTICS )
            curvesIndexesToDelete.push_back( c );
    }

    while ( curvesIndexesToDelete.size() > 0 )
    {
        size_t currIndex = curvesIndexesToDelete.back();
        delete m_curves[currIndex];
        m_curves.erase( currIndex );
        curvesIndexesToDelete.pop_back();
    }
}

//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
RimRegularLegendConfig* RimEnsembleCurveSet::legendConfig()
{
    return m_legendConfig;
}

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

//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
void RimEnsembleCurveSet::onLegendDefinitionChanged()
{
    updateCurveColors();
}

//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
void RimEnsembleCurveSet::setSummaryCaseCollection( RimSummaryCaseCollection* sumCaseCollection )
{
    m_yValuesSummaryCaseCollection = sumCaseCollection;
}

//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
RimSummaryCaseCollection* RimEnsembleCurveSet::summaryCaseCollection() const
{
    return m_yValuesSummaryCaseCollection();
}

//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
RimEnsembleCurveFilterCollection* RimEnsembleCurveSet::filterCollection() const
{
    return m_curveFilters;
}

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

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

//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
void RimEnsembleCurveSet::setEnsembleParameter( const QString& parameterName )
{
    m_ensembleParameter = parameterName;
}

//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
RigEnsembleParameter::Type RimEnsembleCurveSet::currentEnsembleParameterType() const
{
    if ( m_colorMode() == ColorMode::BY_ENSEMBLE_PARAM )
    {
        RimSummaryCaseCollection* group         = m_yValuesSummaryCaseCollection();
        QString                   parameterName = m_ensembleParameter();

        if ( group && !parameterName.isEmpty() )
        {
            auto eParam = group->ensembleParameter( parameterName );
            return eParam.type;
        }
    }
    return RigEnsembleParameter::TYPE_NONE;
}

//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
void RimEnsembleCurveSet::updateAllCurves()
{
    RimSummaryCaseCollection* group = m_yValuesSummaryCaseCollection();
    RimSummaryAddress*        addr  = m_yValuesSummaryAddress();

    if ( group && addr->address().category() != RifEclipseSummaryAddress::SUMMARY_INVALID )
    {
        std::vector<RimSummaryCase*> allCases      = group->allSummaryCases();
        std::vector<RimSummaryCase*> filteredCases = filterEnsembleCases( allCases );

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

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

    filterChanged.send();
}

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

//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
void RimEnsembleCurveSet::setTimeSteps( const std::vector<size_t>& timeStepIndices )
{
    m_selectedTimeSteps.v().clear();
    std::set<time_t> timeSteps = allAvailableTimeSteps();
    size_t           index     = 0;
    for ( auto time : timeSteps )
    {
        if ( std::find( timeStepIndices.begin(), timeStepIndices.end(), index++ ) != timeStepIndices.end() )
        {
            m_selectedTimeSteps.v().push_back( RiaQDateTimeTools::fromTime_t( time ) );
        }
    }
}

//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
std::vector<time_t> RimEnsembleCurveSet::selectedTimeSteps() const
{
    std::vector<time_t> selectedTimeTTimeSteps;
    for ( const QDateTime& dateTime : m_selectedTimeSteps.v() )
    {
        selectedTimeTTimeSteps.push_back( RiaTimeTTools::fromQDateTime( dateTime ) );
    }

    return selectedTimeTTimeSteps;
}

//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
void RimEnsembleCurveSet::fieldChangedByUi( const caf::PdmFieldHandle* changedField, const QVariant& oldValue, const QVariant& newValue )
{
    RimSummaryPlot* plot = nullptr;
    firstAncestorOrThisOfType( plot );
    CVF_ASSERT( plot );

    bool updateTextInPlot = false;

    if ( changedField == &m_showCurves )
    {
        updateConnectedEditors();

        // When multiple ensemble curve sets are toggled on/off, it is required to do a full RimSummaryPlot::loadDataAndUpdate() that will
        // call RimEnsembleCurveSet::updateCurves() on all ensembles in the plot. This can be a heavy operation, but will happen only once.
        // https://github.com/OPM/ResInsight/issues/9956
        plot->loadDataAndUpdate();

        updateTextInPlot = true;
    }
    else if ( changedField == &m_yValuesSummaryAddressUiField )
    {
        m_yValuesSummaryAddress->setAddress( m_yValuesSummaryAddressUiField() );

        updateAllCurves();

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

        updateTextInPlot = true;
    }
    else if ( changedField == &m_resampling )
    {
        updateAllCurves();
    }
    else if ( changedField == &m_colorForRealizations || changedField == &m_mainEnsembleColor || changedField == &m_colorTransparency )
    {
        setTransparentCurveColor();

        updateCurveColors();

        updateConnectedEditors();

        updateTextInPlot = true;
    }
    else if ( changedField == &m_ensembleParameter )
    {
        updateLegendMappingMode();
        updateCurveColors();
    }
    else if ( changedField == &m_objectiveValuesSummaryAddressesUiField )
    {
        updateAddressesUiField();

        std::vector<size_t> indices;
        indices.push_back( 0 );
        setTimeSteps( indices );

        updateMaxMinAndDefaultValues();
        updateCurveColors();
        updateObjectiveFunctionLegend();

        updateTextInPlot = true;
    }
    else if ( changedField == &m_showObjectiveFunctionFormula )
    {
        updateObjectiveFunctionLegend();
    }
    else if ( changedField == &m_colorMode )
    {
        m_ensembleParameter.uiCapability()->setUiHidden( m_colorMode() != ColorMode::BY_ENSEMBLE_PARAM );

        if ( m_colorMode() == ColorMode::BY_ENSEMBLE_PARAM )
        {
            if ( m_ensembleParameter().isEmpty() )
            {
                auto params         = variationSortedEnsembleParameters();
                m_ensembleParameter = !params.empty() ? params.front().name : "";
            }
        }

        if ( m_colorMode() == ColorMode::BY_OBJECTIVE_FUNCTION || m_colorMode == ColorMode::BY_CUSTOM_OBJECTIVE_FUNCTION )
        {
            if ( m_objectiveValuesSummaryAddresses.size() == 0 )
            {
                RimSummaryAddress* summaryAddress = new RimSummaryAddress();
                summaryAddress->setAddress( m_yValuesSummaryAddress->address() );
                m_objectiveValuesSummaryAddresses.push_back( summaryAddress );
                updateAddressesUiField();
                m_minTimeStep = *allAvailableTimeSteps().begin();
                m_maxTimeStep = *allAvailableTimeSteps().rbegin();
                updateMaxMinAndDefaultValues();
            }
        }

        setTransparentCurveColor();
        updateCurveColors();
        updateTimeAnnotations();
        updateObjectiveFunctionLegend();

        updateTextInPlot = true;
    }
    else if ( changedField == &m_timeStepFilter )
    {
        m_selectedTimeSteps.v().clear();

        this->updateConnectedEditors();
    }
    else if ( changedField == &m_selectedTimeSteps )
    {
        updateCurveColors();
        updateTimeAnnotations();
        updateObjectiveFunctionLegend();
        updateMaxMinAndDefaultValues();
    }
    else if ( changedField == &m_minTimeStep || changedField == &m_maxTimeStep )
    {
        updateMaxMinAndDefaultValues();
        updateCurveColors();
        updateTimeAnnotations();
    }
    else if ( changedField == &m_minDateRange || changedField == &m_maxDateRange )
    {
        m_minTimeStep = RiaTimeTTools::fromQDateTime( RiaQDateTimeTools::createDateTime( m_minDateRange() ) );
        m_maxTimeStep = RiaTimeTTools::fromQDateTime( RiaQDateTimeTools::createDateTime( m_maxDateRange() ) );
        updateCurveColors();
        updateTimeAnnotations();
    }
    else if ( changedField == &m_plotAxisProperties )
    {
        for ( RimSummaryCurve* curve : curves() )
        {
            curve->setLeftOrRightAxisY( axisY() );
        }

        updatePlotAxis();
        plot->updateAxes();
        plot->updateAll();

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

        updateTextInPlot = true;
    }
    else if ( changedField == &m_userDefinedName )
    {
        updateTextInPlot = true;
    }
    else if ( changedField == &m_yPushButtonSelectSummaryAddress )
    {
        RiuSummaryVectorSelectionDialog dlg( nullptr );
        RimSummaryCaseCollection*       candidateEnsemble = m_yValuesSummaryCaseCollection();
        RifEclipseSummaryAddress        candicateAddress  = m_yValuesSummaryAddress->address();

        dlg.hideSummaryCases();
        dlg.setEnsembleAndAddress( candidateEnsemble, candicateAddress );

        if ( dlg.exec() == QDialog::Accepted )
        {
            auto curveSelection = dlg.curveSelection();
            if ( !curveSelection.empty() )
            {
                m_yValuesSummaryCaseCollection = curveSelection[0].ensemble();
                m_yValuesSummaryAddress->setAddress( curveSelection[0].summaryAddress() );

                this->loadDataAndUpdate( true );

                plot->updateAxes();
                plot->updatePlotTitle();
                plot->updateConnectedEditors();

                RiuPlotMainWindow* mainPlotWindow = RiaGuiApplication::instance()->mainPlotWindow();
                mainPlotWindow->updateMultiPlotToolBar();
            }
        }

        m_yPushButtonSelectSummaryAddress = false;
    }
    else if ( changedField == &m_objectiveValuesSelectSummaryAddressPushButton )
    {
        RiuSummaryVectorSelectionDialog dlg( nullptr );
        RimObjectiveFunctionTools::configureDialogForObjectiveFunctions( &dlg );
        RimSummaryCaseCollection* candidateEnsemble = m_yValuesSummaryCaseCollection();

        std::vector<RifEclipseSummaryAddress> candidateAddresses;
        for ( auto address : m_objectiveValuesSummaryAddresses().children() )
        {
            candidateAddresses.push_back( address->address() );
        }

        dlg.setEnsembleAndAddresses( candidateEnsemble, candidateAddresses );

        if ( dlg.exec() == QDialog::Accepted )
        {
            auto curveSelection = dlg.curveSelection();
            if ( !curveSelection.empty() )
            {
                m_objectiveValuesSummaryAddresses.deleteChildren();
                for ( auto address : curveSelection )
                {
                    RimSummaryAddress* summaryAddress = new RimSummaryAddress();
                    summaryAddress->setAddress( address.summaryAddress() );
                    m_objectiveValuesSummaryAddresses.push_back( summaryAddress );
                }
                this->loadDataAndUpdate( true );
            }
        }

        m_objectiveValuesSelectSummaryAddressPushButton = false;
    }
    else if ( changedField == &m_customObjectiveFunction )
    {
        if ( m_customObjectiveFunction() )
        {
            if ( m_customObjectiveFunction()->weightContainsFunctionType( RimObjectiveFunction::FunctionType::F2 ) )
            {
                std::vector<size_t> indices;
                indices.push_back( 0 );
                setTimeSteps( indices );
            }

            m_minTimeStep = *allAvailableTimeSteps().begin();
            m_maxTimeStep = *allAvailableTimeSteps().rbegin();

            updateLegendMappingMode();
            updateCurveColors();
            updateTimeAnnotations();
            updateObjectiveFunctionLegend();
        }
    }

    if ( updateTextInPlot )
    {
        updateAllTextInPlot();
    }
}

//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
void RimEnsembleCurveSet::defineObjectEditorAttribute( QString uiConfigName, caf::PdmUiEditorAttribute* attribute )
{
    if ( auto* treeItemAttribute = dynamic_cast<caf::PdmUiTreeViewItemAttribute*>( attribute ) )
    {
        treeItemAttribute->tags.clear();
        auto tag     = caf::PdmUiTreeViewItemAttribute::Tag::create();
        tag->bgColor = RiaColorTools::toQColor( m_colorForRealizations );
        tag->fgColor = RiaColorTools::toQColor( m_statistics->color() );
        tag->text    = "---";

        tag->clicked.connect( this, &RimEnsembleCurveSet::onColorTagClicked );

        treeItemAttribute->tags.push_back( std::move( tag ) );
    }
}

//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
void RimEnsembleCurveSet::defineUiOrdering( QString uiConfigName, caf::PdmUiOrdering& uiOrdering )
{
    {
        caf::PdmUiGroup* curveDataGroup = uiOrdering.addNewGroup( "Summary Vector Y" );
        curveDataGroup->add( &m_yValuesSummaryCaseCollection );
        curveDataGroup->add( &m_yValuesSummaryAddressUiField );
        curveDataGroup->add( &m_yPushButtonSelectSummaryAddress, { false, 1, 0 } );
        curveDataGroup->add( &m_resampling );
        curveDataGroup->add( &m_plotAxisProperties );
    }

    appendColorGroup( uiOrdering );

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

    caf::PdmUiGroup* statGroup           = uiOrdering.addNewGroup( "Statistics" );
    bool             showStatisticsColor = m_colorMode() == RimEnsembleCurveSetColorManager::ColorMode::SINGLE_COLOR;
    m_statistics->showColorField( showStatisticsColor );

    m_statistics->defineUiOrdering( uiConfigName, *statGroup );

    uiOrdering.skipRemainingFields( true );
}

//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
void RimEnsembleCurveSet::updateMaxMinAndDefaultValues()
{
    m_minDateRange = QDateTime::fromSecsSinceEpoch( m_minTimeStep ).date();
    m_maxDateRange = QDateTime::fromSecsSinceEpoch( m_maxTimeStep ).date();

    for ( auto filter : m_curveFilters->filters() )
    {
        filter->updateMaxMinAndDefaultValuesFromParent();
    }

    for ( auto objFunc : m_customObjectiveFunctions->objectiveFunctions() )
    {
        objFunc->clearCache();
    }
}

//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
const RimEnsembleCurveFilterCollection* RimEnsembleCurveSet::curveFilters() const
{
    return m_curveFilters;
}

//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
void RimEnsembleCurveSet::onCustomObjectiveFunctionChanged( const caf::SignalEmitter* emitter )
{
    updateCurveColors();
    updateFilterLegend();
    updateObjectiveFunctionLegend();
}

//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
void RimEnsembleCurveSet::setTransparentCurveColor()
{
    if ( m_colorMode() == RimEnsembleCurveSet::ColorMode::SINGLE_COLOR_WITH_ALPHA )
    {
        auto backgroundColor = RiuGuiTheme::getColorByVariableName( "backgroundColor1" );

        auto sourceColor      = RiaColorTools::toQColor( m_mainEnsembleColor );
        auto sourceWeight     = 100;
        int  backgroundWeight = std::max( 1, static_cast<int>( sourceWeight * 10 * m_colorTransparency ) );
        auto blendedColor     = RiaColorTools::blendQColors( backgroundColor, sourceColor, backgroundWeight, sourceWeight );

        m_colorForRealizations = RiaColorTools::fromQColorTo3f( blendedColor );
        setStatisticsColor( m_mainEnsembleColor );
        updateStatisticsCurves();
    }
}

//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
QColor RimEnsembleCurveSet::mainEnsembleColor() const
{
    if ( m_colorMode() == RimEnsembleCurveSetColorManager::ColorMode::SINGLE_COLOR_WITH_ALPHA )
    {
        return RiaColorTools::toQColor( m_mainEnsembleColor );
    }

    return RiaColorTools::toQColor( m_colorForRealizations );
}

//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
void RimEnsembleCurveSet::onColorTagClicked( const SignalEmitter* emitter, size_t index )
{
    caf::PdmField<cvf::Color3f>* colorToModify = nullptr;
    if ( m_colorMode() == RimEnsembleCurveSetColorManager::ColorMode::SINGLE_COLOR )
    {
        colorToModify = &m_colorForRealizations;
    }
    else
    {
        colorToModify = &m_mainEnsembleColor;
    }

    if ( colorToModify )
    {
        QColor sourceColor = RiaColorTools::toQColor( *colorToModify );
        QColor newColor    = caf::PdmUiColorEditor::getColor( sourceColor );

        if ( newColor.isValid() && newColor != sourceColor )
        {
            auto myColor = RiaColorTools::fromQColorTo3f( newColor );

            colorToModify->setValueWithFieldChanged( myColor );
        }
    }
}

//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
void RimEnsembleCurveSet::onObjectiveFunctionChanged( const caf::SignalEmitter* emitter )
{
    updateCurveColors();
    updateFilterLegend();
    updateObjectiveFunctionLegend();
    updateTimeAnnotations();
}

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

    if ( m_colorMode() == RimEnsembleCurveSetColorManager::ColorMode::SINGLE_COLOR )
    {
        colorsGroup->add( &m_colorForRealizations );
    }
    else if ( m_colorMode() == RimEnsembleCurveSetColorManager::ColorMode::SINGLE_COLOR_WITH_ALPHA )
    {
        colorsGroup->add( &m_mainEnsembleColor );
        colorsGroup->add( &m_colorTransparency );
    }
    else if ( m_colorMode == ColorMode::BY_ENSEMBLE_PARAM )
    {
        m_ensembleParameter.uiCapability()->setUiReadOnly( !m_yValuesSummaryCaseCollection() );
        colorsGroup->add( &m_ensembleParameterSorting );
        colorsGroup->add( &m_ensembleParameter );
    }
    else if ( m_colorMode == ColorMode::BY_OBJECTIVE_FUNCTION || m_colorMode == ColorMode::BY_CUSTOM_OBJECTIVE_FUNCTION )
    {
        if ( m_colorMode == ColorMode::BY_OBJECTIVE_FUNCTION )
        {
            colorsGroup->add( &m_objectiveValuesSummaryAddressesUiField );
            colorsGroup->add( &m_objectiveValuesSelectSummaryAddressPushButton, { false, 1, 0 } );

            {
                auto equationGroup = colorsGroup->addNewGroup( "Equation" );
                m_objectiveFunction->uiOrdering( "", *equationGroup );
                equationGroup->add( &m_showObjectiveFunctionFormula );
            }
        }
        else
        {
            colorsGroup->add( &m_customObjectiveFunction );
            colorsGroup->add( &m_showObjectiveFunctionFormula );
        }

        {
            auto timeSelectionGroup = colorsGroup->addNewGroup( "Time Selection" );

            if ( ( m_colorMode == ColorMode::BY_OBJECTIVE_FUNCTION &&
                   m_objectiveFunction()->functionType() == RimObjectiveFunction::FunctionType::F1 ) ||
                 ( m_colorMode == ColorMode::BY_CUSTOM_OBJECTIVE_FUNCTION && m_customObjectiveFunction() &&
                   m_customObjectiveFunction()->weightContainsFunctionType( RimObjectiveFunction::FunctionType::F1 ) ) )
            {
                timeSelectionGroup->add( &m_minDateRange );
                timeSelectionGroup->add( &m_minTimeStep );
                timeSelectionGroup->add( &m_maxDateRange );
                timeSelectionGroup->add( &m_maxTimeStep );
            }
            if ( m_objectiveFunction()->functionType() == RimObjectiveFunction::FunctionType::F2 ||
                 ( m_customObjectiveFunction() &&
                   m_customObjectiveFunction()->weightContainsFunctionType( RimObjectiveFunction::FunctionType::F2 ) ) )
            {
                timeSelectionGroup->add( &m_timeStepFilter );
                timeSelectionGroup->add( &m_selectedTimeSteps );
            }
        }
    }

    uiOrdering.skipRemainingFields();
}

//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
void RimEnsembleCurveSet::defineUiTreeOrdering( caf::PdmUiTreeOrdering& uiTreeOrdering, QString uiConfigName /*= ""*/ )
{
    if ( m_colorMode == ColorMode::BY_ENSEMBLE_PARAM )
    {
        uiTreeOrdering.add( m_legendConfig() );
    }

    if ( uiConfigName != RicSummaryPlotEditorUi::CONFIGURATION_NAME )
    {
        for ( auto filter : m_curveFilters->filters() )
        {
            uiTreeOrdering.add( filter );
        }

        uiTreeOrdering.add( m_customObjectiveFunctions() );
    }

    uiTreeOrdering.skipRemainingChildren( true );

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

    RimEnsembleCurveSetCollection* coll = nullptr;
    this->firstAncestorOrThisOfType( coll );
    if ( coll && coll->curveSetForSourceStepping() == this )
    {
        iconProvider.setOverlayResourceString( ":/StepUpDownCorner16x16.png" );
    }

    this->setUiIcon( iconProvider );
}

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

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

//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
void RimEnsembleCurveSet::defineEditorAttribute( const caf::PdmFieldHandle* field, QString uiConfigName, caf::PdmUiEditorAttribute* attribute )
{
    if ( auto* attrib = dynamic_cast<caf::PdmUiPushButtonEditorAttribute*>( attribute ) )
    {
        attrib->m_buttonText = "...";
    }

    if ( field == &m_minTimeStep || field == &m_maxTimeStep )
    {
        if ( auto* myAttr = dynamic_cast<caf::PdmUiSliderEditorAttribute*>( attribute ) )
        {
            myAttr->m_minimum     = *allAvailableTimeSteps().begin();
            myAttr->m_maximum     = *allAvailableTimeSteps().rbegin();
            myAttr->m_showSpinBox = false;
        }
    }
    if ( field == &m_colorTransparency )
    {
        if ( auto* myAttr = dynamic_cast<caf::PdmUiDoubleSliderEditorAttribute*>( attribute ) )
        {
            myAttr->m_minimum  = 0.001;
            myAttr->m_maximum  = 1.0;
            myAttr->m_decimals = 2;
        }
    }
}

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

    if ( fieldNeedingOptions == &m_yValuesSummaryCaseCollection )
    {
        RimProject*                            proj   = RimProject::current();
        std::vector<RimSummaryCaseCollection*> groups = proj->summaryGroups();

        for ( RimSummaryCaseCollection* group : groups )
        {
            if ( group->isEnsemble() ) options.push_back( caf::PdmOptionItemInfo( group->name(), group ) );
        }

        options.push_front( caf::PdmOptionItemInfo( "None", nullptr ) );
    }
    else if ( fieldNeedingOptions == &m_colorMode )
    {
        auto singleColorOption          = ColorModeEnum( ColorMode::SINGLE_COLOR );
        auto singleColorWithAlphaOption = ColorModeEnum( ColorMode::SINGLE_COLOR_WITH_ALPHA );
        auto byEnsParamOption           = ColorModeEnum( ColorMode::BY_ENSEMBLE_PARAM );
        auto byObjFuncOption            = ColorModeEnum( ColorMode::BY_OBJECTIVE_FUNCTION );
        auto byCustomObjFuncOption      = ColorModeEnum( ColorMode::BY_CUSTOM_OBJECTIVE_FUNCTION );

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

        RimSummaryCaseCollection* group = m_yValuesSummaryCaseCollection();
        if ( group && group->hasEnsembleParameters() )
        {
            options.push_back( caf::PdmOptionItemInfo( byEnsParamOption.uiText(), ColorMode::BY_ENSEMBLE_PARAM ) );
        }
        options.push_back( caf::PdmOptionItemInfo( byObjFuncOption.uiText(), ColorMode::BY_OBJECTIVE_FUNCTION ) );
        options.push_back( caf::PdmOptionItemInfo( byCustomObjFuncOption.uiText(), ColorMode::BY_CUSTOM_OBJECTIVE_FUNCTION ) );
    }
    else if ( fieldNeedingOptions == &m_ensembleParameter )
    {
        auto params = ensembleParameters( m_ensembleParameterSorting() );
        for ( const auto& paramCorrPair : params )
        {
            QString name = paramCorrPair.first.name;
            double  corr = paramCorrPair.second;
            options.push_back( caf::PdmOptionItemInfo( QString( "%1 (Avg. correlation: %2)" ).arg( name ).arg( corr, 5, 'f', 2 ), name ) );
        }
    }
    else if ( fieldNeedingOptions == &m_yValuesSummaryAddressUiField )
    {
        appendOptionItemsForSummaryAddresses( &options, m_yValuesSummaryCaseCollection() );
    }
    else if ( fieldNeedingOptions == &m_selectedTimeSteps )
    {
        std::set<time_t>    allTimeSteps = allAvailableTimeSteps();
        std::set<QDateTime> currentlySelectedTimeSteps( m_selectedTimeSteps().begin(), m_selectedTimeSteps().end() );

        if ( allTimeSteps.empty() )
        {
            return options;
        }

        std::set<int>          currentlySelectedTimeStepIndices;
        std::vector<QDateTime> allDateTimes;
        for ( time_t timeStep : allTimeSteps )
        {
            QDateTime dateTime = RiaQDateTimeTools::fromTime_t( timeStep );
            if ( currentlySelectedTimeSteps.count( dateTime ) )
            {
                currentlySelectedTimeStepIndices.insert( (int)allDateTimes.size() );
            }
            allDateTimes.push_back( dateTime );
        }

        std::vector<int> filteredTimeStepIndices =
            RimTimeStepFilter::filteredTimeStepIndices( allDateTimes, 0, (int)allDateTimes.size() - 1, m_timeStepFilter(), 1 );

        // Add existing time steps to list of options to avoid removing them when changing filter.
        filteredTimeStepIndices.insert( filteredTimeStepIndices.end(),
                                        currentlySelectedTimeStepIndices.begin(),
                                        currentlySelectedTimeStepIndices.end() );
        std::sort( filteredTimeStepIndices.begin(), filteredTimeStepIndices.end() );
        filteredTimeStepIndices.erase( std::unique( filteredTimeStepIndices.begin(), filteredTimeStepIndices.end() ),
                                       filteredTimeStepIndices.end() );

        QString dateFormatString     = RiaQDateTimeTools::dateFormatString( RiaPreferences::current()->dateFormat(),
                                                                        RiaDefines::DateFormatComponents::DATE_FORMAT_YEAR_MONTH_DAY );
        QString timeFormatString     = RiaQDateTimeTools::timeFormatString( RiaPreferences::current()->timeFormat(),
                                                                        RiaDefines::TimeFormatComponents::TIME_FORMAT_HOUR_MINUTE );
        QString dateTimeFormatString = QString( "%1 %2" ).arg( dateFormatString ).arg( timeFormatString );

        bool showTime = m_timeStepFilter() == RimTimeStepFilter::TS_ALL || m_timeStepFilter() == RimTimeStepFilter::TS_INTERVAL_DAYS;

        for ( auto timeStepIndex : filteredTimeStepIndices )
        {
            QDateTime dateTime = allDateTimes[timeStepIndex];

            if ( showTime && dateTime.time() != QTime( 0, 0, 0 ) )
            {
                options.push_back(
                    caf::PdmOptionItemInfo( RiaQDateTimeTools::toStringUsingApplicationLocale( dateTime, dateTimeFormatString ), dateTime ) );
            }
            else
            {
                options.push_back(
                    caf::PdmOptionItemInfo( RiaQDateTimeTools::toStringUsingApplicationLocale( dateTime, dateFormatString ), dateTime ) );
            }
        }
    }
    else if ( fieldNeedingOptions == &m_customObjectiveFunction )
    {
        auto functions = m_customObjectiveFunctions()->objectiveFunctions();
        for ( const auto& objFunc : functions )
        {
            QString name = objFunc->title();
            options.push_back( caf::PdmOptionItemInfo( name, objFunc ) );
        }
    }
    else if ( fieldNeedingOptions == &m_plotAxisProperties )
    {
        RimSummaryPlot* plot = nullptr;
        firstAncestorOrThisOfTypeAsserted( plot );

        for ( auto axis : plot->plotAxes() )
        {
            options.push_back( caf::PdmOptionItemInfo( axis->objectName(), axis ) );
        }
    }

    return options;
}

//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
std::set<time_t> RimEnsembleCurveSet::allAvailableTimeSteps()
{
    std::set<time_t> timeStepUnion;

    for ( RimSummaryCase* sumCase : summaryCaseCollection()->allSummaryCases() )
    {
        if ( sumCase->summaryReader() )
        {
            std::vector<time_t> timeSteps;
            for ( auto address : m_objectiveValuesSummaryAddresses() )
            {
                for ( auto timeStep : sumCase->summaryReader()->timeSteps( address->address() ) )
                {
                    timeSteps.push_back( timeStep );
                }
            }

            for ( time_t t : timeSteps )
            {
                timeStepUnion.insert( t );
            }
        }
    }
    return timeStepUnion;
}

//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
std::set<RimSummaryCase*> RimEnsembleCurveSet::timestepDefiningSourceCases()
{
    RiaSummaryCurveDefinitionAnalyser* analyserOfSelectedCurveDefs = getOrCreateSelectedCurveDefAnalyser();
    std::set<RimSummaryCase*>          timeStepDefiningSumCases    = analyserOfSelectedCurveDefs->m_singleSummaryCases;
    for ( auto ensemble : analyserOfSelectedCurveDefs->m_ensembles )
    {
        auto allSumCases = ensemble->allSummaryCases();
        timeStepDefiningSumCases.insert( allSumCases.begin(), allSumCases.end() );
    }

    return timeStepDefiningSumCases;
}

//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
RiaSummaryCurveDefinitionAnalyser* RimEnsembleCurveSet::getOrCreateSelectedCurveDefAnalyser()
{
    if ( !m_analyserOfSelectedCurveDefs )
    {
        m_analyserOfSelectedCurveDefs = std::unique_ptr<RiaSummaryCurveDefinitionAnalyser>( new RiaSummaryCurveDefinitionAnalyser );
    }
    m_analyserOfSelectedCurveDefs->setCurveDefinitions( this->curveDefinitions() );
    return m_analyserOfSelectedCurveDefs.get();
}

//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
std::vector<RiaSummaryCurveDefinition> RimEnsembleCurveSet::curveDefinitions() const
{
    std::vector<RiaSummaryCurveDefinition> curveDefs;
    for ( auto dataEntry : m_curves() )
    {
        curveDefs.push_back( dataEntry->curveDefinitionY() );
    }

    return curveDefs;
}

//--------------------------------------------------------------------------------------------------
/// Optimization candidate
//--------------------------------------------------------------------------------------------------
void RimEnsembleCurveSet::appendOptionItemsForSummaryAddresses( QList<caf::PdmOptionItemInfo>* options,
                                                                RimSummaryCaseCollection*      summaryCaseGroup )
{
    if ( !summaryCaseGroup ) return;

    std::set<RifEclipseSummaryAddress> addrSet;
    for ( RimSummaryCase* summaryCase : summaryCaseGroup->allSummaryCases() )
    {
        RifSummaryReaderInterface*                reader = summaryCase->summaryReader();
        const std::set<RifEclipseSummaryAddress>& addrs  = reader ? reader->allResultAddresses() : std::set<RifEclipseSummaryAddress>();

        for ( auto& addr : addrs )
        {
            addrSet.insert( addr );
        }
    }

    for ( auto& addr : addrSet )
    {
        std::string name = addr.uiText();
        QString     s    = QString::fromStdString( name );
        options->push_back( caf::PdmOptionItemInfo( s, QVariant::fromValue( addr ) ) );
    }

    options->push_front( caf::PdmOptionItemInfo( RiaResultNames::undefinedResultName(), QVariant::fromValue( RifEclipseSummaryAddress() ) ) );
}

//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
void RimEnsembleCurveSet::updateFilterLegend()
{
    RimSummaryPlot* plot;
    firstAncestorOrThisOfType( plot );
    if ( plot && plot->plotWidget() )
    {
        if ( m_curveFilters()->isActive() && m_curveFilters()->countActiveFilters() > 0 )
        {
            if ( !m_filterOverlayFrame )
            {
                m_filterOverlayFrame = new RiuDraggableOverlayFrame( plot->plotWidget(), plot->plotWidget()->overlayMargins() );
            }
            m_filterOverlayFrame->setContentFrame( m_curveFilters()->makeFilterDescriptionFrame() );
            plot->plotWidget()->addOverlayFrame( m_filterOverlayFrame );
        }
        else
        {
            if ( m_filterOverlayFrame )
            {
                plot->plotWidget()->removeOverlayFrame( m_filterOverlayFrame );
            }
        }
        plot->scheduleReplotIfVisible();
    }
}

//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
void RimEnsembleCurveSet::updateObjectiveFunctionLegend()
{
    RimSummaryPlot* plot;
    firstAncestorOrThisOfType( plot );
    if ( plot && plot->plotWidget() )
    {
        if ( ( m_colorMode == ColorMode::BY_OBJECTIVE_FUNCTION || m_colorMode == ColorMode::BY_CUSTOM_OBJECTIVE_FUNCTION ) &&
             m_showObjectiveFunctionFormula() )
        {
            if ( !m_objectiveFunctionOverlayFrame )
            {
                m_objectiveFunctionOverlayFrame = new RiuDraggableOverlayFrame( plot->plotWidget(), plot->plotWidget()->overlayMargins() );
            }
            QString title;
            QString description;
            if ( m_colorMode() == ColorMode::BY_OBJECTIVE_FUNCTION )
            {
                std::vector<RifEclipseSummaryAddress> addresses;
                for ( auto address : m_objectiveValuesSummaryAddresses().children() )
                {
                    addresses.push_back( address->address() );
                }

                title = "Objective Function";
                description =
                    QString( "%0::%1" ).arg( m_objectiveFunction()->shortName() ).arg( m_objectiveFunction()->formulaString( addresses ) );
            }
            else if ( m_colorMode() == ColorMode::BY_CUSTOM_OBJECTIVE_FUNCTION && m_customObjectiveFunction() )
            {
                std::vector<RifEclipseSummaryAddress> addresses;
                for ( auto address : m_objectiveValuesSummaryAddresses().children() )
                {
                    addresses.push_back( address->address() );
                }

                title       = "Custom Objective Function";
                description = m_customObjectiveFunction()->formulaString( addresses );
            }
            if ( !title.isEmpty() && !description.isEmpty() )
            {
                m_objectiveFunctionOverlayFrame->setContentFrame( new RiuTextContentFrame( nullptr, title, description ) );
                m_objectiveFunctionOverlayFrame->setMaximumWidth( 10000 );
                plot->plotWidget()->addOverlayFrame( m_objectiveFunctionOverlayFrame );
            }
        }
        else
        {
            if ( m_objectiveFunctionOverlayFrame )
            {
                plot->plotWidget()->removeOverlayFrame( m_objectiveFunctionOverlayFrame );
            }
        }
    }
    plot->scheduleReplotIfVisible();
}

//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
ObjectiveFunctionTimeConfig RimEnsembleCurveSet::objectiveFunctionTimeConfig() const
{
    return { m_minTimeStep(), m_maxTimeStep(), selectedTimeSteps() };
}

//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
void RimEnsembleCurveSet::updateLegendTitle()
{
    if ( m_colorMode == ColorMode::BY_ENSEMBLE_PARAM )
    {
        QString parameterName = m_ensembleParameter();

        QString legendTitle = "Ensemble Parameter";
        legendTitle += "\n";
        legendTitle += parameterName;

        m_legendConfig->setTitle( legendTitle );
    }
    else if ( m_colorMode == ColorMode::BY_OBJECTIVE_FUNCTION )
    {
        QString legendTitle = "Objective Function";

        legendTitle += "\n";
        legendTitle += caf::AppEnum<RimObjectiveFunction::FunctionType>( m_objectiveFunction()->functionType() ).uiText();

        m_legendConfig->setTitle( legendTitle );
    }
    else if ( m_colorMode == ColorMode::BY_CUSTOM_OBJECTIVE_FUNCTION )
    {
        QString legendTitle = "Custom\nObjective Function";
        legendTitle += "\n";
        if ( m_customObjectiveFunction() && m_customObjectiveFunction()->isValid() )
        {
            QString descriptions = m_customObjectiveFunction()->title();
            descriptions.truncate( 30 );
            legendTitle += descriptions;
            if ( m_customObjectiveFunction()->title().length() > descriptions.length() )
            {
                legendTitle += "...";
            }
        }
        else
        {
            legendTitle += "(Invalid Objective Function)";
        }

        m_legendConfig->setTitle( legendTitle );
    }
}

//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
std::vector<cvf::Color3f> RimEnsembleCurveSet::generateColorsForCases( const std::vector<RimSummaryCase*>& summaryCases ) const
{
    std::vector<cvf::Color3f> caseColors;

    if ( m_colorMode == ColorMode::BY_ENSEMBLE_PARAM )
    {
        RimSummaryCaseCollection* group = m_yValuesSummaryCaseCollection();

        QString parameterName = m_ensembleParameter();

        if ( group && !parameterName.isEmpty() && !group->allSummaryCases().empty() )
        {
            auto ensembleParam = group->ensembleParameter( parameterName );
            if ( ensembleParam.isText() || ensembleParam.isNumeric() )
            {
                RimEnsembleCurveSetColorManager::initializeLegendConfig( m_legendConfig, ensembleParam );
                for ( auto& rimCase : summaryCases )
                {
                    caseColors.push_back( RimEnsembleCurveSetColorManager::caseColor( m_legendConfig, rimCase, ensembleParam ) );
                }
            }
        }
    }
    else if ( RimEnsembleCurveSetColorManager::hasSameColorForAllRealizationCurves( m_colorMode() ) )
    {
        caseColors.resize( summaryCases.size(), m_colorForRealizations );
    }
    else if ( m_colorMode == ColorMode::BY_OBJECTIVE_FUNCTION )
    {
        RimSummaryCaseCollection* group = m_yValuesSummaryCaseCollection();

        if ( group && !group->allSummaryCases().empty() )
        {
            auto                                  objectiveFunction = m_objectiveFunction();
            std::vector<RifEclipseSummaryAddress> summaryAddresses;
            for ( auto address : m_objectiveValuesSummaryAddresses() )
            {
                summaryAddresses.push_back( address->address() );
            }

            RimEnsembleCurveSetColorManager::initializeLegendConfig( m_legendConfig,
                                                                     objectiveFunction,
                                                                     group->allSummaryCases(),
                                                                     summaryAddresses,
                                                                     objectiveFunctionTimeConfig() );
            for ( auto& rimCase : summaryCases )
            {
                cvf::Color3f curveColor = RimEnsembleCurveSetColorManager::caseColor( m_legendConfig,
                                                                                      rimCase,
                                                                                      objectiveFunction,
                                                                                      summaryAddresses,
                                                                                      objectiveFunctionTimeConfig() );
                caseColors.push_back( curveColor );
            }
        }
    }
    else if ( m_colorMode == ColorMode::BY_CUSTOM_OBJECTIVE_FUNCTION )
    {
        RimSummaryCaseCollection* group = m_yValuesSummaryCaseCollection();

        if ( group && !group->allSummaryCases().empty() && m_customObjectiveFunction() && m_customObjectiveFunction->isValid() )
        {
            RimEnsembleCurveSetColorManager::initializeLegendConfig( m_legendConfig, m_customObjectiveFunction() );
            for ( auto& rimCase : summaryCases )
            {
                cvf::Color3f curveColor = RimEnsembleCurveSetColorManager::caseColor( m_legendConfig, rimCase, m_customObjectiveFunction() );
                caseColors.push_back( curveColor );
            }
        }
    }

    return caseColors;
}

//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
void RimEnsembleCurveSet::updateCurveColors()
{
    updateLegendTitle();

    // Find the curves to color (skip the statistics)
    std::vector<RimSummaryCurve*> curvesToColor;
    std::vector<RimSummaryCase*>  summaryCases;
    for ( auto& curve : m_curves )
    {
        if ( curve->summaryAddressY().category() == RifEclipseSummaryAddress::SUMMARY_ENSEMBLE_STATISTICS ) continue;

        curvesToColor.push_back( curve );
        summaryCases.push_back( curve->summaryCaseY() );
    }

    // Get the colors
    std::vector<cvf::Color3f> caseColors = 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();
    }

    if ( m_plotCurveForLegendText )
    {
        m_plotCurveForLegendText->setColor( mainEnsembleColor() );
    }

    RimSummaryPlot* plot;
    firstAncestorOrThisOfType( plot );
    if ( plot && plot->plotWidget() )
    {
        if ( m_yValuesSummaryCaseCollection() && isCurvesVisible() &&
             !RimEnsembleCurveSetColorManager::hasSameColorForAllRealizationCurves( m_colorMode() ) && m_legendConfig->showLegend() )
        {
            if ( !m_legendOverlayFrame )
            {
                m_legendOverlayFrame =
                    new RiuDraggableOverlayFrame( plot->plotWidget()->getParentForOverlay(), plot->plotWidget()->overlayMargins() );
            }
            m_legendOverlayFrame->setContentFrame( m_legendConfig->makeLegendFrame() );
            plot->plotWidget()->addOverlayFrame( m_legendOverlayFrame );
        }
        else
        {
            if ( m_legendOverlayFrame )
            {
                plot->plotWidget()->removeOverlayFrame( m_legendOverlayFrame );
            }
        }
        plot->scheduleReplotIfVisible();
    }
}

//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
void RimEnsembleCurveSet::updateTimeAnnotations()
{
    RimSummaryPlot* plot = nullptr;
    firstAncestorOrThisOfType( plot );
    CVF_ASSERT( plot );

    plot->removeAllTimeAnnotations();

    if ( ( m_colorMode() == ColorMode::BY_OBJECTIVE_FUNCTION &&
           m_objectiveFunction()->functionType() == RimObjectiveFunction::FunctionType::F1 ) ||
         ( m_colorMode() == ColorMode::BY_CUSTOM_OBJECTIVE_FUNCTION && m_customObjectiveFunction() &&
           m_customObjectiveFunction()->weightContainsFunctionType( RimObjectiveFunction::FunctionType::F1 ) ) )
    {
        plot->addTimeRangeAnnotation( m_minTimeStep, m_maxTimeStep );
    }

    if ( ( m_colorMode() == ColorMode::BY_OBJECTIVE_FUNCTION &&
           m_objectiveFunction()->functionType() == RimObjectiveFunction::FunctionType::F2 ) ||
         ( m_colorMode() == ColorMode::BY_CUSTOM_OBJECTIVE_FUNCTION && m_customObjectiveFunction() &&
           m_customObjectiveFunction()->weightContainsFunctionType( RimObjectiveFunction::FunctionType::F2 ) ) )
    {
        for ( QDateTime timeStep : m_selectedTimeSteps() )
        {
            plot->addTimeAnnotation( RiaTimeTTools::fromQDateTime( timeStep ) );
        }
    }
    plot->updateAxes();
}

//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
void RimEnsembleCurveSet::updateAddressesUiField()
{
    std::vector<RifEclipseSummaryAddress> addressVector;
    for ( RimSummaryAddress* address : m_objectiveValuesSummaryAddresses )
    {
        addressVector.push_back( address->address() );
    }
    m_objectiveValuesSummaryAddressesUiField = QString::fromStdString( RifEclipseSummaryAddress::generateStringFromAddresses( addressVector ) );
}

//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
void RimEnsembleCurveSet::updatePlotAxis()
{
    for ( RimSummaryCurve* curve : curves() )
    {
        curve->updatePlotAxis();
    }
}

//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
void RimEnsembleCurveSet::updateEnsembleCurves( const std::vector<RimSummaryCase*>& sumCases )
{
    RimSummaryPlot* plot = nullptr;
    firstAncestorOrThisOfType( plot );
    CVF_ASSERT( plot );

    deleteEnsembleCurves();
    if ( m_plotCurveForLegendText ) m_plotCurveForLegendText->detach();
    deleteStatisticsCurves();

    if ( m_statistics->hideEnsembleCurves() ) return;

    setLeftOrRightAxisY( axisY() );

    RimSummaryAddress* addr = m_yValuesSummaryAddress();
    if ( plot && addr->address().category() != RifEclipseSummaryAddress::SUMMARY_INVALID )
    {
        if ( isCurvesVisible() )
        {
            std::vector<RimSummaryCurve*> newSummaryCurves;

            for ( auto& sumCase : sumCases )
            {
                RimSummaryCurve* curve = new RimSummaryCurve();
                curve->setSummaryCaseY( sumCase );
                curve->setSummaryAddressYAndApplyInterpolation( addr->address() );
                curve->setResampling( m_resampling() );

                int lineThickness = 1;
                if ( addr->address().isHistoryVector() )
                {
                    lineThickness = 2;
                    curve->setCurveAppearanceFromCaseType();
                }
                curve->setLineThickness( lineThickness );

                addCurve( curve );

                curve->setLeftOrRightAxisY( axisY() );

                newSummaryCurves.push_back( curve );
            }

#pragma omp parallel for
            for ( int i = 0; i < (int)newSummaryCurves.size(); ++i )
            {
                newSummaryCurves[i]->valuesX();
            }

            for ( int i = 0; i < (int)newSummaryCurves.size(); ++i )
            {
                newSummaryCurves[i]->loadDataAndUpdate( false );
                newSummaryCurves[i]->updatePlotAxis();
                newSummaryCurves[i]->setShowInLegend( false );
            }
        }

        if ( plot->plotWidget() )
        {
            if ( plot->legendsVisible() ) plot->plotWidget()->updateLegend();
            plot->scheduleReplotIfVisible();
            plot->updateAxes();
            plot->updatePlotInfoLabel();

            if ( !m_plotCurveForLegendText )
            {
                m_plotCurveForLegendText.reset( plot->plotWidget()->createPlotCurve( nullptr, "" ) );
            }
            m_plotCurveForLegendText->attachToPlot( plot->plotWidget() );
            updateEnsembleLegendItem();
        }
    }
    updateCurveColors();
}

//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
void RimEnsembleCurveSet::updateStatisticsCurves( const std::vector<RimSummaryCase*>& sumCases )
{
    using SAddr = RifEclipseSummaryAddress;

    RimSummaryCaseCollection* group = m_yValuesSummaryCaseCollection();
    RimSummaryAddress*        addr  = m_yValuesSummaryAddress();

    if ( !isCurvesVisible() || m_disableStatisticCurves || !group || addr->address().category() == RifEclipseSummaryAddress::SUMMARY_INVALID )
        return;

    // Calculate
    {
        std::vector<RimSummaryCase*> statCases = sumCases;
        if ( statCases.empty() )
        {
            if ( m_statistics->basedOnFilteredCases() )
                statCases = filterEnsembleCases( group->allSummaryCases() );
            else
                statCases = group->allSummaryCases();
        }
        m_ensembleStatCase->calculate( statCases, m_statistics->includeIncompleteCurves() );
    }

    std::vector<RifEclipseSummaryAddress> addresses;
    if ( m_statistics->isActive() )
    {
        RifEclipseSummaryAddress dataAddress = m_yValuesSummaryAddress->address();

        if ( m_statistics->showP10Curve() && m_ensembleStatCase->hasP10Data() )
            addresses.push_back( SAddr::ensembleStatisticsAddress( ENSEMBLE_STAT_P10_QUANTITY_NAME, dataAddress.vectorName() ) );
        if ( m_statistics->showP50Curve() && m_ensembleStatCase->hasP50Data() )
            addresses.push_back( SAddr::ensembleStatisticsAddress( ENSEMBLE_STAT_P50_QUANTITY_NAME, dataAddress.vectorName() ) );
        if ( m_statistics->showP90Curve() && m_ensembleStatCase->hasP90Data() )
            addresses.push_back( SAddr::ensembleStatisticsAddress( ENSEMBLE_STAT_P90_QUANTITY_NAME, dataAddress.vectorName() ) );
        if ( m_statistics->showMeanCurve() && m_ensembleStatCase->hasMeanData() )
            addresses.push_back( SAddr::ensembleStatisticsAddress( ENSEMBLE_STAT_MEAN_QUANTITY_NAME, dataAddress.vectorName() ) );
    }

    deleteStatisticsCurves();

    RimSummaryPlot* plot = nullptr;
    firstAncestorOrThisOfType( plot );
    if ( plot && plot->plotWidget() )
    {
        for ( auto address : addresses )
        {
            auto curve = new RimSummaryCurve();
            curve->setParentPlotNoReplot( plot->plotWidget() );
            m_curves.push_back( curve );
            curve->setColor( m_statistics->color() );
            curve->setResampling( m_resampling() );

            auto symbol = statisticsCurveSymbolFromAddress( address );
            curve->setSymbol( symbol );
            curve->setSymbolSize( statisticsCurveSymbolSize( symbol ) );
            curve->setSymbolSkipDistance( 150 );
            if ( m_statistics->showCurveLabels() )
            {
                curve->setSymbolLabel( QString::fromStdString( address.ensembleStatisticsVectorName() ) );
            }
            curve->setLineStyle( RiuQwtPlotCurveDefines::LineStyleEnum::STYLE_SOLID );
            curve->setSummaryCaseY( m_ensembleStatCase.get() );
            curve->setSummaryAddressYAndApplyInterpolation( address );
            curve->setLeftOrRightAxisY( axisY() );

            curve->setShowInLegend( m_statistics->showStatisticsCurveLegends() );

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

        plot->plotWidget()->updateLegend();
        plot->updateAxes();
    }
}

//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
void RimEnsembleCurveSet::updateStatisticsCurves()
{
    updateStatisticsCurves( std::vector<RimSummaryCase*>() );
}

//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
RimEnsembleCurveSet* RimEnsembleCurveSet::clone() const
{
    RimEnsembleCurveSet* copy =
        dynamic_cast<RimEnsembleCurveSet*>( this->xmlCapability()->copyByXmlSerialization( caf::PdmDefaultObjectFactory::instance() ) );
    copy->setSummaryCaseCollection( m_yValuesSummaryCaseCollection() );

    // Update summary case references
    for ( size_t i = 0; i < m_curves.size(); i++ )
    {
        copy->m_curves[i]->setSummaryCaseY( m_curves[i]->summaryCaseY() );
    }
    return copy;
}

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

//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
void RimEnsembleCurveSet::updateAllTextInPlot()
{
    updateEnsembleLegendItem();

    RimSummaryPlot* summaryPlot = nullptr;
    this->firstAncestorOrThisOfTypeAsserted( summaryPlot );
    if ( summaryPlot->plotWidget() )
    {
        summaryPlot->updatePlotTitle();
    }
}

//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
std::vector<RigEnsembleParameter> RimEnsembleCurveSet::variationSortedEnsembleParameters() const
{
    RimSummaryCaseCollection* ensemble = m_yValuesSummaryCaseCollection;
    if ( ensemble )
    {
        return ensemble->variationSortedEnsembleParameters();
    }
    else
    {
        return std::vector<RigEnsembleParameter>();
    }
}

//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
std::vector<std::pair<RigEnsembleParameter, double>> RimEnsembleCurveSet::ensembleParameters( ParameterSorting sortingMode ) const
{
    RimSummaryCaseCollection* ensemble = m_yValuesSummaryCaseCollection;
    if ( ensemble )
    {
        if ( sortingMode == ParameterSorting::ABSOLUTE_VALUE )
        {
            return ensemble->correlationSortedEnsembleParameters( summaryAddress() );
        }

        if ( sortingMode == ParameterSorting::ALPHABETICALLY )
        {
            auto parameters = ensemble->parameterCorrelationsAllTimeSteps( summaryAddress() );
            std::sort( parameters.begin(),
                       parameters.end(),
                       []( const auto& lhs, const auto& rhs ) { return lhs.first.name < rhs.first.name; } );

            return parameters;
        }
    }

    return {};
}

//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
std::vector<RimSummaryCase*> RimEnsembleCurveSet::filterEnsembleCases( const std::vector<RimSummaryCase*>& sumCases )
{
    auto filteredCases = sumCases;

    for ( auto& filter : m_curveFilters->filters() )
    {
        filteredCases = filter->applyFilter( filteredCases );
    }
    return filteredCases;
}

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

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

//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
bool RimEnsembleCurveSet::hasP10Data() const
{
    return m_ensembleStatCase->hasP10Data();
}

//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
bool RimEnsembleCurveSet::hasP50Data() const
{
    return m_ensembleStatCase->hasP50Data();
}

//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
bool RimEnsembleCurveSet::hasP90Data() const
{
    return m_ensembleStatCase->hasP90Data();
}

//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
bool RimEnsembleCurveSet::hasMeanData() const
{
    return m_ensembleStatCase->hasMeanData();
}

//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
void RimEnsembleCurveSet::updateEnsembleLegendItem()
{
    if ( !m_plotCurveForLegendText ) return;

    m_plotCurveForLegendText->setTitle( name() );

    {
        RiuPlotCurveSymbol* symbol = m_plotCurveForLegendText->createSymbol( RiuPlotCurveSymbol::SYMBOL_CROSS );

        if ( RimEnsembleCurveSetColorManager::hasSameColorForAllRealizationCurves( m_colorMode() ) )
        {
            QColor curveColor = mainEnsembleColor();
            QPen   curvePen( curveColor );
            curvePen.setWidth( 2 );

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

        m_plotCurveForLegendText->setSymbol( symbol );
    }

    bool showLegendItem = isCurvesVisible();
    m_plotCurveForLegendText->setVisibleInLegend( showLegendItem );
}

//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
int RimEnsembleCurveSet::ensembleId() const
{
    if ( m_yValuesSummaryCaseCollection() != nullptr ) return m_yValuesSummaryCaseCollection()->ensembleId();

    return -1;
}

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

    return curveSetName;
}

//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
QString RimEnsembleCurveSet::createAutoName() const
{
    RimSummaryPlot* plot = nullptr;
    firstAncestorOrThisOfTypeAsserted( plot );

    QString curveSetName =
        m_summaryAddressNameTools->curveNameY( m_yValuesSummaryAddress->address(), plot->plotTitleHelper(), plot->plotTitleHelper() );
    if ( curveSetName.isEmpty() )
    {
        curveSetName = m_summaryAddressNameTools->curveNameY( m_yValuesSummaryAddress->address(), nullptr, nullptr );
    }

    if ( curveSetName.isEmpty() )
    {
        curveSetName = "Name Placeholder";
    }

    return curveSetName;
}

//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
void RimEnsembleCurveSet::updateLegendMappingMode()
{
    switch ( currentEnsembleParameterType() )
    {
        case RigEnsembleParameter::TYPE_TEXT:
            if ( m_legendConfig->mappingMode() != RimRegularLegendConfig::MappingType::CATEGORY_INTEGER )
                m_legendConfig->setMappingMode( RimRegularLegendConfig::MappingType::CATEGORY_INTEGER );
            break;

        case RigEnsembleParameter::TYPE_NUMERIC:
            if ( m_legendConfig->mappingMode() == RimRegularLegendConfig::MappingType::CATEGORY_INTEGER )
                m_legendConfig->setMappingMode( RimRegularLegendConfig::MappingType::LINEAR_CONTINUOUS );
            break;
    }
}

//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
RiuPlotCurveSymbol::PointSymbolEnum statisticsCurveSymbolFromAddress( const RifEclipseSummaryAddress& address )
{
    auto qName = QString::fromStdString( address.vectorName() );

    if ( qName.contains( ENSEMBLE_STAT_P10_QUANTITY_NAME ) ) return RiuPlotCurveSymbol::SYMBOL_TRIANGLE;
    if ( qName.contains( ENSEMBLE_STAT_P90_QUANTITY_NAME ) ) return RiuPlotCurveSymbol::SYMBOL_DOWN_TRIANGLE;
    if ( qName.contains( ENSEMBLE_STAT_P50_QUANTITY_NAME ) ) return RiuPlotCurveSymbol::SYMBOL_DIAMOND;
    return RiuPlotCurveSymbol::SYMBOL_ELLIPSE;
}

//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
int statisticsCurveSymbolSize( RiuPlotCurveSymbol::PointSymbolEnum symbol )
{
    if ( symbol == RiuPlotCurveSymbol::SYMBOL_DIAMOND ) return 8;
    if ( symbol == RiuPlotCurveSymbol::SYMBOL_TRIANGLE ) return 7;
    if ( symbol == RiuPlotCurveSymbol::SYMBOL_DOWN_TRIANGLE ) return 7;
    return 6;
}

//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
RiuPlotAxis RimEnsembleCurveSet::axisY() const
{
    if ( m_plotAxisProperties )
        return m_plotAxisProperties->plotAxisType();
    else
        return RiuPlotAxis::defaultLeft();
}

//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
void RimEnsembleCurveSet::setLeftOrRightAxisY( RiuPlotAxis plotAxis )
{
    RimSummaryPlot* plot = nullptr;
    firstAncestorOrThisOfTypeAsserted( plot );
    m_plotAxisProperties = plot->axisPropertiesForPlotAxis( plotAxis );

    for ( RimSummaryCurve* curve : curves() )
    {
        curve->setLeftOrRightAxisY( axisY() );
    }
}

//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
void RimEnsembleCurveSet::initAfterRead()
{
    if ( m_plotAxisProperties.value() == nullptr )
    {
        RimSummaryPlot* plot = nullptr;
        firstAncestorOrThisOfType( plot );
        if ( plot )
        {
            m_plotAxisProperties = plot->axisPropertiesForPlotAxis( RiuPlotAxis( m_plotAxis_OBSOLETE() ) );
        }
    }
}
