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

#include "RigWellPathFormations.h"

#include "QStringList"

//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
RigWellPathFormations::RigWellPathFormations( const std::vector<RigWellPathFormation>& formations, const QString& filePath, const QString& key )
{
    m_filePath  = filePath;
    m_keyInFile = key;

    for ( const RigWellPathFormation& formation : formations )
    {
        if ( isFluid( formation.formationName ) )
        {
            m_fluids.push_back( formation );
        }
        else
        {
            FormationLevel level             = detectLevel( formation.formationName );
            m_formationsLevelsPresent[level] = true;

            m_formations.push_back( std::pair<RigWellPathFormation, FormationLevel>( formation, level ) );
        }
    }
}

//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
void RigWellPathFormations::depthAndFormationNamesWithoutDuplicatesOnDepth( std::vector<QString>*         names,
                                                                            std::vector<double>*          measuredDepths,
                                                                            RimWellLogPlot::DepthTypeEnum depthType ) const
{
    std::map<double, bool, DepthComp> tempMakeVectorUniqueOnMeasuredDepth;

    if ( depthType == RiaDefines::DepthTypeEnum::MEASURED_DEPTH )
    {
        for ( const std::pair<RigWellPathFormation, FormationLevel>& formation : m_formations )
        {
            if ( !tempMakeVectorUniqueOnMeasuredDepth.count( formation.first.mdTop ) )
            {
                measuredDepths->push_back( formation.first.mdTop );
                names->push_back( formation.first.formationName + " Top" );
                tempMakeVectorUniqueOnMeasuredDepth[formation.first.mdTop] = true;
            }
        }

        for ( const std::pair<RigWellPathFormation, FormationLevel>& formation : m_formations )
        {
            if ( !tempMakeVectorUniqueOnMeasuredDepth.count( formation.first.mdBase ) )
            {
                measuredDepths->push_back( formation.first.mdBase );
                names->push_back( formation.first.formationName + " Base" );
                tempMakeVectorUniqueOnMeasuredDepth[formation.first.mdBase] = true;
            }
        }
    }
    else if ( depthType == RiaDefines::DepthTypeEnum::TRUE_VERTICAL_DEPTH )
    {
        for ( const std::pair<RigWellPathFormation, FormationLevel>& formation : m_formations )
        {
            if ( !tempMakeVectorUniqueOnMeasuredDepth.count( formation.first.tvdTop ) )
            {
                measuredDepths->push_back( formation.first.tvdTop );
                names->push_back( formation.first.formationName + " Top" );
                tempMakeVectorUniqueOnMeasuredDepth[formation.first.tvdTop] = true;
            }
        }

        for ( const std::pair<RigWellPathFormation, FormationLevel>& formation : m_formations )
        {
            if ( !tempMakeVectorUniqueOnMeasuredDepth.count( formation.first.tvdBase ) )
            {
                measuredDepths->push_back( formation.first.tvdBase );
                names->push_back( formation.first.formationName + " Base" );
                tempMakeVectorUniqueOnMeasuredDepth[formation.first.tvdBase] = true;
            }
        }
    }

    /*
    for (const std::pair<RigWellPathFormation, FormationLevel>& formation : m_formations)
    {
        if (!tempMakeVectorUniqueOnMeasuredDepth.count(formation.first.mdTop))
        {
            double depth;
            if (depthType == RiaDefines::MEASURED_DEPTH)
            {
                depth = formation.first.mdTop;
            }
            else if (depthType == RiaDefines::TRUE_VERTICAL_DEPTH)
            {
                depth = formation.first.tvdTop;
            }
            else return;

            measuredDepths->push_back(depth);
            names->push_back(formation.first.formationName + " Top");
            tempMakeVectorUniqueOnMeasuredDepth[depth] = true;
        }
    }

    for (const std::pair<RigWellPathFormation, FormationLevel>& formation : m_formations)
    {
        double depth;
        if (depthType == RiaDefines::MEASURED_DEPTH)
        {
            depth = formation.first.mdBase;
        }
        else if (depthType == RiaDefines::TRUE_VERTICAL_DEPTH)
        {
            depth = formation.first.tvdBase;
        }
        else return;

        if (!tempMakeVectorUniqueOnMeasuredDepth.count(formation.first.mdBase))
        {
            measuredDepths->push_back(depth);
            names->push_back(formation.first.formationName + " Base");
            tempMakeVectorUniqueOnMeasuredDepth[depth] = true;
        }
    }*/
}

//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
void RigWellPathFormations::evaluateFormationsForOnePosition( const std::vector<std::pair<RigWellPathFormation, FormationLevel>>& formations,
                                                              const FormationLevel&                      maxLevel,
                                                              const PickPosition&                        position,
                                                              std::map<double, LevelAndName, DepthComp>* uniqueListMaker,
                                                              RimWellLogPlot::DepthTypeEnum              depthType ) const
{
    QString postFix;

    if ( position == TOP )
    {
        postFix = " Top";
    }
    else
    {
        postFix = " Base";
    }

    for ( const std::pair<RigWellPathFormation, FormationLevel>& formation : formations )
    {
        double depth;

        if ( depthType == RiaDefines::DepthTypeEnum::MEASURED_DEPTH )
        {
            if ( position == TOP )
            {
                depth = formation.first.mdTop;
            }
            else
            {
                depth = formation.first.mdBase;
            }
        }
        else if ( depthType == RiaDefines::DepthTypeEnum::TRUE_VERTICAL_DEPTH )
        {
            if ( position == TOP )
            {
                depth = formation.first.tvdTop;
            }
            else
            {
                depth = formation.first.tvdBase;
            }
        }
        else
            return;

        if ( formation.second > maxLevel ) continue;

        if ( !uniqueListMaker->count( depth ) || uniqueListMaker->at( depth ).level < formation.second )
        {
            ( *uniqueListMaker )[depth] = LevelAndName( formation.second, formation.first.formationName + postFix );
        }
    }
}

//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
void RigWellPathFormations::evaluateFormations( const std::vector<std::pair<RigWellPathFormation, FormationLevel>>& formations,
                                                const FormationLevel&                                               maxLevel,
                                                std::vector<QString>*                                               names,
                                                std::vector<double>*                                                depths,
                                                RimWellLogPlot::DepthTypeEnum                                       depthType ) const
{
    std::map<double, LevelAndName, DepthComp> tempMakeVectorUniqueOnDepth;

    evaluateFormationsForOnePosition( formations, maxLevel, PickPosition::TOP, &tempMakeVectorUniqueOnDepth, depthType );
    evaluateFormationsForOnePosition( formations, maxLevel, PickPosition::BASE, &tempMakeVectorUniqueOnDepth, depthType );

    for ( const std::pair<const double, LevelAndName>& uniqueDepth : tempMakeVectorUniqueOnDepth )
    {
        depths->push_back( uniqueDepth.first );
        names->push_back( uniqueDepth.second.name );
    }
}

//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
void RigWellPathFormations::evaluateFluids( const std::vector<RigWellPathFormation>& fluidFormations,
                                            std::vector<QString>*                    names,
                                            std::vector<double>*                     depths,
                                            RimWellLogPlot::DepthTypeEnum            depthType ) const
{
    std::map<double, QString, DepthComp> uniqueListMaker;

    for ( const RigWellPathFormation& formation : fluidFormations )
    {
        double depthBase;
        if ( depthType == RiaDefines::DepthTypeEnum::MEASURED_DEPTH )
        {
            depthBase = formation.mdBase;
        }
        else if ( depthType == RiaDefines::DepthTypeEnum::TRUE_VERTICAL_DEPTH )
        {
            depthBase = formation.tvdBase;
        }
        else
            return;

        uniqueListMaker[depthBase] = formation.formationName + " Base";
    }

    for ( const RigWellPathFormation& formation : fluidFormations )
    {
        double depthTop;
        if ( depthType == RiaDefines::DepthTypeEnum::MEASURED_DEPTH )
        {
            depthTop = formation.mdTop;
        }
        else if ( depthType == RiaDefines::DepthTypeEnum::TRUE_VERTICAL_DEPTH )
        {
            depthTop = formation.tvdTop;
        }
        else
            return;

        uniqueListMaker[depthTop] = formation.formationName + " Top";
    }

    for ( const std::pair<const double, QString>& depthAndFormation : uniqueListMaker )
    {
        depths->push_back( depthAndFormation.first );
        names->push_back( depthAndFormation.second );
    }
}

//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
void RigWellPathFormations::depthAndFormationNamesUpToLevel( FormationLevel                level,
                                                             std::vector<QString>*         names,
                                                             std::vector<double>*          depths,
                                                             bool                          includeFluids,
                                                             RimWellLogPlot::DepthTypeEnum depthType ) const
{
    names->clear();
    depths->clear();

    if ( includeFluids )
    {
        evaluateFluids( m_fluids, names, depths, depthType );
    }

    if ( level == RigWellPathFormations::NONE )
    {
        return;
    }
    else if ( level == RigWellPathFormations::ALL )
    {
        depthAndFormationNamesWithoutDuplicatesOnDepth( names, depths, depthType );
    }
    else
    {
        evaluateFormations( m_formations, level, names, depths, depthType );
    }
}

//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
std::vector<RigWellPathFormations::FormationLevel> RigWellPathFormations::formationsLevelsPresent() const
{
    std::vector<RigWellPathFormations::FormationLevel> formationLevels;

    for ( const std::pair<const RigWellPathFormations::FormationLevel, bool>& formationLevel : m_formationsLevelsPresent )
    {
        formationLevels.push_back( formationLevel.first );
    }
    return formationLevels;
}

//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
QString RigWellPathFormations::filePath() const
{
    return m_filePath;
}

//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
QString RigWellPathFormations::keyInFile() const
{
    return m_keyInFile;
}

//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
size_t RigWellPathFormations::formationNamesCount() const
{
    return m_formations.size() + m_fluids.size();
}

//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
bool RigWellPathFormations::isFluid( QString formationName )
{
    formationName = formationName.trimmed();

    if ( formationName == "OIL" || formationName == "GAS" || formationName == "WATER" )
    {
        return true;
    }
    return false;
}

//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
RigWellPathFormations::FormationLevel RigWellPathFormations::detectLevel( QString formationName )
{
    formationName = formationName.trimmed();

    bool isGroupName = true;
    for ( QChar c : formationName )
    {
        if ( c.isLower() )
        {
            isGroupName = false;
            break;
        }
    }
    if ( isGroupName )
    {
        return RigWellPathFormations::GROUP;
    }

    QStringList formationNameSplitted = formationName.split( " " );

    std::vector<QString> levelDesctiptorCandidates;

    for ( QString word : formationNameSplitted )
    {
        for ( const QChar& c : word )
        {
            if ( c.isDigit() )
            {
                levelDesctiptorCandidates.push_back( word );
                break;
            }
        }
    }
    if ( levelDesctiptorCandidates.empty() )
    {
        return RigWellPathFormations::LEVEL0;
    }

    if ( levelDesctiptorCandidates.size() > 1 )
    {
        for ( auto it = levelDesctiptorCandidates.begin(); it != levelDesctiptorCandidates.end(); it++ )
        {
            for ( const QChar& c : *it )
            {
                if ( c.isLetter() )
                {
                    levelDesctiptorCandidates.erase( it );
                }
            }
        }
    }
    if ( levelDesctiptorCandidates.size() != 1 ) return RigWellPathFormations::UNKNOWN;

    QString levelDescriptor = levelDesctiptorCandidates[0];

    QStringList joinedLevel = levelDescriptor.split( '+' );
    if ( joinedLevel.size() > 1 )
    {
        levelDescriptor = joinedLevel[0];
    }

    int dotCount = levelDescriptor.count( '.' );

    switch ( dotCount )
    {
        case 0:
            return RigWellPathFormations::LEVEL1;
        case 1:
            return RigWellPathFormations::LEVEL2;
        case 2:
            return RigWellPathFormations::LEVEL3;
        case 3:
            return RigWellPathFormations::LEVEL4;
        case 4:
            return RigWellPathFormations::LEVEL5;
        case 5:
            return RigWellPathFormations::LEVEL6;
        case 6:
            return RigWellPathFormations::LEVEL7;
        case 7:
            return RigWellPathFormations::LEVEL8;
        case 8:
            return RigWellPathFormations::LEVEL9;
        case 9:
            return RigWellPathFormations::LEVEL10;
        default:
            break;
    }
    return RigWellPathFormations::UNKNOWN;
}
