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

#include "RivFaultPartMgr.h"

#include "RiaGuiApplication.h"
#include "RiaPreferences.h"

#include "RigCaseCellResultsData.h"
#include "RigMainGrid.h"
#include "RigNNCData.h"
#include "RigResultAccessor.h"

#include "RimEclipseCase.h"
#include "RimEclipseCellColors.h"
#include "RimEclipseView.h"
#include "RimFaultInView.h"
#include "RimFaultInViewCollection.h"
#include "RimRegularLegendConfig.h"
#include "RimTernaryLegendConfig.h"

#include "RivFaultGeometryGenerator.h"
#include "RivMeshLinesSourceInfo.h"
#include "RivNNCGeometryGenerator.h"
#include "RivPartPriority.h"
#include "RivResultToTextureMapper.h"
#include "RivScalarMapperUtils.h"
#include "RivSourceInfo.h"
#include "RivTernaryScalarMapper.h"
#include "RivTernaryTextureCoordsCreator.h"
#include "RivTextLabelSourceInfo.h"
#include "RivTextureCoordsCreator.h"

#include "cvfDrawableGeo.h"
#include "cvfDrawableText.h"
#include "cvfModelBasicList.h"
#include "cvfPart.h"
#include "cvfPrimitiveSetDirect.h"
#include "cvfqtUtils.h"

//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
RivFaultPartMgr::RivFaultPartMgr( const RigGridBase* grid, const RimFaultInViewCollection* rimFaultCollection, RimFaultInView* rimFault )
    : m_grid( grid )
    , m_rimFaultCollection( rimFaultCollection )
    , m_rimFault( rimFault )
    , m_opacityLevel( 1.0f )
    , m_defaultColor( cvf::Color3::WHITE )
    , m_isNativeFaultsGenerated( false )
    , m_isOppositeFaultsGenerated( false )
    , m_isNativeNncsGenerated( false )
    , m_isAllNncsGenerated( false )

{
    CVF_ASSERT( rimFault->faultGeometry() );

    m_nativeFaultGenerator = new RivFaultGeometryGenerator( grid, rimFault->faultGeometry(), grid->mainGrid()->nncData(), true );

    m_oppositeFaultGenerator = new RivFaultGeometryGenerator( grid, rimFault->faultGeometry(), grid->mainGrid()->nncData(), false );

    m_nativeFaultFacesTextureCoords   = new cvf::Vec2fArray;
    m_oppositeFaultFacesTextureCoords = new cvf::Vec2fArray;

    m_NNCTextureCoords = new cvf::Vec2fArray;

    m_allanNNCTextureCoords = new cvf::Vec2fArray;
}

//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
void RivFaultPartMgr::setCellVisibility( cvf::UByteArray* cellVisibilities )
{
    m_nativeFaultGenerator->setCellVisibility( cellVisibilities );
    m_oppositeFaultGenerator->setCellVisibility( cellVisibilities );

    if ( m_NNCGenerator.notNull() ) m_NNCGenerator->setCellVisibility( cellVisibilities, m_grid.p() );
    if ( m_allanNNCGenerator.notNull() ) m_allanNNCGenerator->setCellVisibility( cellVisibilities, m_grid.p() );

    clearFlags();
}

//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
void RivFaultPartMgr::applySingleColorEffect()
{
    m_defaultColor = m_rimFault->faultColor();
    this->updatePartEffect();
}

//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
void RivFaultPartMgr::updateCellResultColor( size_t timeStepIndex, RimEclipseCellColors* cellResultColors )
{
    CVF_ASSERT( cellResultColors );

    updateNNCColors( timeStepIndex, cellResultColors );

    RimEclipseView* eclipseView = cellResultColors->reservoirView();

    // Faults
    if ( m_nativeFaultFaces.notNull() )
    {
        if ( cellResultColors->isTernarySaturationSelected() )
        {
            RivTernaryTextureCoordsCreator texturer( cellResultColors,
                                                     cellResultColors->ternaryLegendConfig(),
                                                     eclipseView->wellCollection(),
                                                     timeStepIndex,
                                                     m_grid->gridIndex(),
                                                     m_nativeFaultGenerator->quadToCellFaceMapper() );

            texturer.createTextureCoords( m_nativeFaultFacesTextureCoords.p() );

            const RivTernaryScalarMapper* mapper = cellResultColors->ternaryLegendConfig()->scalarMapper();
            RivScalarMapperUtils::applyTernaryTextureResultsToPart( m_nativeFaultFaces.p(),
                                                                    m_nativeFaultFacesTextureCoords.p(),
                                                                    mapper,
                                                                    m_opacityLevel,
                                                                    this->faceCullingMode(),
                                                                    eclipseView->isLightingDisabled() );
        }
        else
        {
            RivTextureCoordsCreator texturer( cellResultColors,
                                              timeStepIndex,
                                              m_grid->gridIndex(),
                                              m_nativeFaultGenerator->quadToCellFaceMapper() );

            if ( !texturer.isValid() )
            {
                return;
            }

            texturer.createTextureCoords( m_nativeFaultFacesTextureCoords.p() );

            const cvf::ScalarMapper* mapper = cellResultColors->legendConfig()->scalarMapper();
            RivScalarMapperUtils::applyTextureResultsToPart( m_nativeFaultFaces.p(),
                                                             m_nativeFaultFacesTextureCoords.p(),
                                                             mapper,
                                                             m_opacityLevel,
                                                             this->faceCullingMode(),
                                                             eclipseView->isLightingDisabled() );
        }
    }

    if ( m_oppositeFaultFaces.notNull() )
    {
        if ( cellResultColors->isTernarySaturationSelected() )
        {
            RivTernaryTextureCoordsCreator texturer( cellResultColors,
                                                     cellResultColors->ternaryLegendConfig(),
                                                     eclipseView->wellCollection(),
                                                     timeStepIndex,
                                                     m_grid->gridIndex(),
                                                     m_oppositeFaultGenerator->quadToCellFaceMapper() );

            texturer.createTextureCoords( m_oppositeFaultFacesTextureCoords.p() );

            const RivTernaryScalarMapper* mapper = cellResultColors->ternaryLegendConfig()->scalarMapper();
            RivScalarMapperUtils::applyTernaryTextureResultsToPart( m_oppositeFaultFaces.p(),
                                                                    m_oppositeFaultFacesTextureCoords.p(),
                                                                    mapper,
                                                                    m_opacityLevel,
                                                                    this->faceCullingMode(),
                                                                    eclipseView->isLightingDisabled() );
        }
        else
        {
            RivTextureCoordsCreator texturer( cellResultColors,
                                              timeStepIndex,
                                              m_grid->gridIndex(),
                                              m_oppositeFaultGenerator->quadToCellFaceMapper() );

            if ( !texturer.isValid() )
            {
                return;
            }

            texturer.createTextureCoords( m_oppositeFaultFacesTextureCoords.p() );

            const cvf::ScalarMapper* mapper = cellResultColors->legendConfig()->scalarMapper();
            RivScalarMapperUtils::applyTextureResultsToPart( m_oppositeFaultFaces.p(),
                                                             m_oppositeFaultFacesTextureCoords.p(),
                                                             mapper,
                                                             m_opacityLevel,
                                                             this->faceCullingMode(),
                                                             eclipseView->isLightingDisabled() );
        }
    }
}

//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
void RivFaultPartMgr::updateCellEdgeResultColor( size_t                timeStepIndex,
                                                 RimEclipseCellColors* cellResultColors,
                                                 RimCellEdgeColors*    cellEdgeResultColors )
{
    updateNNCColors( timeStepIndex, cellResultColors );

    if ( m_nativeFaultFaces.notNull() )
    {
        cvf::DrawableGeo* dg = dynamic_cast<cvf::DrawableGeo*>( m_nativeFaultFaces->drawable() );
        if ( dg )
        {
            cvf::ref<cvf::Effect> eff = RivScalarMapperUtils::createCellEdgeEffect( dg,
                                                                                    m_nativeFaultGenerator->quadToCellFaceMapper(),
                                                                                    m_grid->gridIndex(),
                                                                                    timeStepIndex,
                                                                                    cellResultColors,
                                                                                    cellEdgeResultColors,
                                                                                    m_opacityLevel,
                                                                                    m_defaultColor,
                                                                                    this->faceCullingMode(),
                                                                                    cellResultColors->reservoirView()->isLightingDisabled() );

            m_nativeFaultFaces->setEffect( eff.p() );
        }
    }

    if ( m_oppositeFaultFaces.notNull() )
    {
        cvf::DrawableGeo* dg = dynamic_cast<cvf::DrawableGeo*>( m_oppositeFaultFaces->drawable() );
        if ( dg )
        {
            cvf::ref<cvf::Effect> eff = RivScalarMapperUtils::createCellEdgeEffect( dg,
                                                                                    m_oppositeFaultGenerator->quadToCellFaceMapper(),
                                                                                    m_grid->gridIndex(),
                                                                                    timeStepIndex,
                                                                                    cellResultColors,
                                                                                    cellEdgeResultColors,
                                                                                    m_opacityLevel,
                                                                                    m_defaultColor,
                                                                                    this->faceCullingMode(),
                                                                                    cellResultColors->reservoirView()->isLightingDisabled() );

            m_oppositeFaultFaces->setEffect( eff.p() );
        }
    }
}

//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
void RivFaultPartMgr::generatePartGeometry()
{
    bool useBufferObjects = true;
    // Surface geometry
    {
        cvf::ref<cvf::DrawableGeo> geo = m_nativeFaultGenerator->generateSurface( m_rimFaultCollection->onlyShowFacesWithDefinedNeighbor() );
        if ( geo.notNull() )
        {
            geo->computeNormals();

            if ( useBufferObjects )
            {
                geo->setRenderMode( cvf::DrawableGeo::BUFFER_OBJECT );
            }

            cvf::ref<cvf::Part> part = new cvf::Part;
            part->setName( "Grid " + cvf::String( static_cast<int>( m_grid->gridIndex() ) ) );
            part->setDrawable( geo.p() );

            // Set mapping from triangle face index to cell index
            cvf::ref<RivSourceInfo> si       = new RivSourceInfo( m_rimFault, m_grid->gridIndex() );
            si->m_cellFaceFromTriangleMapper = m_nativeFaultGenerator->triangleToCellFaceMapper();
            part->setSourceInfo( si.p() );

            part->updateBoundingBox();
            part->setEnableMask( faultBit );
            part->setPriority( RivPartPriority::PartType::Fault );

            m_nativeFaultFaces = part;
        }
    }

    // Mesh geometry
    {
        cvf::ref<cvf::DrawableGeo> geoMesh = m_nativeFaultGenerator->createMeshDrawable();
        if ( geoMesh.notNull() )
        {
            if ( useBufferObjects )
            {
                geoMesh->setRenderMode( cvf::DrawableGeo::BUFFER_OBJECT );
            }

            cvf::ref<cvf::Part> part = new cvf::Part;
            part->setName( "Grid mesh" + cvf::String( static_cast<int>( m_grid->gridIndex() ) ) );
            part->setDrawable( geoMesh.p() );

            part->updateBoundingBox();
            part->setEnableMask( meshFaultBit );
            part->setPriority( RivPartPriority::PartType::FaultMeshLines );

            part->setSourceInfo( new RivMeshLinesSourceInfo( m_rimFault ) );

            m_nativeFaultGridLines = part;
        }
    }

    // Surface geometry
    {
        cvf::ref<cvf::DrawableGeo> geo = m_oppositeFaultGenerator->generateSurface( m_rimFaultCollection->onlyShowFacesWithDefinedNeighbor() );
        if ( geo.notNull() )
        {
            geo->computeNormals();

            if ( useBufferObjects )
            {
                geo->setRenderMode( cvf::DrawableGeo::BUFFER_OBJECT );
            }

            cvf::ref<cvf::Part> part = new cvf::Part;
            part->setName( "Grid " + cvf::String( static_cast<int>( m_grid->gridIndex() ) ) );
            part->setDrawable( geo.p() );

            // Set mapping from triangle face index to cell index
            cvf::ref<RivSourceInfo> si       = new RivSourceInfo( m_rimFault, m_grid->gridIndex() );
            si->m_cellFaceFromTriangleMapper = m_oppositeFaultGenerator->triangleToCellFaceMapper();
            part->setSourceInfo( si.p() );

            part->updateBoundingBox();
            part->setEnableMask( faultBit );
            part->setPriority( RivPartPriority::PartType::Fault );

            m_oppositeFaultFaces = part;
        }
    }

    // Mesh geometry
    {
        cvf::ref<cvf::DrawableGeo> geoMesh = m_oppositeFaultGenerator->createMeshDrawable();
        if ( geoMesh.notNull() )
        {
            if ( useBufferObjects )
            {
                geoMesh->setRenderMode( cvf::DrawableGeo::BUFFER_OBJECT );
            }

            cvf::ref<cvf::Part> part = new cvf::Part;
            part->setName( "Grid mesh" + cvf::String( static_cast<int>( m_grid->gridIndex() ) ) );
            part->setDrawable( geoMesh.p() );

            part->updateBoundingBox();
            part->setEnableMask( meshFaultBit );
            part->setPriority( RivPartPriority::PartType::FaultMeshLines );

            part->setSourceInfo( new RivMeshLinesSourceInfo( m_rimFault ) );

            m_oppositeFaultGridLines = part;
        }
    }

    m_isNativeFaultsGenerated   = true;
    m_isOppositeFaultsGenerated = true;

    createLabelWithAnchorLine( m_nativeFaultFaces.p() );

    updatePartEffect();
}

//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
void RivFaultPartMgr::generateNativeNncPartGeometry()
{
    if ( m_NNCGenerator.isNull() )
    {
        auto nncConnectionIndices = m_rimFault->faultGeometry()->connectionIndices();
        if ( !nncConnectionIndices.empty() )
        {
            m_NNCGenerator = new RivNNCGeometryGenerator( false,
                                                          m_grid->mainGrid()->nncData(),
                                                          m_grid->mainGrid()->displayModelOffset(),
                                                          nncConnectionIndices );
        }
    }

    if ( m_NNCGenerator.isNull() ) return;

    cvf::ref<cvf::DrawableGeo> geo = m_NNCGenerator->generateSurface();
    if ( geo.notNull() )
    {
        geo->computeNormals();
        geo->setRenderMode( cvf::DrawableGeo::BUFFER_OBJECT );

        cvf::ref<cvf::Part> part = new cvf::Part;
        part->setName( "NNC in Fault. Grid " + cvf::String( static_cast<int>( m_grid->gridIndex() ) ) );
        part->setDrawable( geo.p() );

        // Set mapping from triangle face index to cell index
        cvf::ref<RivSourceInfo> si = new RivSourceInfo( m_rimFault, m_grid->gridIndex() );
        si->m_NNCIndices           = m_NNCGenerator->triangleToNNCIndex().p();
        part->setSourceInfo( si.p() );

        part->updateBoundingBox();
        part->setEnableMask( faultBit );
        part->setPriority( RivPartPriority::PartType::Nnc );

        cvf::ref<cvf::Effect> eff = new cvf::Effect;
        part->setEffect( eff.p() );

        m_NNCFaces = part;

        updatePartEffect();
    }

    m_isNativeNncsGenerated = true;
}

//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
void RivFaultPartMgr::generateAllNncPartGeometry()
{
    if ( m_allanNNCGenerator.isNull() )
    {
        auto nncConnectionIndices = m_rimFault->faultGeometry()->connectionIndices();
        if ( !nncConnectionIndices.empty() )
        {
            m_allanNNCGenerator =
                new RivNNCGeometryGenerator( true, m_grid->mainGrid()->nncData(), m_grid->mainGrid()->displayModelOffset(), nncConnectionIndices );
        }
    }

    if ( m_allanNNCGenerator.isNull() ) return;

    cvf::ref<cvf::DrawableGeo> geo = m_allanNNCGenerator->generateSurface();
    if ( geo.notNull() )
    {
        geo->computeNormals();

        geo->setRenderMode( cvf::DrawableGeo::BUFFER_OBJECT );

        cvf::ref<cvf::Part> part = new cvf::Part;
        part->setName( "Allan NNC in Fault. Grid " + cvf::String( static_cast<int>( m_grid->gridIndex() ) ) );
        part->setDrawable( geo.p() );

        // Set mapping from triangle face index to cell index
        cvf::ref<RivSourceInfo> si = new RivSourceInfo( m_rimFault, m_grid->gridIndex() );
        si->m_NNCIndices           = m_allanNNCGenerator->triangleToNNCIndex().p();
        part->setSourceInfo( si.p() );

        part->updateBoundingBox();
        part->setEnableMask( faultBit );
        part->setPriority( RivPartPriority::PartType::Nnc );

        cvf::ref<cvf::Effect> eff = new cvf::Effect;
        part->setEffect( eff.p() );

        m_allanNNCFaces = part;

        updatePartEffect();
    }

    m_isAllNncsGenerated = true;
}

//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
void RivFaultPartMgr::clearFlags()
{
    m_isNativeFaultsGenerated   = false;
    m_isOppositeFaultsGenerated = false;
    m_isNativeNncsGenerated     = false;
    m_isAllNncsGenerated        = false;
}

//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
void RivFaultPartMgr::updatePartEffect()
{
    // Set default effect
    caf::SurfaceEffectGenerator geometryEffgen( m_defaultColor, caf::PO_1 );
    geometryEffgen.setCullBackfaces( faceCullingMode() );

    cvf::ref<cvf::Effect> geometryOnlyEffect = geometryEffgen.generateCachedEffect();

    if ( m_nativeFaultFaces.notNull() )
    {
        m_nativeFaultFaces->setEffect( geometryOnlyEffect.p() );
    }

    if ( m_oppositeFaultFaces.notNull() )
    {
        m_oppositeFaultFaces->setEffect( geometryOnlyEffect.p() );
    }

    updateNNCColors( 0, nullptr );

    // Update mesh colors as well, in case of change
    RiaPreferences* prefs = RiaPreferences::current();

    cvf::ref<cvf::Effect>    eff;
    caf::MeshEffectGenerator faultEffGen( prefs->defaultFaultGridLineColors() );
    eff = faultEffGen.generateCachedEffect();

    if ( m_nativeFaultGridLines.notNull() )
    {
        m_nativeFaultGridLines->setEffect( eff.p() );
    }

    if ( m_oppositeFaultGridLines.notNull() )
    {
        m_oppositeFaultGridLines->setEffect( eff.p() );
    }

    if ( m_opacityLevel < 1.0f )
    {
        // Set priority to make sure this transparent geometry are rendered last
        if ( m_nativeFaultFaces.notNull() ) m_nativeFaultFaces->setPriority( RivPartPriority::PartType::TransparentFault );
        if ( m_oppositeFaultFaces.notNull() ) m_oppositeFaultFaces->setPriority( RivPartPriority::PartType::TransparentFault );

        if ( m_NNCFaces.notNull() ) m_NNCFaces->setPriority( RivPartPriority::PartType::TransparentNnc );
        if ( m_allanNNCFaces.notNull() ) m_allanNNCFaces->setPriority( RivPartPriority::PartType::TransparentNnc );

        if ( m_nativeFaultGridLines.notNull() )
        {
            m_nativeFaultGridLines->setPriority( RivPartPriority::PartType::FaultMeshLines );
        }

        if ( m_oppositeFaultGridLines.notNull() )
        {
            m_oppositeFaultGridLines->setPriority( RivPartPriority::PartType::FaultMeshLines );
        }
    }
}

//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
void RivFaultPartMgr::createLabelWithAnchorLine( const cvf::Part* part )
{
    m_faultLabelPart     = nullptr;
    m_faultLabelLinePart = nullptr;

    if ( !part ) return;

    cvf::BoundingBox bb = part->boundingBox();

    cvf::Vec3d bbTopCenter = bb.center();
    bbTopCenter.z()        = bb.max().z();

    const cvf::DrawableGeo* geo = dynamic_cast<const cvf::DrawableGeo*>( part->drawable() );

    // Find closest vertex to top of bounding box.
    // Will be recomputed when filter changes, to make sure the label is always visible
    // for any filter combination
    cvf::Vec3f faultVertexToAttachLabel = findClosestVertex( cvf::Vec3f( bbTopCenter ), geo->vertexArray() );

    cvf::Vec3f labelPosition = faultVertexToAttachLabel;
    labelPosition.z() += bb.extent().z() / 2;

    // Fault label
    if ( !m_rimFault->name().isEmpty() )
    {
        RiaGuiApplication* app = RiaGuiApplication::instance();

        cvf::Font* font = app->defaultWellLabelFont();

        cvf::ref<cvf::DrawableText> drawableText = new cvf::DrawableText;
        drawableText->setFont( font );
        drawableText->setCheckPosVisible( false );
        drawableText->setDrawBorder( false );
        drawableText->setDrawBackground( false );
        drawableText->setVerticalAlignment( cvf::TextDrawer::CENTER );

        cvf::Color3f defWellLabelColor = app->preferences()->defaultWellLabelColor();
        {
            {
                RimFaultInViewCollection* parentObject;
                m_rimFault->firstAncestorOrThisOfType( parentObject );
                if ( parentObject )
                {
                    defWellLabelColor = parentObject->faultLabelColor();
                }
            }
        }

        drawableText->setTextColor( defWellLabelColor );

        cvf::String cvfString = cvfqt::Utils::toString( m_rimFault->name() );

        cvf::Vec3f textCoord( labelPosition );
        double     characteristicCellSize = bb.extent().z() / 20;
        textCoord.z() += characteristicCellSize;

        drawableText->addText( cvfString, textCoord );

        cvf::ref<cvf::Part> labelPart = new cvf::Part;
        labelPart->setName( "RivFaultPart : text " + cvfString );
        labelPart->setDrawable( drawableText.p() );

        cvf::ref<cvf::Effect> eff = new cvf::Effect;

        labelPart->setEffect( eff.p() );
        labelPart->setPriority( RivPartPriority::PartType::Text );

        labelPart->setSourceInfo( new RivTextLabelSourceInfo( m_rimFault, cvfString, textCoord ) );

        m_faultLabelPart = labelPart;
    }

    // Line from fault geometry to label
    {
        cvf::ref<cvf::Vec3fArray> vertices = new cvf::Vec3fArray;
        vertices->reserve( 2 );
        vertices->add( faultVertexToAttachLabel );
        vertices->add( labelPosition );

        cvf::ref<cvf::DrawableGeo> lineGeo = new cvf::DrawableGeo;
        lineGeo->setVertexArray( vertices.p() );

        cvf::ref<cvf::PrimitiveSetDirect> primSet = new cvf::PrimitiveSetDirect( cvf::PT_LINES );
        primSet->setStartIndex( 0 );
        primSet->setIndexCount( vertices->size() );
        lineGeo->addPrimitiveSet( primSet.p() );

        m_faultLabelLinePart = new cvf::Part;
        m_faultLabelLinePart->setName( "Anchor line for label" + cvf::String( static_cast<int>( m_grid->gridIndex() ) ) );
        m_faultLabelLinePart->setDrawable( lineGeo.p() );

        m_faultLabelLinePart->updateBoundingBox();

        caf::MeshEffectGenerator gen( m_rimFault->faultColor() );
        cvf::ref<cvf::Effect>    eff = gen.generateCachedEffect();

        m_faultLabelLinePart->setEffect( eff.p() );
    }
}

//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
cvf::Vec3f RivFaultPartMgr::findClosestVertex( const cvf::Vec3f& point, const cvf::Vec3fArray* vertices )
{
    CVF_ASSERT( vertices );

    if ( !vertices ) return cvf::Vec3f::UNDEFINED;

    float closestDiff( HUGE_VAL );

    size_t closestIndex = cvf::UNDEFINED_SIZE_T;

    for ( size_t i = 0; i < vertices->size(); i++ )
    {
        float diff = point.pointDistance( vertices->get( i ) );

        if ( diff < closestDiff )
        {
            closestDiff  = diff;
            closestIndex = i;
        }
    }

    if ( closestIndex != cvf::UNDEFINED_SIZE_T )
    {
        return vertices->get( closestIndex );
    }
    else
    {
        return cvf::Vec3f::UNDEFINED;
    }
}

//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
void RivFaultPartMgr::appendNativeFaultFacesToModel( cvf::ModelBasicList* model )
{
    if ( !m_isNativeFaultsGenerated )
    {
        generatePartGeometry();
    }

    if ( m_nativeFaultFaces.notNull() )
    {
        model->addPart( m_nativeFaultFaces.p() );
    }
}

//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
void RivFaultPartMgr::appendOppositeFaultFacesToModel( cvf::ModelBasicList* model )
{
    if ( !m_isOppositeFaultsGenerated )
    {
        generatePartGeometry();
    }

    if ( m_oppositeFaultFaces.notNull() )
    {
        model->addPart( m_oppositeFaultFaces.p() );
    }
}

//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
void RivFaultPartMgr::appendLabelPartsToModel( cvf::ModelBasicList* model )
{
    if ( m_faultLabelPart.notNull() ) model->addPart( m_faultLabelPart.p() );
    if ( m_faultLabelLinePart.notNull() ) model->addPart( m_faultLabelLinePart.p() );
}

//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
void RivFaultPartMgr::appendMeshLinePartsToModel( cvf::ModelBasicList* model )
{
    if ( m_nativeFaultGridLines.notNull() ) model->addPart( m_nativeFaultGridLines.p() );
    if ( m_oppositeFaultGridLines.notNull() ) model->addPart( m_oppositeFaultGridLines.p() );
}

//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
void RivFaultPartMgr::appendCompleteNNCFacesToModel( cvf::ModelBasicList* model )
{
    if ( !m_isAllNncsGenerated )
    {
        generateAllNncPartGeometry();
    }

    if ( m_allanNNCFaces.notNull() ) model->addPart( m_allanNNCFaces.p() );
}

//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
void RivFaultPartMgr::appendNativeNNCFacesToModel( cvf::ModelBasicList* model )
{
    if ( !m_isNativeNncsGenerated )
    {
        generateNativeNncPartGeometry();
    }

    if ( m_NNCFaces.notNull() ) model->addPart( m_NNCFaces.p() );
}

//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
caf::FaceCulling RivFaultPartMgr::faceCullingMode() const
{
    bool isShowingGrid = m_rimFaultCollection->isGridVisualizationMode();
    if ( !isShowingGrid )
    {
        if ( m_rimFaultCollection->faultResult() == RimFaultInViewCollection::FAULT_BACK_FACE_CULLING )
        {
            if ( m_grid->mainGrid()->isFaceNormalsOutwards() )
            {
                return caf::FC_BACK;
            }
            else
            {
                return caf::FC_FRONT;
            }
        }
        else if ( m_rimFaultCollection->faultResult() == RimFaultInViewCollection::FAULT_FRONT_FACE_CULLING )
        {
            if ( m_grid->mainGrid()->isFaceNormalsOutwards() )
            {
                return caf::FC_FRONT;
            }
            else
            {
                return caf::FC_BACK;
            }
        }
        else
        {
            return caf::FC_NONE;
        }
    }
    else
    {
        // Do not perform face culling in grid mode to make sure the displayed grid is watertight
        return caf::FC_NONE;
    }
}

//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
void RivFaultPartMgr::updateNNCColors( size_t timeStepIndex, RimEclipseCellColors* cellResultColors )
{
    bool updateNnc   = m_NNCFaces.notNull();
    bool updateAllan = m_allanNNCFaces.notNull();

    if ( !updateNnc && !updateAllan )
    {
        return;
    }

    bool showNncsWithScalarMappedColor = false;

    RimEclipseView* eclipseView = nullptr;

    if ( cellResultColors )
    {
        RigEclipseResultAddress eclResAddr = cellResultColors->eclipseResultAddress();

        if ( m_grid->mainGrid()->nncData()->hasScalarValues( eclResAddr ) )
        {
            showNncsWithScalarMappedColor = true;
        }
        eclipseView = cellResultColors->reservoirView();
    }

    if ( showNncsWithScalarMappedColor )
    {
        RigEclipseResultAddress   eclResAddr = cellResultColors->eclipseResultAddress();
        RiaDefines::ResultCatType resultType = cellResultColors->resultType();

        const cvf::ScalarMapper* mapper = cellResultColors->legendConfig()->scalarMapper();

        if ( eclipseView )
        {
            RimEclipseCase* eclipseCase = eclipseView->eclipseCase();
            if ( eclipseCase )
            {
                size_t nativeTimeStepIndex = eclipseCase->uiToNativeTimeStepIndex( timeStepIndex );

                if ( updateNnc )
                {
                    m_NNCGenerator->textureCoordinates( m_NNCTextureCoords.p(), mapper, resultType, eclResAddr, nativeTimeStepIndex );
                }

                if ( updateAllan )
                {
                    m_allanNNCGenerator->textureCoordinates( m_allanNNCTextureCoords.p(), mapper, resultType, eclResAddr, nativeTimeStepIndex );
                }
            }
        }

        cvf::ref<cvf::Effect> nncEffect;

        if ( m_rimFaultCollection->showFaultFaces || m_rimFaultCollection->showOppositeFaultFaces )
        {
            // Move NNC closer to camera to avoid z-fighting with grid surface
            caf::ScalarMapperEffectGenerator nncEffgen( mapper, caf::PO_NEG_LARGE );
            if ( eclipseView ) nncEffgen.disableLighting( eclipseView->isLightingDisabled() );
            nncEffect = nncEffgen.generateCachedEffect();
        }
        else
        {
            // If no grid is present, use same offset as grid geometry to be able to see mesh lines
            caf::ScalarMapperEffectGenerator nncEffgen( mapper, caf::PO_1 );
            if ( eclipseView ) nncEffgen.disableLighting( eclipseView->isLightingDisabled() );
            nncEffect = nncEffgen.generateCachedEffect();
        }

        if ( updateNnc )
        {
            cvf::DrawableGeo* dg = dynamic_cast<cvf::DrawableGeo*>( m_NNCFaces->drawable() );
            if ( dg ) dg->setTextureCoordArray( m_NNCTextureCoords.p() );

            m_NNCFaces->setEffect( nncEffect.p() );
        }

        if ( updateAllan )
        {
            cvf::DrawableGeo* dg = dynamic_cast<cvf::DrawableGeo*>( m_allanNNCFaces->drawable() );
            if ( dg ) dg->setTextureCoordArray( m_allanNNCTextureCoords.p() );

            m_allanNNCFaces->setEffect( nncEffect.p() );
        }
    }
    else
    {
        // NNC faces a bit lighter than the fault for now
        cvf::Color3f nncColor = m_defaultColor;
        nncColor.r() += ( 1.0 - nncColor.r() ) * 0.2;
        nncColor.g() += ( 1.0 - nncColor.g() ) * 0.2;
        nncColor.b() += ( 1.0 - nncColor.b() ) * 0.2;

        CVF_ASSERT( nncColor.isValid() );
        cvf::ref<cvf::Effect> nncEffect;

        if ( m_rimFaultCollection->showFaultFaces || m_rimFaultCollection->showOppositeFaultFaces )
        {
            // Move NNC closer to camera to avoid z-fighting with grid surface
            caf::SurfaceEffectGenerator nncEffgen( nncColor, caf::PO_NEG_LARGE );
            nncEffect = nncEffgen.generateCachedEffect();
        }
        else
        {
            // If no grid is present, use same offset as grid geometry to be able to see mesh lines
            caf::SurfaceEffectGenerator nncEffgen( nncColor, caf::PO_1 );
            nncEffect = nncEffgen.generateCachedEffect();
        }

        if ( updateNnc )
        {
            m_NNCFaces->setEffect( nncEffect.p() );
        }

        if ( updateAllan )
        {
            m_allanNNCFaces->setEffect( nncEffect.p() );
        }
    }
}