/////////////////////////////////////////////////////////////////////////////////
//
//  Copyright (C) 2018 Equinor ASA
//
//  ResInsight is free software: you can redistribute it and/or modify
//  it under the terms of the GNU General Public License as published by
//  the Free Software Foundation, either version 3 of the License, or
//  (at your option) any later version.
//
//  ResInsight is distributed in the hope that it will be useful, but WITHOUT ANY
//  WARRANTY; without even the implied warranty of MERCHANTABILITY or
//  FITNESS FOR A PARTICULAR PURPOSE.
//
//  See the GNU General Public License at <http://www.gnu.org/licenses/gpl.html>
//  for more details.
//
/////////////////////////////////////////////////////////////////////////////////

#include "RicWellPathExportMswCompletionsImpl.h"

#include "RiaLogging.h"
#include "RiaWeightedMeanCalculator.h"

#include "RicExportCompletionDataSettingsUi.h"
#include "RicExportFractureCompletionsImpl.h"
#include "RicMswExportInfo.h"
#include "RicMswTableFormatterTools.h"
#include "RicMswValveAccumulators.h"
#include "RicWellPathExportCompletionsFileTools.h"

#include "RifTextDataTableFormatter.h"

#include "RigActiveCellInfo.h"
#include "RigEclipseCaseData.h"
#include "RigGridBase.h"
#include "RigMainGrid.h"
#include "RigWellLogExtractor.h"
#include "RigWellPath.h"
#include "RigWellPathIntersectionTools.h"

#include "RimEclipseCase.h"
#include "RimFishbones.h"
#include "RimFishbonesCollection.h"
#include "RimFractureTemplate.h"
#include "RimModeledWellPath.h"
#include "RimMswCompletionParameters.h"
#include "RimPerforationCollection.h"
#include "RimPerforationInterval.h"
#include "RimProject.h"
#include "RimWellPath.h"
#include "RimWellPathCompletions.h"
#include "RimWellPathFracture.h"
#include "RimWellPathFractureCollection.h"
#include "RimWellPathTieIn.h"
#include "RimWellPathValve.h"

#include <QFile>
#include <QFileInfo>

#include <algorithm>

//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
void RicWellPathExportMswCompletionsImpl::exportWellSegmentsForAllCompletions( const RicExportCompletionDataSettingsUi& exportSettings,
                                                                               const std::vector<RimWellPath*>&         wellPaths )
{
    std::shared_ptr<QFile> unifiedExportFile;
    std::shared_ptr<QFile> unifiedLgrExportFile;
    if ( exportSettings.fileSplit() == RicExportCompletionDataSettingsUi::ExportSplit::UNIFIED_FILE )
    {
        {
            QString fileName;

            QFileInfo fi( exportSettings.customFileName() );
            if ( !exportSettings.customFileName().isEmpty() )
            {
                fileName = fi.baseName() + "_MSW";
            }
            else
            {
                fileName = QString( "UnifiedCompletions_MSW_%1" ).arg( exportSettings.caseToApply->caseUserDescription() );
            }

            unifiedExportFile = RicWellPathExportCompletionsFileTools::openFileForExport( exportSettings.folder,
                                                                                          fileName,
                                                                                          fi.suffix(),
                                                                                          exportSettings.exportDataSourceAsComment() );
        }

        {
            QString   lgrFileName;
            QFileInfo fi( exportSettings.customFileName() );
            if ( !exportSettings.customFileName().isEmpty() )
            {
                lgrFileName = fi.baseName() + "_LGR_MSW";
            }
            else
            {
                lgrFileName = QString( "UnifiedCompletions_LGR_MSW_%1" ).arg( exportSettings.caseToApply->caseUserDescription() );
            }

            unifiedLgrExportFile = RicWellPathExportCompletionsFileTools::openFileForExport( exportSettings.folder,
                                                                                             lgrFileName,
                                                                                             fi.suffix(),
                                                                                             exportSettings.exportDataSourceAsComment() );
        }
    }

    for ( const auto& wellPath : wellPaths )
    {
        std::shared_ptr<QFile> unifiedWellPathFile;
        std::shared_ptr<QFile> unifiedLgrWellPathFile;

        auto allCompletions = wellPath->allCompletionsRecursively();

        bool exportFractures =
            exportSettings.includeFractures() &&
            std::any_of( allCompletions.begin(),
                         allCompletions.end(),
                         []( auto completion )
                         { return completion->isEnabled() && completion->componentType() == RiaDefines::WellPathComponentType::FRACTURE; } );

        bool exportFishbones =
            exportSettings.includeFishbones() &&
            std::any_of( allCompletions.begin(),
                         allCompletions.end(),
                         []( auto completion )
                         { return completion->isEnabled() && completion->componentType() == RiaDefines::WellPathComponentType::FISHBONES; } );

        if ( exportSettings.fileSplit() == RicExportCompletionDataSettingsUi::ExportSplit::SPLIT_ON_WELL && !unifiedWellPathFile )
        {
            QString wellFileName =
                QString( "%1_UnifiedCompletions_MSW_%2" ).arg( wellPath->name(), exportSettings.caseToApply->caseUserDescription() );
            unifiedWellPathFile    = RicWellPathExportCompletionsFileTools::openFileForExport( exportSettings.folder,
                                                                                            wellFileName,
                                                                                            "",
                                                                                            exportSettings.exportDataSourceAsComment() );
            unifiedLgrWellPathFile = RicWellPathExportCompletionsFileTools::openFileForExport( exportSettings.folder,
                                                                                               wellFileName + "_LGR",
                                                                                               "",
                                                                                               exportSettings.exportDataSourceAsComment() );
        }

        {
            // Always use perforation functions to export well segments along well path.
            // If no perforations are present, skip Perforation from file name

            std::shared_ptr<QFile> exportFile;
            std::shared_ptr<QFile> lgrExportFile;

            if ( unifiedExportFile )
            {
                exportFile    = unifiedExportFile;
                lgrExportFile = unifiedLgrExportFile;
            }
            else if ( unifiedWellPathFile )
            {
                exportFile    = unifiedWellPathFile;
                lgrExportFile = unifiedLgrWellPathFile;
            }
            else
            {
                bool anyPerforationsPresent = exportSettings.includeFractures() &&
                                              std::any_of( allCompletions.begin(),
                                                           allCompletions.end(),
                                                           []( auto completion ) {
                                                               return completion->isEnabled() &&
                                                                      completion->componentType() ==
                                                                          RiaDefines::WellPathComponentType::PERFORATION_INTERVAL;
                                                           } );

                QString perforationText = anyPerforationsPresent ? "Perforation_" : "";
                QString fileName =
                    QString( "%1_%2MSW_%3" ).arg( wellPath->name(), perforationText, exportSettings.caseToApply->caseUserDescription() );
                exportFile    = RicWellPathExportCompletionsFileTools::openFileForExport( exportSettings.folder,
                                                                                       fileName,
                                                                                       "",
                                                                                       exportSettings.exportDataSourceAsComment() );
                lgrExportFile = RicWellPathExportCompletionsFileTools::openFileForExport( exportSettings.folder,
                                                                                          fileName + "_LGR",
                                                                                          "",
                                                                                          exportSettings.exportDataSourceAsComment() );
            }
            exportWellSegmentsForPerforations( exportSettings.caseToApply,
                                               exportFile,
                                               lgrExportFile,
                                               wellPath,
                                               exportSettings.timeStep,
                                               exportSettings.exportDataSourceAsComment(),
                                               exportSettings.exportCompletionWelspecAfterMainBore() );
        }

        if ( exportFractures )
        {
            std::shared_ptr<QFile> exportFile;
            std::shared_ptr<QFile> lgrExportFile;

            if ( unifiedExportFile )
            {
                exportFile    = unifiedExportFile;
                lgrExportFile = unifiedLgrExportFile;
            }
            else if ( unifiedWellPathFile )
            {
                exportFile    = unifiedWellPathFile;
                lgrExportFile = unifiedLgrWellPathFile;
            }
            else
            {
                QString fileName = QString( "%1_Fracture_MSW_%2" ).arg( wellPath->name(), exportSettings.caseToApply->caseUserDescription() );
                exportFile    = RicWellPathExportCompletionsFileTools::openFileForExport( exportSettings.folder,
                                                                                       fileName,
                                                                                       "",
                                                                                       exportSettings.exportDataSourceAsComment() );
                lgrExportFile = RicWellPathExportCompletionsFileTools::openFileForExport( exportSettings.folder,
                                                                                          fileName + "_LGR",
                                                                                          "",
                                                                                          exportSettings.exportDataSourceAsComment() );
            }
            exportWellSegmentsForFractures( exportSettings.caseToApply,
                                            exportFile,
                                            lgrExportFile,
                                            wellPath,
                                            exportSettings.exportDataSourceAsComment(),
                                            exportSettings.exportCompletionWelspecAfterMainBore() );
        }

        if ( exportFishbones )
        {
            std::shared_ptr<QFile> exportFile;
            std::shared_ptr<QFile> lgrExportFile;

            if ( unifiedExportFile )
            {
                exportFile    = unifiedExportFile;
                lgrExportFile = unifiedLgrExportFile;
            }
            else if ( unifiedWellPathFile )
            {
                exportFile    = unifiedWellPathFile;
                lgrExportFile = unifiedLgrWellPathFile;
            }
            else
            {
                QString fileName = QString( "%1_Fishbones_MSW_%2" ).arg( wellPath->name(), exportSettings.caseToApply->caseUserDescription() );
                exportFile    = RicWellPathExportCompletionsFileTools::openFileForExport( exportSettings.folder,
                                                                                       fileName,
                                                                                       "",
                                                                                       exportSettings.exportDataSourceAsComment() );
                lgrExportFile = RicWellPathExportCompletionsFileTools::openFileForExport( exportSettings.folder,
                                                                                          fileName + "_LGR",
                                                                                          "",
                                                                                          exportSettings.exportDataSourceAsComment() );
            }
            exportWellSegmentsForFishbones( exportSettings.caseToApply,
                                            exportFile,
                                            lgrExportFile,
                                            wellPath,
                                            exportSettings.exportDataSourceAsComment(),
                                            exportSettings.exportCompletionWelspecAfterMainBore() );
        }
    }
}

//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
void RicWellPathExportMswCompletionsImpl::exportWellSegmentsForPerforations( RimEclipseCase*        eclipseCase,
                                                                             std::shared_ptr<QFile> exportFile,
                                                                             std::shared_ptr<QFile> lgrExportFile,
                                                                             const RimWellPath*     wellPath,
                                                                             int                    timeStep,
                                                                             bool                   exportDataSourceAsComment,
                                                                             bool                   completionSegmentsAfterMainBore )
{
    RiaDefines::EclipseUnitSystem unitSystem = eclipseCase->eclipseCaseData()->unitsType();

    auto mswParameters = wellPath->mswCompletionParameters();

    if ( !mswParameters ) return;

    auto   cellIntersections = generateCellSegments( eclipseCase, wellPath );
    double initialMD         = computeIntitialMeasuredDepth( eclipseCase, wellPath, mswParameters, cellIntersections );

    RicMswExportInfo exportInfo( wellPath, unitSystem, initialMD, mswParameters->lengthAndDepth().text(), mswParameters->pressureDrop().text() );

    if ( generatePerforationsMswExportInfo( eclipseCase, wellPath, timeStep, initialMD, cellIntersections, &exportInfo, exportInfo.mainBoreBranch() ) )
    {
        int branchNumber = 1;

        assignBranchNumbersToBranch( eclipseCase, &exportInfo, exportInfo.mainBoreBranch(), &branchNumber );

        double maxSegmentLength = mswParameters->maxSegmentLength();

        {
            QTextStream               stream( exportFile.get() );
            RifTextDataTableFormatter formatter( stream );
            formatter.setOptionalComment( exportDataSourceAsComment );

            RicMswTableFormatterTools::generateWelsegsTable( formatter, exportInfo, maxSegmentLength, completionSegmentsAfterMainBore );
            bool exportLgrData = false;
            RicMswTableFormatterTools::generateCompsegTables( formatter, exportInfo, exportLgrData );
            RicMswTableFormatterTools::generateWsegvalvTable( formatter, exportInfo );
            RicMswTableFormatterTools::generateWsegAicdTable( formatter, exportInfo );
        }

        if ( exportInfo.hasSubGridIntersections() )
        {
            QTextStream               stream( lgrExportFile.get() );
            RifTextDataTableFormatter formatter( stream );
            formatter.setOptionalComment( exportDataSourceAsComment );

            RicMswTableFormatterTools::generateWelsegsTable( formatter, exportInfo, maxSegmentLength, completionSegmentsAfterMainBore );
            bool exportLgrData = true;
            RicMswTableFormatterTools::generateCompsegTables( formatter, exportInfo, exportLgrData );
            RicMswTableFormatterTools::generateWsegvalvTable( formatter, exportInfo );
            RicMswTableFormatterTools::generateWsegAicdTable( formatter, exportInfo );
        }
    }
}

//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
void RicWellPathExportMswCompletionsImpl::exportWellSegmentsForFractures( RimEclipseCase*        eclipseCase,
                                                                          std::shared_ptr<QFile> exportFile,
                                                                          std::shared_ptr<QFile> lgrExportFile,
                                                                          const RimWellPath*     wellPath,
                                                                          bool                   exportDataSourceAsComment,
                                                                          bool                   completionSegmentsAfterMainBore )
{
    RiaDefines::EclipseUnitSystem unitSystem = eclipseCase->eclipseCaseData()->unitsType();

    if ( eclipseCase == nullptr )
    {
        RiaLogging::error( "Export Fracture Well Segments: Cannot export completions data without specified eclipse case" );
        return;
    }

    auto mswParameters = wellPath->mswCompletionParameters();

    if ( !mswParameters ) return;

    auto   cellIntersections = generateCellSegments( eclipseCase, wellPath );
    double initialMD         = computeIntitialMeasuredDepth( eclipseCase, wellPath, mswParameters, cellIntersections );

    RicMswExportInfo exportInfo( wellPath, unitSystem, initialMD, mswParameters->lengthAndDepth().text(), mswParameters->pressureDrop().text() );

    generateFracturesMswExportInfo( eclipseCase, wellPath, initialMD, cellIntersections, &exportInfo, exportInfo.mainBoreBranch() );

    int branchNumber = 1;
    assignBranchNumbersToBranch( eclipseCase, &exportInfo, exportInfo.mainBoreBranch(), &branchNumber );

    auto doExport = []( RifTextDataTableFormatter& formatter,
                        bool                       exportDataSourceAsComment,
                        RicMswExportInfo&          exportInfo,
                        const RimWellPath*         wellPath,
                        bool                       completionSegmentsAfterMainBore,
                        bool                       exportLgrData )
    {
        formatter.setOptionalComment( exportDataSourceAsComment );

        double maxSegmentLength = wellPath->mswCompletionParameters()->maxSegmentLength();

        RicMswTableFormatterTools::generateWelsegsTable( formatter, exportInfo, maxSegmentLength, completionSegmentsAfterMainBore );

        RicMswTableFormatterTools::generateCompsegTables( formatter, exportInfo, exportLgrData );
    };

    {
        QTextStream               stream( exportFile.get() );
        RifTextDataTableFormatter formatter( stream );
        bool                      exportLgrData = false;

        doExport( formatter, exportDataSourceAsComment, exportInfo, wellPath, completionSegmentsAfterMainBore, exportLgrData );
    }

    if ( exportInfo.hasSubGridIntersections() )
    {
        QTextStream               stream( lgrExportFile.get() );
        RifTextDataTableFormatter formatter( stream );

        bool exportLgrData = true;
        doExport( formatter, exportDataSourceAsComment, exportInfo, wellPath, completionSegmentsAfterMainBore, exportLgrData );
    }
}

//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
void RicWellPathExportMswCompletionsImpl::exportWellSegmentsForFishbones( RimEclipseCase*        eclipseCase,
                                                                          std::shared_ptr<QFile> exportFile,
                                                                          std::shared_ptr<QFile> lgrExportFile,
                                                                          const RimWellPath*     wellPath,
                                                                          bool                   exportDataSourceAsComment,
                                                                          bool                   completionSegmentsAfterMainBore )
{
    auto fishbonesSubs = wellPath->fishbonesCollection()->activeFishbonesSubs();

    if ( eclipseCase == nullptr )
    {
        RiaLogging::error( "Export Well Segments: Cannot export completions data without specified eclipse case" );
        return;
    }

    auto   mswParameters     = wellPath->mswCompletionParameters();
    auto   cellIntersections = generateCellSegments( eclipseCase, wellPath );
    double initialMD         = computeIntitialMeasuredDepth( eclipseCase, wellPath, mswParameters, cellIntersections );

    RiaDefines::EclipseUnitSystem unitSystem = eclipseCase->eclipseCaseData()->unitsType();

    RicMswExportInfo exportInfo( wellPath, unitSystem, initialMD, mswParameters->lengthAndDepth().text(), mswParameters->pressureDrop().text() );

    generateFishbonesMswExportInfo( eclipseCase, wellPath, initialMD, cellIntersections, fishbonesSubs, true, &exportInfo, exportInfo.mainBoreBranch() );

    updateDataForMultipleItemsInSameGridCell( exportInfo.mainBoreBranch() );

    int branchNumber = 1;

    assignBranchNumbersToBranch( eclipseCase, &exportInfo, exportInfo.mainBoreBranch(), &branchNumber );

    double maxSegmentLength = wellPath->mswCompletionParameters()->maxSegmentLength();

    {
        QTextStream               stream( exportFile.get() );
        RifTextDataTableFormatter formatter( stream );
        formatter.setOptionalComment( exportDataSourceAsComment );

        RicMswTableFormatterTools::generateWelsegsTable( formatter, exportInfo, maxSegmentLength, completionSegmentsAfterMainBore );
        bool exportLgrData = false;
        RicMswTableFormatterTools::generateCompsegTables( formatter, exportInfo, exportLgrData );
        RicMswTableFormatterTools::generateWsegvalvTable( formatter, exportInfo );
    }

    if ( exportInfo.hasSubGridIntersections() )
    {
        QTextStream               stream( lgrExportFile.get() );
        RifTextDataTableFormatter formatter( stream );
        formatter.setOptionalComment( exportDataSourceAsComment );

        RicMswTableFormatterTools::generateWelsegsTable( formatter, exportInfo, maxSegmentLength, completionSegmentsAfterMainBore );

        bool exportLgr = true;
        RicMswTableFormatterTools::generateCompsegTables( formatter, exportInfo, exportLgr );
        RicMswTableFormatterTools::generateWsegvalvTable( formatter, exportInfo );
    }
}

//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
void RicWellPathExportMswCompletionsImpl::updateDataForMultipleItemsInSameGridCell( gsl::not_null<RicMswBranch*> branch )
{
    auto allSegments = branch->allSegmentsRecursively();

    {
        // Update effective diameter
        // https://github.com/OPM/ResInsight/issues/7686

        std::map<size_t, std::set<RicMswSegment*>> segmentsInCell;
        {
            for ( auto s : allSegments )
            {
                auto cellsIntersected = s->globalCellsIntersected();
                if ( !cellsIntersected.empty() )
                {
                    for ( auto index : cellsIntersected )
                    {
                        segmentsInCell[index].insert( s );
                    }
                }
            }
        }

        for ( auto [index, segmentsInSameCell] : segmentsInCell )
        {
            // Compute effective diameter based on square root of the sum of diameter squared
            // Deff = sqrt(d1^2 + d2^2 + ..)
            double effectiveDiameter = 0.0;

            for ( auto seg : segmentsInSameCell )
            {
                effectiveDiameter += ( seg->equivalentDiameter() * seg->equivalentDiameter() );
            }

            effectiveDiameter = sqrt( effectiveDiameter );

            for ( auto seg : segmentsInSameCell )
            {
                seg->setEffectiveDiameter( effectiveDiameter );
            }
        }

        {
            // Reduce the diameter for segments in the same cell as main bore
            // https://github.com/OPM/ResInsight/issues/7731

            for ( auto s : allSegments )
            {
                for ( auto completion : s->completions() )
                {
                    if ( completion->completionType() == RigCompletionData::CompletionType::FISHBONES )
                    {
                        auto segments = completion->segments();
                        if ( segments.size() > 1 )
                        {
                            auto firstSegment  = segments[0];
                            auto secondSegment = segments[1];

                            double diameter = secondSegment->effectiveDiameter();

                            firstSegment->setEffectiveDiameter( diameter );
                        }
                    }
                }
            }
        }
    }

    {
        // Update IDC area

        std::map<size_t, std::set<RicMswFishbonesICD*>> icdsInCell;

        {
            for ( auto s : allSegments )
            {
                for ( auto completion : s->completions() )
                {
                    if ( auto icd = dynamic_cast<RicMswFishbonesICD*>( completion ) )
                    {
                        for ( auto icdSegment : icd->segments() )
                        {
                            for ( auto gridIntersection : icdSegment->intersections() )
                            {
                                icdsInCell[gridIntersection->globalCellIndex()].insert( icd );
                            }
                        }
                    }
                }
            }
        }

        for ( auto [Index, icdsInSameCell] : icdsInCell )
        {
            // Compute area sum for all ICDs in same grid cell
            double areaSum = 0.0;

            for ( auto icd : icdsInSameCell )
            {
                areaSum += icd->area();
            }

            for ( auto icd : icdsInSameCell )
            {
                icd->setArea( areaSum );
            }
        }
    }
}

//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
void RicWellPathExportMswCompletionsImpl::generateFishbonesMswExportInfo( const RimEclipseCase* eclipseCase,
                                                                          const RimWellPath*    wellPath,
                                                                          double                initialMD,
                                                                          const std::vector<WellPathCellIntersectionInfo>& cellIntersections,
                                                                          bool                             enableSegmentSplitting,
                                                                          gsl::not_null<RicMswExportInfo*> exportInfo,
                                                                          gsl::not_null<RicMswBranch*>     branch )
{
    std::vector<RimFishbones*> fishbonesSubs = wellPath->fishbonesCollection()->activeFishbonesSubs();
    if ( fishbonesSubs.empty() ) return;

    generateFishbonesMswExportInfo( eclipseCase, wellPath, initialMD, cellIntersections, fishbonesSubs, enableSegmentSplitting, exportInfo, branch );
}

//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
void RicWellPathExportMswCompletionsImpl::generateFishbonesMswExportInfo( const RimEclipseCase* eclipseCase,
                                                                          const RimWellPath*    wellPath,
                                                                          double                initialMD,
                                                                          const std::vector<WellPathCellIntersectionInfo>& cellIntersections,
                                                                          const std::vector<RimFishbones*>& fishbonesSubs,
                                                                          bool                              enableSegmentSplitting,
                                                                          gsl::not_null<RicMswExportInfo*>  exportInfo,
                                                                          gsl::not_null<RicMswBranch*>      branch )
{
    std::vector<WellPathCellIntersectionInfo> filteredIntersections =
        filterIntersections( cellIntersections, initialMD, wellPath->wellPathGeometry(), eclipseCase );

    auto mswParameters = wellPath->mswCompletionParameters();

    bool foundSubGridIntersections = false;

    // Create a dummy perforation interval
    RimPerforationInterval perfInterval;

    createWellPathSegments( branch, filteredIntersections, { &perfInterval }, wellPath, -1, eclipseCase, &foundSubGridIntersections );

    double maxSegmentLength = enableSegmentSplitting ? mswParameters->maxSegmentLength() : std::numeric_limits<double>::infinity();

    auto unitSystem = exportInfo->unitSystem();

    for ( RimFishbones* subs : fishbonesSubs )
    {
        std::map<size_t, std::vector<size_t>> subAndLateralIndices;
        for ( const auto& [subIndex, lateralIndex] : subs->installedLateralIndices() )
        {
            subAndLateralIndices[subIndex].push_back( lateralIndex );
        }

        // Find cell intersections closest to each sub location
        std::map<size_t, std::vector<size_t>> closestSubForCellIntersections;
        std::map<size_t, size_t>              cellIntersectionContainingSubIndex;
        {
            auto fishboneSectionStart = subs->startMD();
            auto fishboneSectionEnd   = subs->endMD();

            for ( size_t intersectionIndex = 0; intersectionIndex < filteredIntersections.size(); intersectionIndex++ )
            {
                const auto& cellIntersection = filteredIntersections[intersectionIndex];
                if ( ( fishboneSectionEnd >= cellIntersection.startMD ) && ( fishboneSectionStart <= cellIntersection.endMD ) )

                {
                    double intersectionMidpoint = 0.5 * ( cellIntersection.startMD + cellIntersection.endMD );
                    size_t closestSubIndex      = 0;
                    double closestDistance      = std::numeric_limits<double>::infinity();
                    for ( const auto& [subIndex, lateralIndices] : subAndLateralIndices )
                    {
                        double subMD = subs->measuredDepth( subIndex );

                        if ( ( cellIntersection.startMD <= subMD ) && ( subMD <= cellIntersection.endMD ) )
                        {
                            cellIntersectionContainingSubIndex[subIndex] = intersectionIndex;
                        }

                        auto distanceCandicate = std::abs( subMD - intersectionMidpoint );
                        if ( distanceCandicate < closestDistance )
                        {
                            closestDistance = distanceCandicate;
                            closestSubIndex = subIndex;
                        }
                    }

                    closestSubForCellIntersections[closestSubIndex].push_back( intersectionIndex );
                }
            }
        }

        for ( const auto& [subIndex, lateralIndices] : subAndLateralIndices )
        {
            double subEndMD  = subs->measuredDepth( subIndex );
            double subEndTVD = RicMswTableFormatterTools::tvdFromMeasuredDepth( branch->wellPath(), subEndMD );

            {
                // Add completion for ICD
                auto icdSegment = std::make_unique<RicMswSegment>( "ICD segment", subEndMD, subEndMD + 0.1, subEndTVD, subEndTVD, subIndex );

                for ( auto lateralIndex : lateralIndices )
                {
                    QString label = QString( "Lateral %1" ).arg( lateralIndex + 1 );
                    icdSegment->addCompletion( std::make_unique<RicMswFishbones>( label, wellPath, subEndMD, subEndTVD, lateralIndex ) );
                }

                assignFishbonesLateralIntersections( eclipseCase,
                                                     branch->wellPath(),
                                                     subs,
                                                     icdSegment.get(),
                                                     &foundSubGridIntersections,
                                                     maxSegmentLength,
                                                     unitSystem );

                auto icdCompletion = std::make_unique<RicMswFishbonesICD>( QString( "ICD" ), wellPath, subEndMD, subEndTVD, nullptr );
                icdCompletion->setFlowCoefficient( subs->icdFlowCoefficient() );
                double icdOrificeRadius = subs->icdOrificeDiameter( unitSystem ) / 2;
                icdCompletion->setArea( icdOrificeRadius * icdOrificeRadius * cvf::PI_D * subs->icdCount() );

                // assign open hole segments to sub
                {
                    const RigMainGrid* mainGrid = eclipseCase->mainGrid();

                    std::set<size_t> indices;
                    for ( auto intersectionIndex : closestSubForCellIntersections[subIndex] )
                    {
                        indices.insert( intersectionIndex );
                    }

                    indices.insert( cellIntersectionContainingSubIndex[subIndex] );

                    for ( auto intersectionIndex : indices )
                    {
                        auto intersection = filteredIntersections[intersectionIndex];
                        if ( intersection.globCellIndex >= mainGrid->globalCellArray().size() ) continue;

                        size_t             localGridCellIndex = 0u;
                        const RigGridBase* localGrid =
                            mainGrid->gridAndGridLocalIdxFromGlobalCellIdx( intersection.globCellIndex, &localGridCellIndex );
                        QString gridName;
                        if ( localGrid != mainGrid )
                        {
                            gridName                  = QString::fromStdString( localGrid->gridName() );
                            foundSubGridIntersections = true;
                        }

                        size_t i, j, k;
                        localGrid->ijkFromCellIndex( localGridCellIndex, &i, &j, &k );
                        cvf::Vec3st localIJK( i, j, k );

                        auto mswIntersect = std::make_shared<RicMswSegmentCellIntersection>( gridName,
                                                                                             intersection.globCellIndex,
                                                                                             localIJK,
                                                                                             intersection.intersectionLengthsInCellCS );
                        icdSegment->addIntersection( mswIntersect );
                    }
                }

                icdCompletion->addSegment( std::move( icdSegment ) );

                RicMswSegment* segmentOnParentBranch = branch->findClosestSegmentWithLowerMD( subEndMD );
                if ( segmentOnParentBranch )
                {
                    segmentOnParentBranch->addCompletion( std::move( icdCompletion ) );
                }
            }
        }
    }
    exportInfo->setHasSubGridIntersections( exportInfo->hasSubGridIntersections() || foundSubGridIntersections );
    // branch->sortSegments();

    auto connectedWellPaths = wellPathsWithTieIn( wellPath );
    for ( auto childWellPath : connectedWellPaths )
    {
        auto childMswBranch         = createChildMswBranch( childWellPath );
        auto childCellIntersections = generateCellSegments( eclipseCase, childWellPath );

        generateFishbonesMswExportInfo( eclipseCase,
                                        childWellPath,
                                        initialMD,
                                        childCellIntersections,
                                        enableSegmentSplitting,
                                        exportInfo,
                                        childMswBranch.get() );

        branch->addChildBranch( std::move( childMswBranch ) );
    }
}

//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
void RicWellPathExportMswCompletionsImpl::generateFishbonesMswExportInfoForWell( const RimEclipseCase*            eclipseCase,
                                                                                 const RimWellPath*               wellPath,
                                                                                 gsl::not_null<RicMswExportInfo*> exportInfo,
                                                                                 gsl::not_null<RicMswBranch*>     branch )
{
    auto   mswParameters     = wellPath->mswCompletionParameters();
    auto   cellIntersections = generateCellSegments( eclipseCase, wellPath );
    double initialMD         = computeIntitialMeasuredDepth( eclipseCase, wellPath, mswParameters, cellIntersections );

    bool enableSegmentSplitting = false;
    generateFishbonesMswExportInfo( eclipseCase, wellPath, initialMD, cellIntersections, enableSegmentSplitting, exportInfo, branch );
}

//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
bool RicWellPathExportMswCompletionsImpl::generateFracturesMswExportInfo( RimEclipseCase*    eclipseCase,
                                                                          const RimWellPath* wellPath,
                                                                          double             initialMD,
                                                                          const std::vector<WellPathCellIntersectionInfo>& cellIntersections,
                                                                          gsl::not_null<RicMswExportInfo*> exportInfo,
                                                                          gsl::not_null<RicMswBranch*>     branch )
{
    auto fractures = wellPath->fractureCollection()->activeFractures();

    std::vector<WellPathCellIntersectionInfo> filteredIntersections =
        filterIntersections( cellIntersections, initialMD, wellPath->wellPathGeometry(), eclipseCase );

    // Create a dummy perforation interval
    RimPerforationInterval perfInterval;

    bool foundSubGridIntersections = false;
    createWellPathSegments( branch, filteredIntersections, { &perfInterval }, wellPath, -1, eclipseCase, &foundSubGridIntersections );

    // Check if fractures are to be assigned to current main bore segment
    for ( RimWellPathFracture* fracture : fractures )
    {
        double fractureStartMD = fracture->fractureMD();
        if ( fracture->fractureTemplate()->orientationType() == RimFractureTemplate::ALONG_WELL_PATH )
        {
            double perforationLength = fracture->fractureTemplate()->perforationLength();
            fractureStartMD -= 0.5 * perforationLength;
        }

        auto segment = branch->findClosestSegmentWithLowerMD( fractureStartMD );
        if ( segment )
        {
            std::vector<RigCompletionData> completionData =
                RicExportFractureCompletionsImpl::generateCompdatValues( eclipseCase,
                                                                         wellPath->completionSettings()->wellNameForExport(),
                                                                         wellPath->wellPathGeometry(),
                                                                         { fracture },
                                                                         nullptr,
                                                                         nullptr );

            assignFractureCompletionsToCellSegment( eclipseCase, wellPath, fracture, completionData, segment, &foundSubGridIntersections );
        }
    }

    exportInfo->setHasSubGridIntersections( exportInfo->hasSubGridIntersections() || foundSubGridIntersections );
    branch->sortSegments();

    auto connectedWellPaths = wellPathsWithTieIn( wellPath );
    for ( auto childWellPath : connectedWellPaths )
    {
        auto childMswBranch         = createChildMswBranch( childWellPath );
        auto childCellIntersections = generateCellSegments( eclipseCase, childWellPath );

        if ( generateFracturesMswExportInfo( eclipseCase, childWellPath, initialMD, childCellIntersections, exportInfo, childMswBranch.get() ) )
        {
            branch->addChildBranch( std::move( childMswBranch ) );
        }
    }

    return true;
}

//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
bool RicWellPathExportMswCompletionsImpl::generatePerforationsMswExportInfo( RimEclipseCase*    eclipseCase,
                                                                             const RimWellPath* wellPath,
                                                                             int                timeStep,
                                                                             double             initialMD,
                                                                             const std::vector<WellPathCellIntersectionInfo>& cellIntersections,
                                                                             gsl::not_null<RicMswExportInfo*> exportInfo,
                                                                             gsl::not_null<RicMswBranch*>     branch )
{
    auto perforationIntervals = wellPath->perforationIntervalCollection()->activePerforations();

    // Check if there exist overlap between valves in a perforation interval
    for ( const auto& perfInterval : perforationIntervals )
    {
        for ( const auto& valve : perfInterval->valves() )
        {
            for ( const auto& otherValve : perfInterval->valves() )
            {
                if ( otherValve != valve )
                {
                    bool hasIntersection = !( ( valve->endMD() < otherValve->startMD() ) || ( otherValve->endMD() < valve->startMD() ) );

                    if ( hasIntersection )
                    {
                        RiaLogging::error( QString( "Valve overlap detected for perforation interval : %1" ).arg( perfInterval->name() ) );

                        RiaLogging::error( "Name of valves" );
                        RiaLogging::error( valve->name() );
                        RiaLogging::error( otherValve->name() );

                        RiaLogging::error( "Failed to export well segments" );

                        return false;
                    }
                }
            }
        }
    }

    std::vector<WellPathCellIntersectionInfo> filteredIntersections =
        filterIntersections( cellIntersections, initialMD, wellPath->wellPathGeometry(), eclipseCase );

    bool foundSubGridIntersections = false;

    createWellPathSegments( branch, filteredIntersections, perforationIntervals, wellPath, timeStep, eclipseCase, &foundSubGridIntersections );

    createValveCompletions( branch, perforationIntervals, wellPath, exportInfo->unitSystem() );

    const RigActiveCellInfo* activeCellInfo = eclipseCase->eclipseCaseData()->activeCellInfo( RiaDefines::PorosityModelType::MATRIX_MODEL );

    assignValveContributionsToSuperICDsOrAICDs( branch, perforationIntervals, filteredIntersections, activeCellInfo, exportInfo->unitSystem() );
    moveIntersectionsToICVs( branch, perforationIntervals, exportInfo->unitSystem() );
    moveIntersectionsToSuperICDsOrAICDs( branch );

    exportInfo->setHasSubGridIntersections( exportInfo->hasSubGridIntersections() || foundSubGridIntersections );
    branch->sortSegments();

    auto connectedWellPaths = wellPathsWithTieIn( wellPath );

    for ( auto childWellPath : connectedWellPaths )
    {
        auto childMswBranch         = createChildMswBranch( childWellPath );
        auto childCellIntersections = generateCellSegments( eclipseCase, childWellPath );

        // Start MD of child well path at the tie in location
        const double tieInOnParentWellPath = childWellPath->wellPathTieIn() ? childWellPath->wellPathTieIn()->tieInMeasuredDepth() : initialMD;

        if ( generatePerforationsMswExportInfo( eclipseCase,
                                                childWellPath,
                                                timeStep,
                                                tieInOnParentWellPath,
                                                childCellIntersections,
                                                exportInfo,
                                                childMswBranch.get() ) )
        {
            branch->addChildBranch( std::move( childMswBranch ) );
        }
    }

    return true;
}

//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
std::vector<WellPathCellIntersectionInfo> RicWellPathExportMswCompletionsImpl::generateCellSegments( const RimEclipseCase* eclipseCase,
                                                                                                     const RimWellPath*    wellPath )
{
    auto wellPathGeometry = wellPath->wellPathGeometry();
    CVF_ASSERT( wellPathGeometry );

    const std::vector<cvf::Vec3d>& coords = wellPathGeometry->uniqueWellPathPoints();
    const std::vector<double>&     mds    = wellPathGeometry->uniqueMeasuredDepths();
    CVF_ASSERT( !coords.empty() && !mds.empty() );

    const RigMainGrid* mainGrid = eclipseCase->mainGrid();

    std::vector<WellPathCellIntersectionInfo> allIntersections =
        RigWellPathIntersectionTools::findCellIntersectionInfosAlongPath( eclipseCase->eclipseCaseData(), wellPath->name(), coords, mds );
    if ( allIntersections.empty() ) return {};

    std::vector<WellPathCellIntersectionInfo> continuousIntersections =
        RigWellPathIntersectionTools::buildContinuousIntersections( allIntersections, mainGrid );

    return continuousIntersections;
}

//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
double RicWellPathExportMswCompletionsImpl::computeIntitialMeasuredDepth( const RimEclipseCase*             eclipseCase,
                                                                          const RimWellPath*                wellPath,
                                                                          const RimMswCompletionParameters* mswParameters,
                                                                          const std::vector<WellPathCellIntersectionInfo>& allIntersections )
{
    if ( allIntersections.empty() ) return 0.0;

    const RigActiveCellInfo* activeCellInfo = eclipseCase->eclipseCaseData()->activeCellInfo( RiaDefines::PorosityModelType::MATRIX_MODEL );

    double candidateMeasuredDepth = 0.0;
    if ( mswParameters->referenceMDType() == RimMswCompletionParameters::ReferenceMDType::MANUAL_REFERENCE_MD )
    {
        candidateMeasuredDepth = mswParameters->manualReferenceMD();
    }
    else
    {
        for ( const WellPathCellIntersectionInfo& intersection : allIntersections )
        {
            if ( activeCellInfo->isActive( intersection.globCellIndex ) )
            {
                candidateMeasuredDepth = intersection.startMD;
                break;
            }
        }

        double startOfFirstCompletion = std::numeric_limits<double>::infinity();
        {
            std::vector<const RimWellPathComponentInterface*> allCompletions = wellPath->completions()->allCompletions();

            for ( const RimWellPathComponentInterface* completion : allCompletions )
            {
                if ( completion->isEnabled() && completion->startMD() < startOfFirstCompletion )
                {
                    startOfFirstCompletion = completion->startMD();
                }
            }
        }

        // Initial MD is the lowest MD based on grid intersection and start of fracture completions
        // https://github.com/OPM/ResInsight/issues/6071
        candidateMeasuredDepth = std::min( candidateMeasuredDepth, startOfFirstCompletion );
    }

    return candidateMeasuredDepth;
}

//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
std::vector<WellPathCellIntersectionInfo>
    RicWellPathExportMswCompletionsImpl::filterIntersections( const std::vector<WellPathCellIntersectionInfo>& intersections,
                                                              double                                           initialMD,
                                                              gsl::not_null<const RigWellPath*>                wellPathGeometry,
                                                              gsl::not_null<const RimEclipseCase*>             eclipseCase )
{
    std::vector<WellPathCellIntersectionInfo> filteredIntersections;

    if ( !intersections.empty() && intersections[0].startMD > initialMD )
    {
        WellPathCellIntersectionInfo firstIntersection = intersections[0];

        // Add a segment from user defined MD to start of grid
        cvf::Vec3d intersectionPoint = wellPathGeometry->interpolatedPointAlongWellPath( initialMD );

        WellPathCellIntersectionInfo extraIntersection;

        extraIntersection.globCellIndex         = std::numeric_limits<size_t>::max();
        extraIntersection.startPoint            = intersectionPoint;
        extraIntersection.endPoint              = firstIntersection.startPoint;
        extraIntersection.startMD               = initialMD;
        extraIntersection.endMD                 = firstIntersection.startMD;
        extraIntersection.intersectedCellFaceIn = cvf::StructGridInterface::NO_FACE;

        if ( firstIntersection.intersectedCellFaceIn != cvf::StructGridInterface::NO_FACE )

        {
            extraIntersection.intersectedCellFaceOut = cvf::StructGridInterface::oppositeFace( firstIntersection.intersectedCellFaceIn );
        }
        else if ( firstIntersection.intersectedCellFaceOut != cvf::StructGridInterface::NO_FACE )
        {
            extraIntersection.intersectedCellFaceOut = firstIntersection.intersectedCellFaceOut;
        }

        extraIntersection.intersectionLengthsInCellCS = cvf::Vec3d::ZERO;

        filteredIntersections.push_back( extraIntersection );
    }

    const double epsilon = 1.0e-3;

    for ( const WellPathCellIntersectionInfo& intersection : intersections )
    {
        if ( ( intersection.endMD - initialMD ) < epsilon )
        {
            // Skip all intersections before initial measured depth
            continue;
        }

        if ( ( intersection.startMD - initialMD ) > epsilon )
        {
            filteredIntersections.push_back( intersection );
        }
        else
        {
            // InitialMD is inside intersection, split based on intersection point

            cvf::Vec3d intersectionPoint = wellPathGeometry->interpolatedPointAlongWellPath( initialMD );

            WellPathCellIntersectionInfo extraIntersection;

            extraIntersection.globCellIndex          = intersection.globCellIndex;
            extraIntersection.startPoint             = intersectionPoint;
            extraIntersection.endPoint               = intersection.endPoint;
            extraIntersection.startMD                = initialMD;
            extraIntersection.endMD                  = intersection.endMD;
            extraIntersection.intersectedCellFaceIn  = cvf::StructGridInterface::NO_FACE;
            extraIntersection.intersectedCellFaceOut = intersection.intersectedCellFaceOut;

            const RigMainGrid* grid = eclipseCase->mainGrid();

            if ( intersection.globCellIndex < grid->cellCount() )
            {
                extraIntersection.intersectionLengthsInCellCS =
                    RigWellPathIntersectionTools::calculateLengthInCell( grid, intersection.globCellIndex, intersectionPoint, intersection.endPoint );
            }
            else
            {
                extraIntersection.intersectionLengthsInCellCS = cvf::Vec3d::ZERO;
            }

            filteredIntersections.push_back( extraIntersection );
        }
    }

    return filteredIntersections;
}

//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
void RicWellPathExportMswCompletionsImpl::createWellPathSegments( gsl::not_null<RicMswBranch*>                     branch,
                                                                  const std::vector<WellPathCellIntersectionInfo>& cellSegmentIntersections,
                                                                  const std::vector<const RimPerforationInterval*>& perforationIntervals,
                                                                  const RimWellPath*                                wellPath,
                                                                  int                                               timeStep,
                                                                  const RimEclipseCase*                             eclipseCase,
                                                                  bool* foundSubGridIntersections )
{
    // Intersections along the well path with grid geometry is handled by well log extraction tools.
    // The threshold in RigWellLogExtractionTools::isEqualDepth is currently set to 0.1m, and this
    // is a pretty large threshold based on the indicated threshold of 0.001m for MSW segments
    const double segmentLengthThreshold = 1.0e-3;

    for ( const auto& cellIntInfo : cellSegmentIntersections )
    {
        const double segmentLength = std::fabs( cellIntInfo.endMD - cellIntInfo.startMD );

        if ( segmentLength > segmentLengthThreshold )
        {
            auto segment = std::make_unique<RicMswSegment>( QString( "%1 segment" ).arg( branch->label() ),
                                                            cellIntInfo.startMD,
                                                            cellIntInfo.endMD,
                                                            cellIntInfo.startTVD(),
                                                            cellIntInfo.endTVD() );

            for ( const RimPerforationInterval* interval : perforationIntervals )
            {
                double overlapStart = std::max( interval->startMD(), segment->startMD() );
                double overlapEnd   = std::min( interval->endMD(), segment->endMD() );
                double overlap      = std::max( 0.0, overlapEnd - overlapStart );
                if ( overlap > 0.0 )
                {
                    double overlapStartTVD = -wellPath->wellPathGeometry()->interpolatedPointAlongWellPath( overlapStart ).z();
                    auto intervalCompletion = std::make_unique<RicMswPerforation>( interval->name(), wellPath, overlapStart, overlapStartTVD );
                    std::vector<RigCompletionData> completionData =
                        generatePerforationIntersections( wellPath, interval, timeStep, eclipseCase );
                    assignPerforationIntersections( completionData,
                                                    intervalCompletion.get(),
                                                    cellIntInfo,
                                                    overlapStart,
                                                    overlapEnd,
                                                    foundSubGridIntersections );
                    segment->addCompletion( std::move( intervalCompletion ) );
                }
            }
            branch->addSegment( std::move( segment ) );
        }
        else
        {
            QString text = QString( "Skipping segment , threshold = %1, length = %2" ).arg( segmentLengthThreshold ).arg( segmentLength );
            RiaLogging::info( text );
        }
    }
}

//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
void RicWellPathExportMswCompletionsImpl::createValveCompletions( gsl::not_null<RicMswBranch*>                      branch,
                                                                  const std::vector<const RimPerforationInterval*>& perforationIntervals,
                                                                  const RimWellPath*                                wellPath,
                                                                  RiaDefines::EclipseUnitSystem                     unitSystem )
{
    int  nMainSegment = 0;
    auto segments     = branch->segments();
    for ( auto segment : segments )
    {
        std::unique_ptr<RicMswPerforationICV>  ICV;
        std::unique_ptr<RicMswPerforationICD>  superICD;
        std::unique_ptr<RicMswPerforationAICD> superAICD;

        double totalICDOverlap  = 0.0;
        double totalAICDOverlap = 0.0;

        for ( const RimPerforationInterval* interval : perforationIntervals )
        {
            if ( !interval->isChecked() ) continue;

            std::vector<const RimWellPathValve*> perforationValves;
            interval->descendantsIncludingThisOfType( perforationValves );

            for ( const RimWellPathValve* valve : perforationValves )
            {
                if ( !valve->isChecked() ) continue;

                for ( size_t nSubValve = 0u; nSubValve < valve->valveLocations().size(); ++nSubValve )
                {
                    double valveMD = valve->valveLocations()[nSubValve];

                    std::pair<double, double> valveSegment = valve->valveSegments()[nSubValve];
                    double                    overlapStart = std::max( valveSegment.first, segment->startMD() );
                    double                    overlapEnd   = std::min( valveSegment.second, segment->endMD() );
                    double                    overlap      = std::max( 0.0, overlapEnd - overlapStart );

                    double exportStartMD = valveMD;
                    double exportEndMD   = valveMD + 0.1;

                    double exportStartTVD = RicMswTableFormatterTools::tvdFromMeasuredDepth( wellPath, exportStartMD );
                    double exportEndTVD   = RicMswTableFormatterTools::tvdFromMeasuredDepth( wellPath, exportEndMD );

                    if ( segment->startMD() <= valveMD && valveMD < segment->endMD() )
                    {
                        if ( valve->componentType() == RiaDefines::WellPathComponentType::AICD )
                        {
                            QString valveLabel = QString( "%1 #%2" ).arg( "Combined Valve for segment" ).arg( nMainSegment + 2 );
                            auto    subSegment =
                                std::make_unique<RicMswSegment>( "Valve segment", exportStartMD, exportEndMD, exportStartTVD, exportEndTVD );

                            superAICD = std::make_unique<RicMswPerforationAICD>( valveLabel, wellPath, exportStartMD, exportStartTVD, valve );
                            superAICD->addSegment( std::move( subSegment ) );
                        }
                        else if ( valve->componentType() == RiaDefines::WellPathComponentType::ICD )
                        {
                            QString valveLabel = QString( "%1 #%2" ).arg( "Combined Valve for segment" ).arg( nMainSegment + 2 );
                            auto    subSegment =
                                std::make_unique<RicMswSegment>( "Valve segment", exportStartMD, exportEndMD, exportStartTVD, exportEndTVD );

                            superICD = std::make_unique<RicMswPerforationICD>( valveLabel, wellPath, exportStartMD, exportStartTVD, valve );
                            superICD->addSegment( std::move( subSegment ) );
                        }
                        else if ( valve->componentType() == RiaDefines::WellPathComponentType::ICV )
                        {
                            QString valveLabel = QString( "ICV %1 at segment #%2" ).arg( valve->name() ).arg( nMainSegment + 2 );
                            auto    subSegment =
                                std::make_unique<RicMswSegment>( "Valve segment", exportStartMD, exportEndMD, exportStartTVD, exportEndTVD );

                            ICV = std::make_unique<RicMswPerforationICV>( valveLabel, wellPath, exportStartMD, exportStartTVD, valve );
                            ICV->addSegment( std::move( subSegment ) );
                        }
                    }
                    else if ( overlap > 0.0 && ( valve->componentType() == RiaDefines::WellPathComponentType::ICD && !superICD ) )
                    {
                        QString valveLabel = QString( "%1 #%2" ).arg( "Combined Valve for segment" ).arg( nMainSegment + 2 );

                        auto subSegment =
                            std::make_unique<RicMswSegment>( "Valve segment", exportStartMD, exportEndMD, exportStartTVD, exportEndTVD );
                        superICD = std::make_unique<RicMswPerforationICD>( valveLabel, wellPath, exportStartMD, exportStartTVD, valve );
                        superICD->addSegment( std::move( subSegment ) );
                    }
                    else if ( overlap > 0.0 && ( valve->componentType() == RiaDefines::WellPathComponentType::AICD && !superAICD ) )
                    {
                        QString valveLabel = QString( "%1 #%2" ).arg( "Combined Valve for segment" ).arg( nMainSegment + 2 );

                        auto subSegment =
                            std::make_unique<RicMswSegment>( "Valve segment", exportStartMD, exportEndMD, exportStartTVD, exportEndTVD );
                        superAICD = std::make_unique<RicMswPerforationAICD>( valveLabel, wellPath, exportStartMD, exportStartTVD, valve );
                        superAICD->addSegment( std::move( subSegment ) );
                    }

                    if ( valve->componentType() == RiaDefines::WellPathComponentType::AICD )
                    {
                        totalAICDOverlap += overlap;
                    }
                    else if ( valve->componentType() == RiaDefines::WellPathComponentType::ICD )
                    {
                        totalICDOverlap += overlap;
                    }
                }
            }
        }

        if ( ICV )
        {
            segment->addCompletion( std::move( ICV ) );
        }
        else
        {
            if ( totalICDOverlap > 0.0 || totalAICDOverlap > 0.0 )
            {
                if ( totalAICDOverlap > totalICDOverlap )
                {
                    segment->addCompletion( std::move( superAICD ) );
                }
                else
                {
                    segment->addCompletion( std::move( superICD ) );
                }
            }
        }
        nMainSegment++;
    }
}

//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
void RicWellPathExportMswCompletionsImpl::assignValveContributionsToSuperICDsOrAICDs(
    gsl::not_null<RicMswBranch*>                      branch,
    const std::vector<const RimPerforationInterval*>& perforationIntervals,
    const std::vector<WellPathCellIntersectionInfo>&  wellPathIntersections,
    const RigActiveCellInfo*                          activeCellInfo,
    RiaDefines::EclipseUnitSystem                     unitSystem )
{
    using ValveContributionMap = std::map<RicMswCompletion*, std::vector<const RimWellPathValve*>>;

    ValveContributionMap assignedRegularValves;

    std::map<RicMswSegment*, std::unique_ptr<RicMswValveAccumulator>> accumulators;

    for ( auto segment : branch->segments() )
    {
        RicMswValve* superValve = nullptr;
        for ( auto completion : segment->completions() )
        {
            auto valve = dynamic_cast<RicMswValve*>( completion );
            if ( valve )
            {
                superValve = valve;
                break;
            }
        }
        if ( dynamic_cast<RicMswPerforationICD*>( superValve ) )
        {
            accumulators[segment] = std::make_unique<RicMswICDAccumulator>( superValve, unitSystem );
        }
        else if ( dynamic_cast<RicMswPerforationAICD*>( superValve ) )
        {
            accumulators[segment] = std::make_unique<RicMswAICDAccumulator>( superValve, unitSystem );
        }
    }

    for ( const RimPerforationInterval* interval : perforationIntervals )
    {
        if ( !interval->isChecked() ) continue;

        std::vector<const RimWellPathValve*> perforationValves;
        interval->descendantsIncludingThisOfType( perforationValves );

        double totalPerforationLength = 0.0;
        for ( const RimWellPathValve* valve : perforationValves )
        {
            if ( !valve->isChecked() ) continue;

            for ( auto segment : branch->segments() )
            {
                double intervalOverlapStart = std::max( interval->startMD(), segment->startMD() );
                double intervalOverlapEnd   = std::min( interval->endMD(), segment->endMD() );
                auto   intervalOverlapWithActiveCells =
                    calculateOverlapWithActiveCells( intervalOverlapStart, intervalOverlapEnd, wellPathIntersections, activeCellInfo );

                totalPerforationLength += intervalOverlapWithActiveCells.second - intervalOverlapWithActiveCells.first;
            }
        }

        for ( const RimWellPathValve* valve : perforationValves )
        {
            if ( !valve->isChecked() ) continue;

            for ( auto segment : branch->segments() )
            {
                double intervalOverlapStart = std::max( interval->startMD(), segment->startMD() );
                double intervalOverlapEnd   = std::min( interval->endMD(), segment->endMD() );

                auto intervalOverlapWithActiveCells =
                    calculateOverlapWithActiveCells( intervalOverlapStart, intervalOverlapEnd, wellPathIntersections, activeCellInfo );

                double overlapLength = intervalOverlapWithActiveCells.second - intervalOverlapWithActiveCells.first;
                if ( overlapLength > 0.0 )
                {
                    auto it = accumulators.find( segment );

                    if ( it != accumulators.end() )
                    {
                        it->second->accumulateValveParameters( valve, overlapLength, totalPerforationLength );
                        assignedRegularValves[it->second->superValve()].push_back( valve );
                    }
                }
            }
        }
    }

    for ( const auto& accumulator : accumulators )
    {
        accumulator.second->applyToSuperValve();
    }

    for ( auto regularValvePair : assignedRegularValves )
    {
        if ( !regularValvePair.second.empty() )
        {
            QStringList valveLabels;
            for ( const RimWellPathValve* regularValve : regularValvePair.second )
            {
                QString valveLabel = QString( "%1" ).arg( regularValve->name() );
                valveLabels.push_back( valveLabel );
            }
            QString valveContribLabel = QString( " with contribution from: %1" ).arg( valveLabels.join( ", " ) );
            regularValvePair.first->setLabel( regularValvePair.first->label() + valveContribLabel );
        }
    }
}

//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
void RicWellPathExportMswCompletionsImpl::moveIntersectionsToICVs( gsl::not_null<RicMswBranch*>                      branch,
                                                                   const std::vector<const RimPerforationInterval*>& perforationIntervals,
                                                                   RiaDefines::EclipseUnitSystem                     unitSystem )
{
    std::map<const RimWellPathValve*, RicMswPerforationICV*> icvCompletionMap;

    for ( auto segment : branch->segments() )
    {
        for ( auto completion : segment->completions() )
        {
            auto icv = dynamic_cast<RicMswPerforationICV*>( completion );
            if ( icv )
            {
                icvCompletionMap[icv->wellPathValve()] = icv;
            }
        }
    }

    for ( auto segment : branch->segments() )
    {
        std::vector<RicMswCompletion*> perforations;
        for ( auto completion : segment->completions() )
        {
            if ( completion->completionType() == RigCompletionData::CompletionType::PERFORATION )
            {
                perforations.push_back( completion );
            }
        }

        for ( const RimPerforationInterval* interval : perforationIntervals )
        {
            if ( !interval->isChecked() ) continue;

            std::vector<const RimWellPathValve*> perforationValves;
            interval->descendantsIncludingThisOfType( perforationValves );

            for ( const RimWellPathValve* valve : perforationValves )
            {
                if ( !valve->isChecked() ) continue;
                if ( valve->componentType() != RiaDefines::WellPathComponentType::ICV ) continue;

                auto icvIt = icvCompletionMap.find( valve );
                if ( icvIt == icvCompletionMap.end() ) continue;

                auto icvCompletion = icvIt->second;
                CVF_ASSERT( icvCompletion );

                std::pair<double, double> valveSegment = valve->valveSegments().front();
                double                    overlapStart = std::max( valveSegment.first, segment->startMD() );
                double                    overlapEnd   = std::min( valveSegment.second, segment->endMD() );
                double                    overlap      = std::max( 0.0, overlapEnd - overlapStart );

                if ( overlap > 0.0 )
                {
                    CVF_ASSERT( icvCompletion->segments().size() == 1u );
                    for ( auto perforationPtr : perforations )
                    {
                        for ( auto subSegmentPtr : perforationPtr->segments() )
                        {
                            for ( auto intersectionPtr : subSegmentPtr->intersections() )
                            {
                                icvCompletion->segments()[0]->addIntersection( intersectionPtr );
                            }
                        }
                        segment->removeCompletion( perforationPtr );
                    }
                }
            }
        }
    }
}

//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
void RicWellPathExportMswCompletionsImpl::moveIntersectionsToSuperICDsOrAICDs( gsl::not_null<RicMswBranch*> branch )
{
    for ( auto segment : branch->segments() )
    {
        RicMswCompletion*              superValve = nullptr;
        std::vector<RicMswCompletion*> perforations;
        for ( auto completion : segment->completions() )
        {
            if ( completion->completionType() == RigCompletionData::CompletionType::PERFORATION_ICD ||
                 completion->completionType() == RigCompletionData::CompletionType::PERFORATION_AICD )
            {
                superValve = completion;
            }
            else if ( completion->completionType() == RigCompletionData::CompletionType::PERFORATION )
            {
                perforations.push_back( completion );
            }
        }

        if ( superValve == nullptr ) continue;

        CVF_ASSERT( superValve->segments().size() == 1u );

        // Remove and take over ownership of the superValve completion
        auto completionPtr = segment->removeCompletion( superValve );
        for ( auto perforation : perforations )
        {
            for ( auto subSegment : perforation->segments() )
            {
                // The valve completions on the main branch will be deleted. Create a segment with startMD and
                // endMD representing the perforation along main well path to be connected to the valve. When COMPSEGS
                // data is exported, the startMD and endMD of the segment is used to define the Start Length and End
                // Length of the COMPSEGS keyword
                //
                // Example output
                //
                // COMPSEGS
                // --Name
                //     Well - 1 /
                // --I      J      K      Branch no     Start Length     End Length
                //   17     17     9      2             3030.71791       3034.01331 /
                //   17     18     9      3             3034.01331       3125.47617 /

                auto valveInflowSegment = std::make_unique<RicMswSegment>( QString( "%1 real valve segment " ).arg( branch->label() ),
                                                                           subSegment->startMD(),
                                                                           subSegment->endMD(),
                                                                           subSegment->startTVD(),
                                                                           subSegment->endTVD() );

                for ( auto intersectionPtr : subSegment->intersections() )
                {
                    valveInflowSegment->addIntersection( intersectionPtr );
                }

                {
                    double midpoint = ( segment->startMD() + segment->endMD() ) * 0.5;

                    // Set the output MD to the midpoint of the segment, this info is used when exporting WELSEGS in
                    // RicMswTableFormatterTools::writeValveWelsegsSegment
                    completionPtr->segments()[0]->setOutputMD( midpoint );
                }
                completionPtr->addSegment( std::move( valveInflowSegment ) );
            }
        }

        // Remove all completions and re-add the super valve
        segment->deleteAllCompletions();
        segment->addCompletion( std::move( completionPtr ) );
    }
}

//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
void RicWellPathExportMswCompletionsImpl::assignFishbonesLateralIntersections( const RimEclipseCase*         eclipseCase,
                                                                               const RimWellPath*            wellPath,
                                                                               const RimFishbones*           fishbonesSubs,
                                                                               gsl::not_null<RicMswSegment*> segment,
                                                                               bool*                         foundSubGridIntersections,
                                                                               double                        maxSegmentLength,
                                                                               RiaDefines::EclipseUnitSystem unitSystem )
{
    CVF_ASSERT( foundSubGridIntersections != nullptr );

    const RigMainGrid* grid = eclipseCase->eclipseCaseData()->mainGrid();

    for ( auto completion : segment->completions() )
    {
        if ( completion->completionType() != RigCompletionData::CompletionType::FISHBONES )
        {
            continue;
        }

        std::vector<std::pair<cvf::Vec3d, double>> lateralCoordMDPairs =
            fishbonesSubs->coordsAndMDForLateral( segment->subIndex(), completion->index() );

        if ( lateralCoordMDPairs.empty() )
        {
            continue;
        }

        std::vector<cvf::Vec3d> lateralCoords;
        std::vector<double>     lateralMDs;

        lateralCoords.reserve( lateralCoordMDPairs.size() );
        lateralMDs.reserve( lateralCoordMDPairs.size() );

        for ( auto& coordMD : lateralCoordMDPairs )
        {
            lateralCoords.push_back( coordMD.first );
            lateralMDs.push_back( coordMD.second );
        }

        std::vector<WellPathCellIntersectionInfo> intersections =
            RigWellPathIntersectionTools::findCellIntersectionInfosAlongPath( eclipseCase->eclipseCaseData(),
                                                                              wellPath->name(),
                                                                              lateralCoords,
                                                                              lateralMDs );

        RigWellPath pathGeometry( lateralCoords, lateralMDs );

        double previousExitMD  = lateralMDs.front();
        double previousExitTVD = -lateralCoords.front().z();

        for ( const auto& cellIntInfo : intersections )
        {
            size_t             localGridCellIndex = 0u;
            const RigGridBase* localGrid = grid->gridAndGridLocalIdxFromGlobalCellIdx( cellIntInfo.globCellIndex, &localGridCellIndex );
            QString            gridName;
            if ( localGrid != grid )
            {
                gridName                   = QString::fromStdString( localGrid->gridName() );
                *foundSubGridIntersections = true;
            }

            size_t i = 0u, j = 0u, k = 0u;
            localGrid->ijkFromCellIndex( localGridCellIndex, &i, &j, &k );
            auto subSegment = std::make_unique<RicMswSegment>( "Sub segment",
                                                               previousExitMD,
                                                               cellIntInfo.endMD,
                                                               previousExitTVD,
                                                               cellIntInfo.endTVD(),
                                                               segment->subIndex() );

            subSegment->setEquivalentDiameter( fishbonesSubs->equivalentDiameter( unitSystem ) );
            subSegment->setHoleDiameter( fishbonesSubs->holeDiameter( unitSystem ) );
            subSegment->setOpenHoleRoughnessFactor( fishbonesSubs->openHoleRoughnessFactor( unitSystem ) );
            subSegment->setSkinFactor( fishbonesSubs->skinFactor() );
            subSegment->setSourcePdmObject( fishbonesSubs );
            subSegment->setIntersectedGlobalCells( { cellIntInfo.globCellIndex } );

            auto intersection = std::make_shared<RicMswSegmentCellIntersection>( gridName,
                                                                                 cellIntInfo.globCellIndex,
                                                                                 cvf::Vec3st( i, j, k ),
                                                                                 cellIntInfo.intersectionLengthsInCellCS );
            subSegment->addIntersection( std::move( intersection ) );
            completion->addSegment( std::move( subSegment ) );

            previousExitMD  = cellIntInfo.endMD;
            previousExitTVD = cellIntInfo.endTVD();
        }
    }
}

//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
void RicWellPathExportMswCompletionsImpl::assignFractureCompletionsToCellSegment( const RimEclipseCase*                 eclipseCase,
                                                                                  const RimWellPath*                    wellPath,
                                                                                  const RimWellPathFracture*            fracture,
                                                                                  const std::vector<RigCompletionData>& completionData,
                                                                                  gsl::not_null<RicMswSegment*>         segment,
                                                                                  bool* foundSubGridIntersections )
{
    CVF_ASSERT( foundSubGridIntersections != nullptr );

    double position = fracture->fractureMD();
    double width    = fracture->fractureTemplate()->computeFractureWidth( fracture );

    auto fractureCompletion = std::make_unique<RicMswFracture>( fracture->name(), wellPath, position, position + width );

    if ( fracture->fractureTemplate()->orientationType() == RimFractureTemplate::ALONG_WELL_PATH )
    {
        double perforationLength = fracture->fractureTemplate()->perforationLength();
        position -= 0.5 * perforationLength;
        width = perforationLength;
    }

    auto subSegment = std::make_unique<RicMswSegment>( "Fracture segment", position, position + width, 0.0, 0.0 );
    for ( const RigCompletionData& compIntersection : completionData )
    {
        const RigCompletionDataGridCell& cell = compIntersection.completionDataGridCell();

        if ( !cell.isMainGridCell() )
        {
            *foundSubGridIntersections = true;
        }

        cvf::Vec3st localIJK( cell.localCellIndexI(), cell.localCellIndexJ(), cell.localCellIndexK() );

        auto intersection =
            std::make_shared<RicMswSegmentCellIntersection>( cell.lgrName(), cell.globalCellIndex(), localIJK, cvf::Vec3d::ZERO );
        subSegment->addIntersection( intersection );
    }
    fractureCompletion->addSegment( std::move( subSegment ) );
    segment->addCompletion( std::move( fractureCompletion ) );
}

//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
std::vector<RigCompletionData>
    RicWellPathExportMswCompletionsImpl::generatePerforationIntersections( gsl::not_null<const RimWellPath*>            wellPath,
                                                                           gsl::not_null<const RimPerforationInterval*> perforationInterval,
                                                                           int                                          timeStep,
                                                                           gsl::not_null<const RimEclipseCase*>         eclipseCase )
{
    std::vector<RigCompletionData> completionData;
    const RigActiveCellInfo* activeCellInfo = eclipseCase->eclipseCaseData()->activeCellInfo( RiaDefines::PorosityModelType::MATRIX_MODEL );

    auto wellPathGeometry = wellPath->wellPathGeometry();
    CVF_ASSERT( wellPathGeometry );
    bool hasDate  = (size_t)timeStep < eclipseCase->timeStepDates().size();
    bool isActive = !hasDate || perforationInterval->isActiveOnDate( eclipseCase->timeStepDates()[timeStep] );

    if ( wellPath->perforationIntervalCollection()->isChecked() && perforationInterval->isChecked() && isActive )
    {
        std::pair<std::vector<cvf::Vec3d>, std::vector<double>> perforationPointsAndMD =
            wellPathGeometry->clippedPointSubset( perforationInterval->startMD(), perforationInterval->endMD() );

        std::vector<WellPathCellIntersectionInfo> intersectedCells =
            RigWellPathIntersectionTools::findCellIntersectionInfosAlongPath( eclipseCase->eclipseCaseData(),
                                                                              wellPath->name(),
                                                                              perforationPointsAndMD.first,
                                                                              perforationPointsAndMD.second );

        for ( auto& cell : intersectedCells )
        {
            bool cellIsActive = activeCellInfo->isActive( cell.globCellIndex );
            if ( !cellIsActive ) continue;

            RigCompletionData completion( wellPath->completionSettings()->wellNameForExport(),
                                          RigCompletionDataGridCell( cell.globCellIndex, eclipseCase->mainGrid() ),
                                          cell.startMD );

            completion.setSourcePdmObject( perforationInterval );
            completionData.push_back( completion );
        }
    }

    return completionData;
}

//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
void RicWellPathExportMswCompletionsImpl::assignPerforationIntersections( const std::vector<RigCompletionData>& completionData,
                                                                          gsl::not_null<RicMswCompletion*>      perforationCompletion,
                                                                          const WellPathCellIntersectionInfo&   cellIntInfo,
                                                                          double                                overlapStart,
                                                                          double                                overlapEnd,
                                                                          bool*                                 foundSubGridIntersections )
{
    size_t currCellId = cellIntInfo.globCellIndex;

    auto subSegment =
        std::make_unique<RicMswSegment>( "Perforation segment", overlapStart, overlapEnd, cellIntInfo.startTVD(), cellIntInfo.endTVD() );
    for ( const RigCompletionData& compIntersection : completionData )
    {
        const RigCompletionDataGridCell& cell = compIntersection.completionDataGridCell();
        if ( !cell.isMainGridCell() )
        {
            *foundSubGridIntersections = true;
        }

        if ( cell.globalCellIndex() != currCellId ) continue;

        cvf::Vec3st localIJK( cell.localCellIndexI(), cell.localCellIndexJ(), cell.localCellIndexK() );

        auto intersection = std::make_shared<RicMswSegmentCellIntersection>( cell.lgrName(),
                                                                             cell.globalCellIndex(),
                                                                             localIJK,
                                                                             cellIntInfo.intersectionLengthsInCellCS );
        subSegment->addIntersection( intersection );
    }
    perforationCompletion->addSegment( std::move( subSegment ) );
}

//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
void RicWellPathExportMswCompletionsImpl::assignBranchNumbersToPerforations( const RimEclipseCase*         eclipseCase,
                                                                             gsl::not_null<RicMswSegment*> segment,
                                                                             gsl::not_null<int*>           branchNumber )
{
    for ( auto completion : segment->completions() )
    {
        if ( completion->completionType() == RigCompletionData::CompletionType::PERFORATION )
        {
            completion->setBranchNumber( *branchNumber );
        }
    }
}

//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
void RicWellPathExportMswCompletionsImpl::assignBranchNumbersToOtherCompletions( const RimEclipseCase*         eclipseCase,
                                                                                 gsl::not_null<RicMswSegment*> segment,
                                                                                 gsl::not_null<int*>           branchNumber )
{
    for ( auto completion : segment->completions() )
    {
        if ( completion->completionType() != RigCompletionData::CompletionType::PERFORATION )
        {
            completion->setBranchNumber( ++( *branchNumber ) );

            for ( auto seg : completion->segments() )
            {
                assignBranchNumbersToOtherCompletions( eclipseCase, seg, branchNumber );
            }
        }
    }
}

//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
void RicWellPathExportMswCompletionsImpl::assignBranchNumbersToBranch( const RimEclipseCase*        eclipseCase,
                                                                       RicMswExportInfo*            exportInfo,
                                                                       gsl::not_null<RicMswBranch*> branch,
                                                                       gsl::not_null<int*>          branchNumber )
{
    branch->setBranchNumber( *branchNumber );

    // Assign perforations first to ensure the same branch number as the segment
    for ( auto segment : branch->segments() )
    {
        assignBranchNumbersToPerforations( eclipseCase, segment, branchNumber );
    }

    // Assign other completions with an incremented branch number
    for ( auto segment : branch->segments() )
    {
        assignBranchNumbersToOtherCompletions( eclipseCase, segment, branchNumber );
    }

    ( *branchNumber )++;

    for ( auto childBranch : branch->branches() )
    {
        assignBranchNumbersToBranch( eclipseCase, exportInfo, childBranch, branchNumber );
    }
}

//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
std::unique_ptr<RicMswBranch> RicWellPathExportMswCompletionsImpl::createChildMswBranch( const RimWellPath* childWellPath )
{
    auto initialChildMD  = childWellPath->wellPathTieIn()->tieInMeasuredDepth();
    auto initialChildTVD = -childWellPath->wellPathGeometry()->interpolatedPointAlongWellPath( initialChildMD ).z();

    const RimWellPathValve* outletValve = childWellPath->wellPathTieIn()->outletValve();
    if ( outletValve )
    {
        auto branchStartingWithValve =
            RicMswValve::createTieInValve( QString( "%1 valve for %2" ).arg( outletValve->componentLabel() ).arg( childWellPath->name() ),
                                           childWellPath,
                                           initialChildMD,
                                           initialChildTVD,
                                           outletValve );
        if ( branchStartingWithValve )
        {
            auto dummySegment =
                std::make_unique<RicMswSegment>( QString( "%1 segment" ).arg( outletValve->componentLabel() ),
                                                 initialChildMD,
                                                 initialChildMD + 0.1,
                                                 initialChildTVD,
                                                 RicMswTableFormatterTools::tvdFromMeasuredDepth( childWellPath, initialChildMD + 0.1 ) );
            branchStartingWithValve->addSegment( std::move( dummySegment ) );

            return branchStartingWithValve;
        }
    }

    auto childBranch = std::make_unique<RicMswBranch>( childWellPath->name(), childWellPath, initialChildMD, initialChildTVD );

    return childBranch;
}

//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
std::vector<RimWellPath*> RicWellPathExportMswCompletionsImpl::wellPathsWithTieIn( const RimWellPath* wellPath )
{
    std::vector<RimWellPath*> connectedWellPaths;
    {
        auto wellPaths = RimProject::current()->allWellPaths();
        for ( auto well : wellPaths )
        {
            if ( well && well->isEnabled() && well->wellPathTieIn() && well->wellPathTieIn()->parentWell() == wellPath )
            {
                connectedWellPaths.push_back( well );
            }
        }
    }

    return connectedWellPaths;
}

//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
std::pair<double, double>
    RicWellPathExportMswCompletionsImpl::calculateOverlapWithActiveCells( double startMD,
                                                                          double endMD,
                                                                          const std::vector<WellPathCellIntersectionInfo>& wellPathIntersections,
                                                                          const RigActiveCellInfo* activeCellInfo )
{
    for ( const WellPathCellIntersectionInfo& intersection : wellPathIntersections )
    {
        if ( intersection.globCellIndex < activeCellInfo->reservoirCellCount() && activeCellInfo->isActive( intersection.globCellIndex ) )
        {
            double overlapStart = std::max( startMD, intersection.startMD );
            double overlapEnd   = std::min( endMD, intersection.endMD );
            if ( overlapEnd > overlapStart )
            {
                return std::make_pair( overlapStart, overlapEnd );
            }
        }
    }
    return std::make_pair( 0.0, 0.0 );
}
