
//
//  Copyright (C) 2011-     Statoil ASA
//  Copyright (C) 2013-     Ceetron Solutions AS
//  Copyright (C) 2011-2012 Ceetron 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 "RimTools.h"

#include "RimCase.h"
#include "RimColorLegend.h"
#include "RimColorLegendCollection.h"
#include "RimEclipseCase.h"
#include "RimGeoMechCase.h"
#include "RimOilField.h"
#include "RimProject.h"
#include "RimSeismicData.h"
#include "RimSeismicDataCollection.h"
#include "RimWellLogFile.h"
#include "RimWellPath.h"
#include "RimWellPathCollection.h"

#include "cafPdmUiItem.h"
#include "cafUtils.h"

#include <QDateTime>
#include <QDir>
#include <QFileInfo>

//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
QString RimTools::getCacheRootDirectoryPathFromProject()
{
    if ( !RimProject::current() )
    {
        return QString();
    }

    QString projectFileName = RimProject::current()->fileName();

    QString   cacheRootFolderPath;
    QFileInfo fileInfo( projectFileName );
    cacheRootFolderPath = fileInfo.canonicalPath();
    cacheRootFolderPath += "/" + fileInfo.completeBaseName();

    return cacheRootFolderPath;
}

//--------------------------------------------------------------------------------------------------
///  Relocate the supplied file name, based on the search path as follows:
///  fileName, newProjectPath/fileNameWoPath, relocatedPath/fileNameWoPath
///  If the file is not found in any of the positions, the fileName is returned but converted to Qt Style path
///  separators: "/"
///
///  The relocatedPath is found in this way:
///  use the start of newProjectPath
///  plus the end of the path to m_gridFileName
///  such that the common start of oldProjectPath and m_gridFileName is removed from m_gridFileName
///  and replaced with the start of newProjectPath up to where newProjectPath starts to be equal to oldProjectPath
//--------------------------------------------------------------------------------------------------
QString RimTools::relocateFile( const QString&        originalFileName,
                                const QString&        currentProjectPath,
                                const QString&        previousProjectPath,
                                bool*                 foundFile,
                                std::vector<QString>* searchedPaths )
{
    if ( foundFile ) *foundFile = true;

    // Make sure we have a Qt formatted path ( using "/" not "\")
    QString fileName       = QDir::fromNativeSeparators( originalFileName );
    QString newProjectPath = QDir::fromNativeSeparators( currentProjectPath );
    QString oldProjectPath = QDir::fromNativeSeparators( previousProjectPath );

    // If we from a file or whatever gets a real windows path on linux, we need to manually convert it
    // because Qt will not. QDir::fromNativeSeparators does nothing on linux.

    bool isWindowsPath = false;
    if ( originalFileName.count( "/" ) )
        isWindowsPath = false; // "/" are not allowed in a windows path
    else if ( originalFileName.count( "\\" ) && !caf::Utils::fileExists( originalFileName ) ) // To make sure we do not
                                                                                              // convert single linux
                                                                                              // files containing "\"
    {
        isWindowsPath = true;
    }

    if ( isWindowsPath )
    {
        // Windows absolute path detected. transform.
        fileName.replace( QString( "\\" ), QString( "/" ) );
    }

    if ( searchedPaths ) searchedPaths->push_back( fileName );
    if ( caf::Utils::fileExists( fileName ) )
    {
        return fileName;
    }

    // First check in the new project file directory
    {
        QString fileNameWithoutPath = QFileInfo( fileName ).fileName();
        QString candidate           = QDir::fromNativeSeparators( newProjectPath + QDir::separator() + fileNameWithoutPath );
        if ( searchedPaths ) searchedPaths->push_back( candidate );

        if ( caf::Utils::fileExists( candidate ) )
        {
            return candidate;
        }
    }

    // Then find the possible move of a directory structure where projects and files referenced are moved in "paralell"

    QFileInfo   fileNameFileInfo( QDir::fromNativeSeparators( fileName ) );
    QString     fileNamePath         = fileNameFileInfo.path();
    QString     fileNameWithoutPath  = fileNameFileInfo.fileName();
    QStringList fileNamePathElements = fileNamePath.split( "/" );

    QString     oldProjPath         = QDir::fromNativeSeparators( oldProjectPath );
    QStringList oldProjPathElements = oldProjPath.split( "/" );

    QString     newProjPath         = QDir::fromNativeSeparators( newProjectPath );
    QStringList newProjPathElements = newProjPath.split( "/" );

    // Find the possible equal start of the old project path, and the referenced file

    bool pathStartsAreEqual = false;
    bool pathEndsDiffer     = false;
    int  firstDiffIdx       = 0;
    for ( firstDiffIdx = 0; firstDiffIdx < fileNamePathElements.size() && firstDiffIdx < oldProjPathElements.size(); ++firstDiffIdx )
    {
#ifdef WIN32
        // When comparing parts of a file path, the drive letter has been seen to be a mix of
        // upper and lower cases. Always use case insensitive compare on Windows, as this is a valid approach
        // for all parts for a file path
        Qt::CaseSensitivity cs = Qt::CaseInsensitive;
#else
        Qt::CaseSensitivity cs = Qt::CaseSensitive;
#endif
        if ( fileNamePathElements[firstDiffIdx].compare( oldProjPathElements[firstDiffIdx], cs ) == 0 )
        {
            pathStartsAreEqual = pathStartsAreEqual || !fileNamePathElements[firstDiffIdx].isEmpty();
        }
        else
        {
            pathEndsDiffer = true;
            break;
        }
    }

    if ( ( !pathEndsDiffer && firstDiffIdx < fileNamePathElements.size() ) || firstDiffIdx < oldProjPathElements.size() )
    {
        pathEndsDiffer = true;
    }

    // If the path starts are equal, try to substitute it in the referenced file, with the corresponding new project
    // path start

    if ( pathStartsAreEqual )
    {
        if ( pathEndsDiffer )
        {
            QString oldFileNamePathEnd;
            for ( int i = firstDiffIdx; i < fileNamePathElements.size(); ++i )
            {
                oldFileNamePathEnd += fileNamePathElements[i];
                oldFileNamePathEnd += "/";
            }

            // Find the new Project File Start Path

            QStringList oldProjectFilePathEndElements;
            for ( int i = firstDiffIdx; i < oldProjPathElements.size(); ++i )
            {
                oldProjectFilePathEndElements.push_back( oldProjPathElements[i] );
            }

            int lastProjectDiffIdx = newProjPathElements.size() - 1;
            {
                int ppIdx = oldProjectFilePathEndElements.size() - 1;

                for ( ; lastProjectDiffIdx >= 0 && ppIdx >= 0; --lastProjectDiffIdx, --ppIdx )
                {
                    if ( oldProjectFilePathEndElements[ppIdx] != newProjPathElements[lastProjectDiffIdx] )
                    {
                        break;
                    }
                }
            }

            QString newProjectFileStartPath;
            for ( int i = 0; i <= lastProjectDiffIdx; ++i )
            {
                newProjectFileStartPath += newProjPathElements[i];
                newProjectFileStartPath += "/";
            }

            QString relocationPath = newProjectFileStartPath + oldFileNamePathEnd;

            QString relocatedFileName = relocationPath + fileNameWithoutPath;

            if ( searchedPaths ) searchedPaths->push_back( relocatedFileName );

            if ( caf::Utils::fileExists( relocatedFileName ) )
            {
                return relocatedFileName;
            }
        }
        else
        {
            // The Grid file was located in the same dir as the Project file. This is supposed to be handled above.
            // So we did not find it
        }
    }

    // return the unchanged filename, if we could not find a valid relocation file
    if ( foundFile ) *foundFile = false;

    return fileName;
}

//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
void RimTools::wellPathOptionItemsSubset( const std::vector<RimWellPath*>& wellPathsToExclude, QList<caf::PdmOptionItemInfo>* options )
{
    if ( !options ) return;

    auto wellPathColl = RimTools::wellPathCollection();
    if ( wellPathColl )
    {
        std::vector<RimWellPath*> wellPathsToInclude;

        auto all = wellPathColl->allWellPaths();
        for ( auto w : all )
        {
            bool include = true;
            for ( auto exclude : wellPathsToExclude )
            {
                if ( w == exclude ) include = false;
            }

            if ( include ) wellPathsToInclude.push_back( w );
        }

        optionItemsForSpecifiedWellPaths( wellPathsToInclude, options );
    }
}

//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
void RimTools::wellPathOptionItems( QList<caf::PdmOptionItemInfo>* options )
{
    if ( !options ) return;

    auto wellPathColl = RimTools::wellPathCollection();
    if ( wellPathColl )
    {
        optionItemsForSpecifiedWellPaths( wellPathColl->allWellPaths(), options );
    }
}

//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
void RimTools::wellPathWithFormationsOptionItems( QList<caf::PdmOptionItemInfo>* options )
{
    if ( !options ) return;

    std::vector<RimWellPath*> wellPaths;
    RimTools::wellPathWithFormations( &wellPaths );

    optionItemsForSpecifiedWellPaths( wellPaths, options );
}

//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
void RimTools::wellPathWithFormations( std::vector<RimWellPath*>* wellPaths )
{
    auto wellPathColl = RimTools::wellPathCollection();
    if ( wellPathColl )
    {
        for ( RimWellPath* wellPath : wellPathColl->allWellPaths() )
        {
            if ( wellPath->hasFormations() )
            {
                wellPaths->push_back( wellPath );
            }
        }
    }
}

//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
void RimTools::caseOptionItems( QList<caf::PdmOptionItemInfo>* options )
{
    if ( !options ) return;

    RimProject* proj = RimProject::current();
    if ( proj )
    {
        std::vector<RimCase*> cases;
        proj->allCases( cases );

        for ( RimCase* c : cases )
        {
            options->push_back( caf::PdmOptionItemInfo( c->caseUserDescription(), c, false, c->uiIconProvider() ) );
        }
    }
}

//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
void RimTools::eclipseCaseOptionItems( QList<caf::PdmOptionItemInfo>* options )
{
    if ( !options ) return;

    RimProject* proj = RimProject::current();
    if ( proj )
    {
        std::vector<RimCase*> cases;
        proj->allCases( cases );

        for ( RimCase* c : cases )
        {
            RimEclipseCase* eclipseCase = dynamic_cast<RimEclipseCase*>( c );
            if ( eclipseCase )
            {
                options->push_back( caf::PdmOptionItemInfo( c->caseUserDescription(), c, false, c->uiIconProvider() ) );
            }
        }
    }
}

//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
void RimTools::geoMechCaseOptionItems( QList<caf::PdmOptionItemInfo>* options )
{
    if ( !options ) return;

    RimProject* proj = RimProject::current();
    if ( proj )
    {
        std::vector<RimCase*> cases;
        proj->allCases( cases );

        for ( RimCase* c : cases )
        {
            RimGeoMechCase* geoMechCase = dynamic_cast<RimGeoMechCase*>( c );
            if ( geoMechCase )
            {
                options->push_back( caf::PdmOptionItemInfo( c->caseUserDescription(), c, false, c->uiIconProvider() ) );
            }
        }
    }
}

//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
void RimTools::seismicDataOptionItems( QList<caf::PdmOptionItemInfo>* options, cvf::BoundingBox worldBBox )
{
    if ( !options ) return;

    RimProject* proj = RimProject::current();
    if ( proj )
    {
        const auto& coll = proj->activeOilField()->seismicCollection().p();

        for ( auto* c : coll->seismicData() )
        {
            if ( c->boundingBox()->intersects( worldBBox ) )
                options->push_back( caf::PdmOptionItemInfo( c->userDescription(), c, false, c->uiIconProvider() ) );
        }
    }
}

//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
void RimTools::colorLegendOptionItems( QList<caf::PdmOptionItemInfo>* options )
{
    if ( !options ) return;

    RimProject*                  project               = RimProject::current();
    RimColorLegendCollection*    colorLegendCollection = project->colorLegendCollection();
    std::vector<RimColorLegend*> colorLegends          = colorLegendCollection->allColorLegends();

    for ( RimColorLegend* colorLegend : colorLegends )
    {
        options->push_back( caf::PdmOptionItemInfo( colorLegend->colorLegendName(), colorLegend, false, colorLegend->paletteIconProvider() ) );
    }
}

//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
RimWellPathCollection* RimTools::wellPathCollection()
{
    RimProject* proj = RimProject::current();
    if ( proj && proj->activeOilField() )
    {
        return proj->activeOilField()->wellPathCollection();
    }

    return nullptr;
}

//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
RimWellPath* RimTools::firstWellPath()
{
    auto wellpathcoll = wellPathCollection();
    auto wellpaths    = wellpathcoll->allWellPaths();

    if ( wellpaths.size() > 0 ) return wellpaths[0];

    return nullptr;
}

//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
void RimTools::timeStepsForCase( RimCase* gridCase, QList<caf::PdmOptionItemInfo>* options )
{
    QStringList timeStepNames;

    if ( gridCase )
    {
        timeStepNames = gridCase->timeStepStrings();
    }

    for ( int i = 0; i < timeStepNames.size(); i++ )
    {
        options->push_back( caf::PdmOptionItemInfo( timeStepNames[i], i ) );
    }
}

//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
void RimTools::optionItemsForSpecifiedWellPaths( const std::vector<RimWellPath*>& wellPaths, QList<caf::PdmOptionItemInfo>* options )
{
    if ( !options ) return;

    caf::IconProvider wellIcon( ":/Well.svg" );
    for ( auto wellPath : wellPaths )
    {
        options->push_back( caf::PdmOptionItemInfo( wellPath->name(), wellPath, false, wellIcon ) );
    }
}
