/////////////////////////////////////////////////////////////////////////////////
//
//  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 "RimWellPltPlot.h"

#include "RiaDateStringParser.h"
#include "RiaLogging.h"
#include "RiaPlotDefines.h"
#include "RiaQDateTimeTools.h"
#include "RiaWellNameComparer.h"

#include "RifReaderEclipseRft.h"

#include "RigAccWellFlowCalculator.h"
#include "RigCaseCellResultsData.h"
#include "RigEclipseCaseData.h"
#include "RigEclipseWellLogExtractor.h"
#include "RigMainGrid.h"
#include "RigSimWellData.h"
#include "RigWellLogExtractor.h"
#include "RigWellPath.h"
#include "RigWellResultFrame.h"
#include "RigWellResultPoint.h"

#include "RimDataSourceForRftPlt.h"
#include "RimEclipseCase.h"
#include "RimEclipseCaseCollection.h"
#include "RimEclipseResultCase.h"
#include "RimEclipseResultDefinition.h"
#include "RimMainPlotCollection.h"
#include "RimOilField.h"
#include "RimProject.h"
#include "RimSummaryCurveAppearanceCalculator.h"
#include "RimWellFlowRateCurve.h"
#include "RimWellLogExtractionCurve.h"
#include "RimWellLogFile.h"
#include "RimWellLogFileChannel.h"
#include "RimWellLogFileCurve.h"
#include "RimWellLogPlot.h"
#include "RimWellLogPlotCollection.h"
#include "RimWellLogPlotNameConfig.h"
#include "RimWellLogRftCurve.h"
#include "RimWellLogTrack.h"
#include "RimWellPath.h"
#include "RimWellPathCollection.h"
#include "RimWellPlotTools.h"

#include "cafPdmUiTreeOrdering.h"
#include "cafPdmUiTreeSelectionEditor.h"
#include "cafVecIjk.h"

#include <algorithm>
#include <iterator>
#include <tuple>

CAF_PDM_SOURCE_INIT( RimWellPltPlot, "WellPltPlot" );

namespace caf
{
template <>
void caf::AppEnum<FlowType>::setUp()
{
    addItem( FLOW_TYPE_PHASE_SPLIT, "PHASE_SPLIT", "Phase Split" );
    addItem( FLOW_TYPE_TOTAL, "TOTAL", "Total Flow" );
    setDefault( FLOW_TYPE_PHASE_SPLIT );
}

template <>
void caf::AppEnum<FlowPhase>::setUp()
{
    addItem( FLOW_PHASE_OIL, "PHASE_OIL", "Oil" );
    addItem( FLOW_PHASE_GAS, "PHASE_GAS", "Gas" );
    addItem( FLOW_PHASE_WATER, "PHASE_WATER", "Water" );
    addItem( FLOW_PHASE_TOTAL, "PHASE_TOTAL", "Total" );
}
} // namespace caf

//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
const char RimWellPltPlot::PLOT_NAME_QFORMAT_STRING[] = "PLT: %1";

//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
RimWellPltPlot::RimWellPltPlot()
    : RimWellLogPlot()
{
    CAF_PDM_InitObject( "Well Allocation Plot", ":/WellFlowPlot16x16.png" );

    CAF_PDM_InitFieldNoDefault( &m_wellLogPlot_OBSOLETE, "WellLog", "WellLog" );
    m_wellLogPlot_OBSOLETE.uiCapability()->setUiTreeHidden( true );
    m_wellLogPlot_OBSOLETE.xmlCapability()->setIOWritable( false );

    CAF_PDM_InitFieldNoDefault( &m_wellPathName, "WellName", "Well Name" );

    CAF_PDM_InitFieldNoDefault( &m_selectedSources, "SourcesInternal", "Sources Internal" );
    m_selectedSources.uiCapability()->setUiEditorTypeName( caf::PdmUiTreeSelectionEditor::uiEditorTypeName() );
    m_selectedSources.uiCapability()->setUiLabelPosition( caf::PdmUiItemInfo::HIDDEN );
    m_selectedSources.uiCapability()->setAutoAddingOptionFromValue( false );
    m_selectedSources.xmlCapability()->disableIO();

    CAF_PDM_InitFieldNoDefault( &m_selectedSourcesForIo, "Sources", "Sources" );
    m_selectedSourcesForIo.uiCapability()->setUiTreeHidden( true );

    CAF_PDM_InitFieldNoDefault( &m_selectedTimeSteps, "TimeSteps", "Time Steps" );
    m_selectedTimeSteps.uiCapability()->setUiEditorTypeName( caf::PdmUiTreeSelectionEditor::uiEditorTypeName() );
    m_selectedTimeSteps.uiCapability()->setUiLabelPosition( caf::PdmUiItemInfo::HIDDEN );
    m_selectedTimeSteps.uiCapability()->setAutoAddingOptionFromValue( false );

    CAF_PDM_InitField( &m_useStandardConditionCurves, "UseStandardConditionCurves", true, "Standard Volume" );
    CAF_PDM_InitField( &m_useReservoirConditionCurves, "UseReservoirConditionCurves", true, "Reservoir Volume" );

    CAF_PDM_InitFieldNoDefault( &m_phases, "Phases", "Phases" );
    m_phases.uiCapability()->setUiEditorTypeName( caf::PdmUiTreeSelectionEditor::uiEditorTypeName() );
    m_phases = std::vector<caf::AppEnum<FlowPhase>>( { FLOW_PHASE_OIL, FLOW_PHASE_GAS, FLOW_PHASE_WATER } );
    m_phases.uiCapability()->setUiLabelPosition( caf::PdmUiItemInfo::HIDDEN );

    m_nameConfig->setCustomName( "PLT Plot" );
    setNamingMethod( RiaDefines::ObjectNamingMethod::CUSTOM );

    this->setAsPlotMdiWindow();
    m_doInitAfterLoad       = false;
    m_isOnLoad              = true;
    m_plotLegendsHorizontal = false;

    setAvailableDepthTypes( { RiaDefines::DepthTypeEnum::MEASURED_DEPTH } );
    setPlotTitleVisible( true );
}

//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
RimWellPltPlot::~RimWellPltPlot()
{
    removeMdiWindowFromMdiArea();

    deleteViewWidget();
}

//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
void RimWellPltPlot::setPlotXAxisTitles( RimWellLogTrack* plotTrack )
{
    std::set<RiaDefines::EclipseUnitSystem> presentUnitSystems;
    for ( const auto& source : m_selectedSources() )
    {
        auto systems = source.availableUnitSystems();
        for ( const auto& s : systems )
        {
            presentUnitSystems.insert( s );
        }
    }
    if ( presentUnitSystems.size() > 1 )
    {
        RiaLogging::errorInMessageBox( nullptr, "ResInsight PLT Plot", "Inconsistent units in PLT plot" );
    }

    if ( presentUnitSystems.empty() ) return;

    RiaDefines::EclipseUnitSystem unitSet = *presentUnitSystems.begin();

    QString axisTitle;
    if ( m_useReservoirConditionCurves )
        axisTitle += RimWellPlotTools::flowPlotAxisTitle( RimWellLogFile::WELL_FLOW_COND_RESERVOIR, unitSet );
    if ( m_useReservoirConditionCurves && m_useStandardConditionCurves ) axisTitle += " | ";
    if ( m_useStandardConditionCurves )
        axisTitle += RimWellPlotTools::flowPlotAxisTitle( RimWellLogFile::WELL_FLOW_COND_STANDARD, unitSet );

    plotTrack->setPropertyValueAxisTitle( axisTitle );

#if 0
    QString unitText;
    for ( auto unitSet: presentUnitSystems )
    {
        switch ( unitSet )
        {
            case RiaEclipseUnitTools::UNITS_METRIC:
            unitText += "[Liquid Sm<sup>3</sup>/day], [Gas kSm<sup>3</sup>/day]";
            break;
            case RiaEclipseUnitTools::UNITS_FIELD:
            unitText += "[Liquid BBL/day], [Gas BOE/day]";
            break;
            case RiaEclipseUnitTools::UNITS_LAB:
            unitText += "[cm<sup>3</sup>/hr]";
            break;
            default:
            unitText += "(unknown unit)";
            break;
        }
    }

    plotTrack->setXAxisTitle("Surface Flow Rate " + unitText);
#endif
}

//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
void RimWellPltPlot::updateFormationsOnPlot() const
{
    if ( plotCount() > 0 )
    {
        RimProject*  proj     = RimProject::current();
        RimWellPath* wellPath = proj->wellPathByName( m_wellPathName );

        RimWellLogTrack* track = dynamic_cast<RimWellLogTrack*>( plotByIndex( 0 ) );
        if ( track )
        {
            RimCase* formationNamesCase = track->formationNamesCase();

            if ( !formationNamesCase )
            {
                /// Set default case. Todo : Use the first of the selected cases in the
                /// plot
                std::vector<RimCase*> cases;
                proj->allCases( cases );

                if ( !cases.empty() )
                {
                    formationNamesCase = cases[0];
                }
            }

            track->setAndUpdateWellPathFormationNamesData( formationNamesCase, wellPath );
        }
    }
}

//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
std::set<RiaRftPltCurveDefinition> RimWellPltPlot::selectedCurveDefs() const
{
    std::set<RifEclipseRftAddress::RftWellLogChannelType> channelTypesToUse = RifEclipseRftAddress::pltPlotChannelTypes();

    return RimWellPlotTools::curveDefsFromTimesteps( RimWellPlotTools::simWellName( m_wellPathName ),
                                                     m_selectedTimeSteps.v(),
                                                     false,
                                                     m_selectedSources(),
                                                     channelTypesToUse );
}

//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
class RigResultPointCalculator
{
public:
    RigResultPointCalculator() {}
    virtual ~RigResultPointCalculator() {}

    const std::vector<cvf::Vec3d>&         pipeBranchCLCoords() { return m_pipeBranchCLCoords; }
    const std::vector<RigWellResultPoint>& pipeBranchWellResultPoints() { return m_pipeBranchWellResultPoints; }
    const std::vector<double>&             pipeBranchMeasuredDepths() { return m_pipeBranchMeasuredDepths; }

protected:
    RigEclipseWellLogExtractor* findWellLogExtractor( const QString& wellPathName, RimEclipseResultCase* eclCase )
    {
        RimProject*                 proj              = RimProject::current();
        RimWellPath*                wellPath          = proj->wellPathByName( wellPathName );
        RimWellLogPlotCollection*   wellLogCollection = RimMainPlotCollection::current()->wellLogPlotCollection();
        RigEclipseWellLogExtractor* eclExtractor      = wellLogCollection->findOrCreateExtractor( wellPath, eclCase );

        return eclExtractor;
    }

    std::vector<cvf::Vec3d>         m_pipeBranchCLCoords;
    std::vector<RigWellResultPoint> m_pipeBranchWellResultPoints;
    std::vector<double>             m_pipeBranchMeasuredDepths;
};

//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------

class RigRftResultPointCalculator : public RigResultPointCalculator
{
public:
    RigRftResultPointCalculator( const QString& wellPathName, RimEclipseResultCase* eclCase, QDateTime m_timeStep )
    {
        RifEclipseRftAddress gasRateAddress = RifEclipseRftAddress::createAddress( RimWellPlotTools::simWellName( wellPathName ),
                                                                                   m_timeStep,
                                                                                   RifEclipseRftAddress::RftWellLogChannelType::GRAT );
        RifEclipseRftAddress oilRateAddress = RifEclipseRftAddress::createAddress( RimWellPlotTools::simWellName( wellPathName ),
                                                                                   m_timeStep,
                                                                                   RifEclipseRftAddress::RftWellLogChannelType::ORAT );
        RifEclipseRftAddress watRateAddress = RifEclipseRftAddress::createAddress( RimWellPlotTools::simWellName( wellPathName ),
                                                                                   m_timeStep,
                                                                                   RifEclipseRftAddress::RftWellLogChannelType::WRAT );

        std::vector<caf::VecIjk> rftIndices;
        eclCase->rftReader()->cellIndices( gasRateAddress, &rftIndices );
        if ( rftIndices.empty() ) eclCase->rftReader()->cellIndices( oilRateAddress, &rftIndices );
        if ( rftIndices.empty() ) eclCase->rftReader()->cellIndices( watRateAddress, &rftIndices );
        if ( rftIndices.empty() ) return;

        std::vector<double> gasRates;
        std::vector<double> oilRates;
        std::vector<double> watRates;
        eclCase->rftReader()->values( gasRateAddress, &gasRates );
        eclCase->rftReader()->values( oilRateAddress, &oilRates );
        eclCase->rftReader()->values( watRateAddress, &watRates );

        std::map<size_t, size_t> globCellIdxToIdxInRftFile;

        const RigMainGrid* mainGrid = eclCase->eclipseCaseData()->mainGrid();

        for ( size_t rftCellIdx = 0; rftCellIdx < rftIndices.size(); rftCellIdx++ )
        {
            caf::VecIjk ijkIndex                       = rftIndices[rftCellIdx];
            size_t      globalCellIndex                = mainGrid->cellIndexFromIJK( ijkIndex.i(), ijkIndex.j(), ijkIndex.k() );
            globCellIdxToIdxInRftFile[globalCellIndex] = rftCellIdx;
        }

        RigEclipseWellLogExtractor* eclExtractor = findWellLogExtractor( wellPathName, eclCase );
        if ( !eclExtractor ) return;

        std::vector<WellPathCellIntersectionInfo> intersections = eclExtractor->cellIntersectionInfosAlongWellPath();

        for ( size_t wpExIdx = 0; wpExIdx < intersections.size(); wpExIdx++ )
        {
            size_t globCellIdx = intersections[wpExIdx].globCellIndex;

            auto it = globCellIdxToIdxInRftFile.find( globCellIdx );
            if ( it == globCellIdxToIdxInRftFile.end() )
            {
                if ( !m_pipeBranchWellResultPoints.empty() && wpExIdx == ( intersections.size() - 1 ) )
                {
                    m_pipeBranchWellResultPoints.pop_back();
                }
                continue;
            }
            m_pipeBranchCLCoords.push_back( intersections[wpExIdx].startPoint );
            m_pipeBranchMeasuredDepths.push_back( intersections[wpExIdx].startMD );

            m_pipeBranchCLCoords.push_back( intersections[wpExIdx].endPoint );
            m_pipeBranchMeasuredDepths.push_back( intersections[wpExIdx].endMD );

            RigWellResultPoint resPoint;
            resPoint.setIsOpen( true );
            resPoint.setGridIndex( 0 ); // Always main grid
            resPoint.setGridCellIndex( globCellIdx ); // Shortcut, since we only have
                                                      // main grid results from RFT

            const double adjustedGasRate =
                RiaEclipseUnitTools::convertSurfaceGasFlowRateToOilEquivalents( eclCase->eclipseCaseData()->unitsType(), gasRates[it->second] );
            resPoint.setFlowData( -1.0, oilRates[it->second], adjustedGasRate, watRates[it->second] );

            m_pipeBranchWellResultPoints.push_back( resPoint );

            if ( wpExIdx < intersections.size() - 1 )
            {
                m_pipeBranchWellResultPoints.push_back( RigWellResultPoint() ); // Invalid res point describing the
                                                                                // "line" between the cells
            }
        }
    }
};

//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
class RigSimWellResultPointCalculator : public RigResultPointCalculator
{
public:
    RigSimWellResultPointCalculator( const QString& wellPathName, RimEclipseResultCase* eclCase, QDateTime m_timeStep )
    {
        // Find timestep index from qdatetime

        const std::vector<QDateTime> timeSteps =
            eclCase->eclipseCaseData()->results( RiaDefines::PorosityModelType::MATRIX_MODEL )->timeStepDates();
        size_t tsIdx = timeSteps.size();
        for ( tsIdx = 0; tsIdx < timeSteps.size(); ++tsIdx )
        {
            if ( timeSteps[tsIdx] == m_timeStep ) break;
        }

        if ( tsIdx >= timeSteps.size() ) return;

        // Build search map into simulation well data

        std::map<size_t, std::pair<size_t, size_t>> globCellIdxToIdxInSimWellBranch;

        const RimWellPath*    wellPath = RimWellPlotTools::wellPathByWellPathNameOrSimWellName( wellPathName );
        const RigSimWellData* simWell =
            wellPath != nullptr ? eclCase->eclipseCaseData()->findSimWellData( wellPath->associatedSimulationWellName() ) : nullptr;

        if ( !simWell ) return;

        if ( !simWell->hasWellResult( tsIdx ) ) return;

        const RigWellResultFrame* resFrame = simWell->wellResultFrame( tsIdx );

        const RigMainGrid* mainGrid = eclCase->eclipseCaseData()->mainGrid();

        for ( size_t brIdx = 0; brIdx < resFrame->wellResultBranches().size(); ++brIdx )
        {
            const std::vector<RigWellResultPoint> branchResPoints = resFrame->branchResultPointsFromBranchIndex( brIdx );
            for ( size_t wrpIdx = 0; wrpIdx < branchResPoints.size(); wrpIdx++ )
            {
                const RigGridBase* grid            = mainGrid->gridByIndex( branchResPoints[wrpIdx].gridIndex() );
                size_t             globalCellIndex = grid->reservoirCellIndex( branchResPoints[wrpIdx].cellIndex() );

                globCellIdxToIdxInSimWellBranch[globalCellIndex] = std::make_pair( brIdx, wrpIdx );
            }
        }

        RigEclipseWellLogExtractor* eclExtractor = findWellLogExtractor( wellPathName, eclCase );

        if ( !eclExtractor ) return;

        std::vector<WellPathCellIntersectionInfo> intersections = eclExtractor->cellIntersectionInfosAlongWellPath();

        for ( size_t wpExIdx = 0; wpExIdx < intersections.size(); wpExIdx++ )
        {
            size_t globCellIdx = intersections[wpExIdx].globCellIndex;

            auto it = globCellIdxToIdxInSimWellBranch.find( globCellIdx );
            if ( it == globCellIdxToIdxInSimWellBranch.end() )
            {
                if ( !m_pipeBranchWellResultPoints.empty() && wpExIdx == ( intersections.size() - 1 ) )
                {
                    m_pipeBranchWellResultPoints.pop_back();
                }
                continue;
            }

            m_pipeBranchCLCoords.push_back( intersections[wpExIdx].startPoint );
            m_pipeBranchMeasuredDepths.push_back( intersections[wpExIdx].startMD );

            m_pipeBranchCLCoords.push_back( intersections[wpExIdx].endPoint );
            m_pipeBranchMeasuredDepths.push_back( intersections[wpExIdx].endMD );

            const RigWellResultPoint resPoint = resFrame->branchResultPointsFromBranchIndex( it->second.first )[it->second.second];
            m_pipeBranchWellResultPoints.push_back( resPoint );

            if ( wpExIdx < intersections.size() - 1 )
            {
                m_pipeBranchWellResultPoints.push_back( RigWellResultPoint() ); // Invalid res point describing the
                                                                                // "line" between the cells
            }
        }
    }
};

//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
void RimWellPltPlot::syncCurvesFromUiSelection()
{
    RimWellLogTrack* plotTrack = dynamic_cast<RimWellLogTrack*>( plotByIndex( 0 ) );
    if ( !plotTrack ) return;

    const std::set<RiaRftPltCurveDefinition>& curveDefs = selectedCurveDefs();

    setPlotXAxisTitles( plotTrack );

    // Delete existing curves

    plotTrack->deleteAllCurves();

    int curveGroupId = 0;

    QString dateFormatString;
    {
        std::vector<QDateTime> allTimeSteps;
        for ( const RiaRftPltCurveDefinition& curveDefToAdd : curveDefs )
        {
            allTimeSteps.push_back( curveDefToAdd.timeStep() );
        }
        dateFormatString = RiaQDateTimeTools::createTimeFormatStringFromDates( allTimeSteps );
    }

    // Add curves
    for ( const RiaRftPltCurveDefinition& curveDefToAdd : curveDefs )
    {
        std::set<FlowPhase> selectedPhases = std::set<FlowPhase>( m_phases().begin(), m_phases().end() );

        RifDataSourceForRftPlt sourceDef = curveDefToAdd.address();
        QDateTime              timeStep  = curveDefToAdd.timeStep();

        std::unique_ptr<RigResultPointCalculator> resultPointCalc;

        QString curveName;

        {
            curveName += sourceDef.eclCase() ? sourceDef.eclCase()->caseUserDescription() : "";
            curveName += sourceDef.wellLogFile() ? sourceDef.wellLogFile()->name() : "";
            if ( sourceDef.sourceType() == RifDataSourceForRftPlt::SourceType::RFT_SIM_WELL_DATA ) curveName += ", RFT";

            curveName += ", " + RiaQDateTimeTools::toStringUsingApplicationLocale( timeStep, dateFormatString );
        }

        RimEclipseResultCase* rimEclipseResultCase = dynamic_cast<RimEclipseResultCase*>( sourceDef.eclCase() );

        if ( sourceDef.sourceType() == RifDataSourceForRftPlt::SourceType::RFT_SIM_WELL_DATA )
        {
            resultPointCalc.reset( new RigRftResultPointCalculator( m_wellPathName, rimEclipseResultCase, timeStep ) );
        }
        else if ( sourceDef.sourceType() == RifDataSourceForRftPlt::SourceType::GRID_MODEL_CELL_DATA )
        {
            resultPointCalc.reset( new RigSimWellResultPointCalculator( m_wellPathName, rimEclipseResultCase, timeStep ) );
        }

        RiaDefines::EclipseUnitSystem unitSet = RiaDefines::EclipseUnitSystem::UNITS_UNKNOWN;
        if ( rimEclipseResultCase )
        {
            unitSet = rimEclipseResultCase->eclipseCaseData()->unitsType();
        }

        if ( resultPointCalc != nullptr )
        {
            if ( !resultPointCalc->pipeBranchCLCoords().empty() )
            {
                if ( selectedPhases.count( FLOW_PHASE_TOTAL ) && m_useReservoirConditionCurves() &&
                     sourceDef.sourceType() == RifDataSourceForRftPlt::SourceType::GRID_MODEL_CELL_DATA )
                {
                    RigAccWellFlowCalculator wfTotalAccumulator( resultPointCalc->pipeBranchCLCoords(),
                                                                 resultPointCalc->pipeBranchWellResultPoints(),
                                                                 resultPointCalc->pipeBranchMeasuredDepths(),
                                                                 true );

                    const std::vector<double>& depthValues = wfTotalAccumulator.pseudoLengthFromTop( 0 );

                    QString curveUnitText = RimWellPlotTools::flowUnitText( RimWellLogFile::WELL_FLOW_COND_RESERVOIR, unitSet );

                    const std::vector<double> accFlow = wfTotalAccumulator.accumulatedTracerFlowPrPseudoLength( RIG_FLOW_TOTAL_NAME, 0 );
                    addStackedCurve( curveName + ", " + RIG_FLOW_TOTAL_NAME + " " + curveUnitText,
                                     depthValues,
                                     accFlow,
                                     plotTrack,
                                     cvf::Color3f::DARK_GRAY,
                                     curveGroupId,
                                     false );
                    curveGroupId++;
                }

                if ( m_useStandardConditionCurves() )
                {
                    RigAccWellFlowCalculator wfPhaseAccumulator( resultPointCalc->pipeBranchCLCoords(),
                                                                 resultPointCalc->pipeBranchWellResultPoints(),
                                                                 resultPointCalc->pipeBranchMeasuredDepths(),
                                                                 false );

                    const std::vector<double>& depthValues = wfPhaseAccumulator.pseudoLengthFromTop( 0 );
                    std::vector<QString>       tracerNames = wfPhaseAccumulator.tracerNames();
                    for ( const QString& tracerName : tracerNames )
                    {
                        auto color = tracerName == RIG_FLOW_OIL_NAME     ? cvf::Color3f::DARK_GREEN
                                     : tracerName == RIG_FLOW_GAS_NAME   ? cvf::Color3f::DARK_RED
                                     : tracerName == RIG_FLOW_WATER_NAME ? cvf::Color3f::BLUE
                                                                         : cvf::Color3f::DARK_GRAY;

                        if ( ( tracerName == RIG_FLOW_OIL_NAME && selectedPhases.count( FLOW_PHASE_OIL ) ) ||
                             ( tracerName == RIG_FLOW_GAS_NAME && selectedPhases.count( FLOW_PHASE_GAS ) ) ||
                             ( tracerName == RIG_FLOW_WATER_NAME && selectedPhases.count( FLOW_PHASE_WATER ) ) )
                        {
                            FlowPhase flowPhase = FLOW_PHASE_NONE;
                            if ( tracerName == RIG_FLOW_OIL_NAME )
                                flowPhase = FLOW_PHASE_OIL;
                            else if ( tracerName == RIG_FLOW_GAS_NAME )
                                flowPhase = FLOW_PHASE_GAS;
                            else if ( tracerName == RIG_FLOW_WATER_NAME )
                                flowPhase = FLOW_PHASE_WATER;
                            QString curveUnitText =
                                RimWellPlotTools::curveUnitText( RimWellLogFile::WELL_FLOW_COND_STANDARD, unitSet, flowPhase );

                            const std::vector<double>& accFlow = wfPhaseAccumulator.accumulatedTracerFlowPrPseudoLength( tracerName, 0 );
                            addStackedCurve( curveName + ", " + tracerName + " " + curveUnitText,
                                             depthValues,
                                             accFlow,
                                             plotTrack,
                                             color,
                                             curveGroupId,
                                             false );
                        }
                    }
                }
            }
        }
        else if ( sourceDef.sourceType() == RifDataSourceForRftPlt::SourceType::OBSERVED_LAS_FILE )
        {
            if ( sourceDef.wellLogFile() && sourceDef.wellLogFile()->wellLogFileData() )
            {
                RimWellLogFile::WellFlowCondition flowCondition = sourceDef.wellLogFile()->wellFlowRateCondition();

                if ( ( m_useStandardConditionCurves() && flowCondition == RimWellLogFile::WELL_FLOW_COND_STANDARD ) ||
                     ( m_useReservoirConditionCurves() && flowCondition == RimWellLogFile::WELL_FLOW_COND_RESERVOIR ) )
                {
                    using ChannelValNameIdxTuple = std::tuple<double, QString, int>;

                    RigWellLogFile* wellLogFileData = sourceDef.wellLogFile()->wellLogFileData();

                    QStringList channelNames = wellLogFileData->wellLogChannelNames();

                    std::multiset<ChannelValNameIdxTuple> sortedChannels;
                    std::vector<std::vector<double>>      channelData;
                    channelData.resize( channelNames.size() );

                    for ( int chIdx = 0; chIdx < channelNames.size(); ++chIdx )
                    {
                        QString channelName = channelNames[chIdx];
                        channelData[chIdx]  = wellLogFileData->values( channelName );
                        if ( !channelData[chIdx].empty() )
                        {
                            sortedChannels.insert( ChannelValNameIdxTuple( -fabs( channelData[chIdx].front() ), channelName, chIdx ) );
                        }
                    }

                    std::vector<double> depthValues = wellLogFileData->depthValues();

                    RiaDefines::EclipseUnitSystem unitSystem = RiaDefines::fromDepthUnit( wellLogFileData->depthUnit() );

                    for ( const ChannelValNameIdxTuple& channelInfo : sortedChannels )
                    {
                        const auto& channelName = std::get<1>( channelInfo );
                        if ( selectedPhases.count( RimWellPlotTools::flowPhaseFromChannelName( channelName ) ) > 0 )
                        {
                            auto color = RimWellPlotTools::isOilFlowChannel( channelName )     ? cvf::Color3f::DARK_GREEN
                                         : RimWellPlotTools::isGasFlowChannel( channelName )   ? cvf::Color3f::DARK_RED
                                         : RimWellPlotTools::isWaterFlowChannel( channelName ) ? cvf::Color3f::BLUE
                                                                                               : cvf::Color3f::DARK_GRAY;

                            FlowPhase flowPhase = FLOW_PHASE_NONE;
                            if ( RimWellPlotTools::isOilFlowChannel( channelName ) )
                                flowPhase = FLOW_PHASE_OIL;
                            else if ( RimWellPlotTools::isGasFlowChannel( channelName ) )
                                flowPhase = FLOW_PHASE_GAS;
                            else if ( RimWellPlotTools::isWaterFlowChannel( channelName ) )
                                flowPhase = FLOW_PHASE_WATER;
                            QString curveUnitText = RimWellPlotTools::curveUnitText( flowCondition, unitSystem, flowPhase );

                            addStackedCurve( curveName + ", " + channelName + " " + curveUnitText,
                                             depthValues,
                                             channelData[std::get<2>( channelInfo )],
                                             plotTrack,
                                             color,
                                             curveGroupId,
                                             true );

                            // Total flow channel will end up first, so just increment the
                            // group idx to make the rest of the phases group together
                            if ( RimWellPlotTools::isTotalFlowChannel( channelName ) ) curveGroupId++;
                        }
                    }
                }
            }
        }
        curveGroupId++;
    }

    plotTrack->setAutoScalePropertyValuesEnabled( true );
    RimWellLogPlot::onLoadDataAndUpdate();
}

//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
void RimWellPltPlot::addStackedCurve( const QString&             curveName,
                                      const std::vector<double>& depthValues,
                                      const std::vector<double>& accFlow,
                                      RimWellLogTrack*           plotTrack,
                                      cvf::Color3f               color,
                                      int                        curveGroupId,
                                      bool                       doFillCurve )
{
    RimWellFlowRateCurve* curve = new RimWellFlowRateCurve;
    curve->setFlowValuesPrDepthValue( curveName, depthType(), depthValues, accFlow );

    curve->setColor( color );
    curve->setGroupId( curveGroupId );

    if ( curveGroupId == 0 )
    {
        curve->setDoFillCurve( true );
        curve->setSymbol( RiuPlotCurveSymbol::SYMBOL_NONE );
    }
    else
    {
        curve->setDoFillCurve( false );
        curve->setSymbol( RimSummaryCurveAppearanceCalculator::cycledSymbol( curveGroupId ) );
    }

    curve->setSymbolSkipDistance( 10 );
    plotTrack->addCurve( curve );
}

//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
void RimWellPltPlot::setCurrentWellName( const QString& currWellName )
{
    m_wellPathName = currWellName;
}

//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
const char* RimWellPltPlot::plotNameFormatString()
{
    return PLOT_NAME_QFORMAT_STRING;
}

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

    const QString simWellName = RimWellPlotTools::simWellName( m_wellPathName );

    if ( fieldNeedingOptions == &m_wellPathName )
    {
        calculateValueOptionsForWells( options );
    }
    else if ( fieldNeedingOptions == &m_selectedSources )
    {
        const std::vector<RimEclipseResultCase*> rftCases = RimWellPlotTools::rftCasesForWell( simWellName );
        std::set<RifDataSourceForRftPlt>         availableRftSources;

        for ( const auto& rftCase : rftCases )
        {
            std::set<RifEclipseRftAddress::RftWellLogChannelType> channelTypesToUse = RifEclipseRftAddress::pltPlotChannelTypes();

            std::set<QDateTime> rftTimes = rftCase->rftReader()->availableTimeSteps( simWellName, channelTypesToUse );
            if ( !rftTimes.empty() )
            {
                availableRftSources.insert( RifDataSourceForRftPlt( RifDataSourceForRftPlt::SourceType::RFT_SIM_WELL_DATA, rftCase ) );
            }
        }

        if ( !availableRftSources.empty() )
        {
            options.push_back( caf::PdmOptionItemInfo::createHeader( RifDataSourceForRftPlt::sourceTypeUiText(
                                                                         RifDataSourceForRftPlt::SourceType::RFT_SIM_WELL_DATA ),
                                                                     true ) );

            for ( const auto& addr : availableRftSources )
            {
                auto item = caf::PdmOptionItemInfo( addr.eclCase()->caseUserDescription(), QVariant::fromValue( addr ) );
                item.setLevel( 1 );
                options.push_back( item );
            }
        }

        const std::vector<RimEclipseResultCase*> gridCases = RimWellPlotTools::gridCasesForWell( simWellName );
        if ( !gridCases.empty() )
        {
            options.push_back( caf::PdmOptionItemInfo::createHeader( RifDataSourceForRftPlt::sourceTypeUiText(
                                                                         RifDataSourceForRftPlt::SourceType::GRID_MODEL_CELL_DATA ),
                                                                     true ) );
        }

        for ( const auto& gridCase : gridCases )
        {
            auto addr = RifDataSourceForRftPlt( RifDataSourceForRftPlt::SourceType::GRID_MODEL_CELL_DATA, gridCase );
            auto item = caf::PdmOptionItemInfo( gridCase->caseUserDescription(), QVariant::fromValue( addr ) );
            item.setLevel( 1 );
            options.push_back( item );
        }

        auto wellLogFiles = RimWellPlotTools::wellLogFilesContainingFlow( m_wellPathName );
        if ( !wellLogFiles.empty() )
        {
            options.push_back( caf::PdmOptionItemInfo::createHeader( RifDataSourceForRftPlt::sourceTypeUiText(
                                                                         RifDataSourceForRftPlt::SourceType::OBSERVED_LAS_FILE ),
                                                                     true ) );

            for ( const auto& wellLogFile : wellLogFiles )
            {
                auto addr = RifDataSourceForRftPlt( wellLogFile );
                auto item = caf::PdmOptionItemInfo( wellLogFile->name(), QVariant::fromValue( addr ) );
                item.setLevel( 1 );
                options.push_back( item );
            }
        }
    }
    else if ( fieldNeedingOptions == &m_selectedTimeSteps )
    {
        std::set<RifEclipseRftAddress::RftWellLogChannelType> channelTypesToUse = RifEclipseRftAddress::pltPlotChannelTypes();

        RimWellPlotTools::calculateValueOptionsForTimeSteps( RimWellPlotTools::simWellName( m_wellPathName ),
                                                             m_selectedSources(),
                                                             channelTypesToUse,
                                                             options );
    }

    if ( fieldNeedingOptions == &m_phases )
    {
        options.push_back( caf::PdmOptionItemInfo( "Oil", FLOW_PHASE_OIL ) );
        options.push_back( caf::PdmOptionItemInfo( "Gas", FLOW_PHASE_GAS ) );
        options.push_back( caf::PdmOptionItemInfo( "Water", FLOW_PHASE_WATER ) );
        options.push_back( caf::PdmOptionItemInfo( "Total", FLOW_PHASE_TOTAL ) );
    }

    return options;
}

//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
void RimWellPltPlot::fieldChangedByUi( const caf::PdmFieldHandle* changedField, const QVariant& oldValue, const QVariant& newValue )
{
    RimWellLogPlot::fieldChangedByUi( changedField, oldValue, newValue );

    if ( changedField == &m_wellPathName )
    {
        m_nameConfig->setCustomName( QString( plotNameFormatString() ).arg( m_wellPathName ) );
    }

    if ( changedField == &m_wellPathName )
    {
        RimWellLogTrack* const plotTrack = dynamic_cast<RimWellLogTrack*>( plotByIndex( 0 ) );
        if ( plotTrack )
        {
            plotTrack->deleteAllCurves();
            m_selectedSources.v().clear();
            m_selectedTimeSteps.v().clear();
            updateFormationsOnPlot();
        }
    }
    else if ( changedField == &m_selectedSources )
    {
        RimProject*  project  = RimProject::current();
        RimWellPath* wellPath = project->wellPathByName( m_wellPathName() );
        if ( wellPath && !wellPath->wellPathGeometry() )
        {
            for ( const RifDataSourceForRftPlt& address : m_selectedSources() )
            {
                if ( address.sourceType() == RifDataSourceForRftPlt::SourceType::RFT_SIM_WELL_DATA ||
                     address.sourceType() == RifDataSourceForRftPlt::SourceType::GRID_MODEL_CELL_DATA )
                {
                    if ( !wellPath->wellPathGeometry() )
                    {
                        QString tmp = QString( "Display of Measured Depth (MD) for Grid or "
                                               "RFT curves is not possible without a "
                                               "well log path, and the curve will be hidden "
                                               "in this mode.\n\n" );

                        RiaLogging::errorInMessageBox( nullptr, "Grid/RFT curve without MD", tmp );

                        // Do not show multiple dialogs
                        break;
                    }
                }
            }
        }
    }

    if ( changedField == &m_selectedSources || changedField == &m_selectedTimeSteps )
    {
        updateFormationsOnPlot();
        syncSourcesIoFieldFromGuiField();
        syncCurvesFromUiSelection();

        RimWellLogTrack* const plotTrack = dynamic_cast<RimWellLogTrack*>( plotByIndex( 0 ) );
        if ( plotTrack )
        {
            plotTrack->setAutoScalePropertyValuesEnabled( true );
        }
        updateZoom();
    }

    if ( changedField == &m_useStandardConditionCurves || changedField == &m_useReservoirConditionCurves || changedField == &m_phases )
    {
        syncCurvesFromUiSelection();

        RimWellLogTrack* const plotTrack = dynamic_cast<RimWellLogTrack*>( plotByIndex( 0 ) );
        if ( plotTrack )
        {
            plotTrack->setAutoScalePropertyValuesEnabled( true );
        }
        updateZoom();
    }
}

//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
void RimWellPltPlot::defineUiTreeOrdering( caf::PdmUiTreeOrdering& uiTreeOrdering, QString uiConfigName )
{
    uiTreeOrdering.skipRemainingChildren( true );
}

//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
void RimWellPltPlot::defineUiOrdering( QString uiConfigName, caf::PdmUiOrdering& uiOrdering )
{
    uiOrdering.add( &m_wellPathName );

    caf::PdmUiGroup* sourcesGroup = uiOrdering.addNewGroupWithKeyword( "Sources", "Sources" );
    sourcesGroup->add( &m_selectedSources );

    caf::PdmUiGroup* timeStepsGroup = uiOrdering.addNewGroupWithKeyword( "Time Steps", "TimeSteps" );
    timeStepsGroup->add( &m_selectedTimeSteps );

    caf::PdmUiGroup* flowGroup = uiOrdering.addNewGroupWithKeyword( "Curve Selection", "PhaseSelection" );
    flowGroup->add( &m_useStandardConditionCurves );
    flowGroup->add( &m_useReservoirConditionCurves );

    flowGroup->add( &m_phases );

    if ( plotCount() > 0 )
    {
        RimWellLogTrack* const track = dynamic_cast<RimWellLogTrack*>( plotByIndex( 0 ) );
        if ( track )
        {
            track->uiOrderingForRftPltFormations( uiOrdering );
            track->uiOrderingForPropertyAxisSettings( uiOrdering );
            caf::PdmUiGroup* depthGroup = uiOrdering.addNewGroup( "Depth Axis Settings" );
            uiOrderingForDepthAxis( uiConfigName, *depthGroup );

            caf::PdmUiGroup* nameGroup = uiOrdering.addNewGroup( "Plot Name" );
            nameGroup->setCollapsedByDefault();
            RimWellLogPlot::uiOrderingForAutoName( uiConfigName, *nameGroup );

            RimPlotWindow::uiOrderingForLegendsAndFonts( uiConfigName, uiOrdering );
        }
    }

    uiOrdering.skipRemainingFields( true );
}

//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
void RimWellPltPlot::defineEditorAttribute( const caf::PdmFieldHandle* field, QString uiConfigName, caf::PdmUiEditorAttribute* attribute )
{
    if ( field == &m_phases )
    {
        caf::PdmUiTreeSelectionEditorAttribute* attrib = dynamic_cast<caf::PdmUiTreeSelectionEditorAttribute*>( attribute );
        attrib->showTextFilter                         = false;
        attrib->showToggleAllCheckbox                  = false;
    }
}

//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
void RimWellPltPlot::initAfterRead()
{
    RimDepthTrackPlot::initAfterRead();

    if ( m_wellLogPlot_OBSOLETE )
    {
        RimWellLogPlot& wellLogPlot = dynamic_cast<RimWellLogPlot&>( *this );
        wellLogPlot                 = std::move( *m_wellLogPlot_OBSOLETE.value() );
        delete m_wellLogPlot_OBSOLETE;
        m_wellLogPlot_OBSOLETE = nullptr;
    }

    RimWellLogPlot::initAfterRead();

    // Postpone init until data has been loaded
    m_doInitAfterLoad = true;
}

//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
void RimWellPltPlot::setupBeforeSave()
{
    syncSourcesIoFieldFromGuiField();
}

//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
void RimWellPltPlot::initAfterLoad()
{
    std::vector<RifDataSourceForRftPlt> selectedSources;
    for ( RimDataSourceForRftPlt* addr : m_selectedSourcesForIo )
    {
        if ( RimProject::current()->isProjectFileVersionEqualOrOlderThan( "2023.1.0" ) &&
             addr->address().sourceType() == RifDataSourceForRftPlt::SourceType::OBSERVED_LAS_FILE )
        {
            // In previous versions, the observed LAS files were not stored in the project file, but handled as a unity.
            // Now we have more observed data types, and need to show all LAS files individually for selection
            auto wellLogFiles = RimWellPlotTools::wellLogFilesContainingFlow( m_wellPathName );
            if ( !wellLogFiles.empty() )
            {
                for ( const auto& wellLogFile : wellLogFiles )
                {
                    auto addr = RifDataSourceForRftPlt( wellLogFile );
                    selectedSources.push_back( addr );
                }
            }

            continue;
        }

        selectedSources.push_back( addr->address() );
    }

    m_selectedSources = selectedSources;
}

//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
void RimWellPltPlot::syncSourcesIoFieldFromGuiField()
{
    m_selectedSourcesForIo.deleteChildren();

    for ( const RifDataSourceForRftPlt& addr : m_selectedSources() )
    {
        m_selectedSourcesForIo.push_back( new RimDataSourceForRftPlt( addr ) );
    }
}

//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
void RimWellPltPlot::calculateValueOptionsForWells( QList<caf::PdmOptionItemInfo>& options )
{
    RimProject* proj = RimProject::current();

    if ( proj != nullptr )
    {
        // Observed wells
        for ( const RimWellPath* const wellPath : proj->allWellPaths() )
        {
            const QString wellName = wellPath->name();

            if ( wellPath->wellPathGeometry() || RimWellPlotTools::hasFlowData( wellPath ) )
                options.push_back( caf::PdmOptionItemInfo( wellName, wellName ) );
        }
    }

    options.push_back( caf::PdmOptionItemInfo( "None", "" ) );
}

//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
void RimWellPltPlot::onLoadDataAndUpdate()
{
    if ( m_doInitAfterLoad )
    {
        initAfterLoad();
        m_doInitAfterLoad = false;
    }

    if ( m_isOnLoad )
    {
        if ( plotCount() > 0 )
        {
            RimWellLogTrack* const plotTrack = dynamic_cast<RimWellLogTrack*>( plotByIndex( 0 ) );
            if ( plotTrack )
            {
                plotTrack->setAnnotationType( RiaDefines::RegionAnnotationType::FORMATION_ANNOTATIONS );
            }
        }
        m_isOnLoad = false;
    }

    updateMdiWindowVisibility();
    updateFormationsOnPlot();
    syncCurvesFromUiSelection();
}
