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

#include "RiaArgumentParser.h"
#include "RiaBaseDefs.h"
#include "RiaDefines.h"
#include "RiaFilePathTools.h"
#include "RiaFontCache.h"
#include "RiaImportEclipseCaseTools.h"
#include "RiaLogging.h"
#include "RiaPlotWindowRedrawScheduler.h"
#include "RiaPreferences.h"
#include "RiaPreferencesSystem.h"
#include "RiaProjectModifier.h"
#include "RiaRegressionTestRunner.h"
#include "RiaSocketServer.h"
#include "RiaSummaryTools.h"
#include "RiaVersionInfo.h"
#include "RiaViewRedrawScheduler.h"

#include "ExportCommands/RicSnapshotAllPlotsToFileFeature.h"
#include "ExportCommands/RicSnapshotAllViewsToFileFeature.h"
#include "ExportCommands/RicSnapshotViewToFileFeature.h"
#include "HoloLensCommands/RicHoloLensSessionManager.h"
#include "RicImportGeneralDataFeature.h"
#include "SummaryPlotCommands/RicSummaryPlotFeatureImpl.h"

#include "Rim2dIntersectionViewCollection.h"
#include "RimAnnotationCollection.h"
#include "RimAnnotationInViewCollection.h"
#include "RimAnnotationTextAppearance.h"
#include "RimCommandObject.h"
#include "RimEclipseCaseCollection.h"
#include "RimEclipseView.h"
#include "RimFlowPlotCollection.h"
#include "RimFormationNamesCollection.h"
#include "RimFractureTemplateCollection.h"
#include "RimGeoMechCase.h"
#include "RimGeoMechCellColors.h"
#include "RimGeoMechModels.h"
#include "RimGeoMechView.h"
#include "RimGridCrossPlot.h"
#include "RimGridCrossPlotCollection.h"
#include "RimIdenticalGridCaseGroup.h"
#include "RimMainPlotCollection.h"
#include "RimMultiPlot.h"
#include "RimMultiPlotCollection.h"
#include "RimObservedDataCollection.h"
#include "RimObservedSummaryData.h"
#include "RimOilField.h"
#include "RimPltPlotCollection.h"
#include "RimProject.h"
#include "RimRftPlotCollection.h"
#include "RimSaturationPressurePlot.h"
#include "RimSaturationPressurePlotCollection.h"
#include "RimSimWellInViewCollection.h"
#include "RimStimPlanColors.h"
#include "RimSummaryCase.h"
#include "RimSummaryCaseCollection.h"
#include "RimSummaryCaseMainCollection.h"
#include "RimSummaryCrossPlotCollection.h"
#include "RimSummaryPlot.h"
#include "RimTextAnnotation.h"
#include "RimTextAnnotationInView.h"
#include "RimViewLinker.h"
#include "RimViewLinkerCollection.h"
#include "RimWellLogFile.h"
#include "RimWellLogPlot.h"
#include "RimWellLogPlotCollection.h"
#include "RimWellPathCollection.h"
#include "RimWellPathFracture.h"
#include "RimWellPltPlot.h"
#include "RimWellRftPlot.h"

#include "Riu3dSelectionManager.h"
#include "RiuDockWidgetTools.h"
#include "RiuFileDialogTools.h"
#include "RiuGuiTheme.h"
#include "RiuMainWindow.h"
#include "RiuMainWindowTools.h"
#include "RiuMdiMaximizeWindowGuard.h"
#include "RiuMessagePanel.h"
#include "RiuPlotMainWindow.h"
#include "RiuPlotMainWindowTools.h"
#include "RiuProcessMonitor.h"
#include "RiuRecentFileActionProvider.h"
#include "RiuViewer.h"

#include "DockManager.h"

#include "cafAppEnum.h"
#include "cafCmdExecCommandManager.h"
#include "cafEffectGenerator.h"
#include "cafFixedAtlasFont.h"
#include "cafPdmUiModelChangeDetector.h"
#include "cafPdmUiTreeView.h"
#include "cafProgressInfo.h"
#include "cafQTreeViewStateSerializer.h"
#include "cafSelectionManager.h"
#include "cafUiProcess.h"
#include "cafUtils.h"

#include "cvfProgramOptions.h"
#include "cvfqtUtils.h"

#include <QDebug>
#include <QDesktopWidget>
#include <QDir>
#include <QErrorMessage>
#include <QGridLayout>
#include <QKeyEvent>
#include <QMdiSubWindow>
#include <QMessageBox>
#include <QProcessEnvironment>
#include <QPushButton>
#include <QTextEdit>
#include <QTreeView>

#include <iostream>

#ifndef WIN32
#include <unistd.h> // for usleep
#endif // WIN32

#ifdef USE_UNIT_TESTS
#include "gtest/gtest.h"
#endif // USE_UNIT_TESTS

//==================================================================================================
///
/// \class RiaGuiApplication
///
/// Application class
///
//==================================================================================================

//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
bool RiaGuiApplication::isRunning()
{
    return dynamic_cast<RiaGuiApplication*>( RiaApplication::instance() ) != nullptr;
}

//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
RiaGuiApplication* RiaGuiApplication::instance()
{
    RiaGuiApplication* currentGuiApp = dynamic_cast<RiaGuiApplication*>( RiaApplication::instance() );
    CAF_ASSERT( currentGuiApp && "Should never be called from a method that isn't within the GUI context" );
    return currentGuiApp;
}

//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
RiaGuiApplication::RiaGuiApplication( int& argc, char** argv )
    : QApplication( argc, argv )
    , RiaApplication()
    , m_mainWindow( nullptr )
    , m_mainPlotWindow( nullptr )
{
    setWindowIcon( QIcon( ":/AppLogo48x48.png" ) );

    m_recentFileActionProvider = std::make_unique<RiuRecentFileActionProvider>();

    connect( this, SIGNAL( lastWindowClosed() ), SLOT( onLastWindowClosed() ) );
}

//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
RiaGuiApplication::~RiaGuiApplication()
{
    m_mainWindow.clear();
}

//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
bool RiaGuiApplication::saveProject()
{
    CVF_ASSERT( m_project );

    QString fileName;
    if ( !isProjectSavedToDisc() )
    {
        fileName = promptForProjectSaveAsFileName();
    }
    else
    {
        fileName = m_project->fileName();
    }
    return saveProjectAs( fileName );
}

//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
QString RiaGuiApplication::promptForProjectSaveAsFileName() const
{
    RiaGuiApplication* app = RiaGuiApplication::instance();

    QString startPath;
    if ( !m_project->fileName().isEmpty() )
    {
        startPath = m_project->fileName();
    }
    else
    {
        startPath = app->lastUsedDialogDirectory( "BINARY_GRID" );
        startPath += "/ResInsightProject.rsp";
    }

    QString fileName =
        RiuFileDialogTools::getSaveFileName( nullptr, tr( "Save File" ), startPath, tr( "Project Files (*.rsp);;All files(*.*)" ) );
    return fileName;
}

//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
bool RiaGuiApplication::askUserToSaveModifiedProject()
{
    if ( RiaPreferencesSystem::current()->showProjectChangedDialog() && caf::PdmUiModelChangeDetector::instance()->isModelChanged() )
    {
        QMessageBox msgBox;
        msgBox.setIcon( QMessageBox::Question );

        QString questionText;
        questionText = QString( "The current project is modified.\n\nDo you want to save the changes?" );

        msgBox.setText( questionText );
        msgBox.setStandardButtons( QMessageBox::Yes | QMessageBox::No | QMessageBox::Cancel );

        int ret = msgBox.exec();
        if ( ret == QMessageBox::Cancel )
        {
            return false;
        }
        else if ( ret == QMessageBox::Yes )
        {
            if ( !saveProject() )
            {
                return false;
            }
        }
        else
        {
            caf::PdmUiModelChangeDetector::instance()->reset();
        }
    }

    return true;
}

//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
bool RiaGuiApplication::saveProjectAs( const QString& fileName )
{
    QString errMsg;
    if ( !RiaApplication::saveProjectAs( fileName, &errMsg ) )
    {
        QMessageBox::warning( nullptr, "Error when saving project file", errMsg );
        return false;
    }

    return true;
}

//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
void RiaGuiApplication::storeTreeViewState()
{
    if ( m_mainWindow )
    {
        QStringList treeStates;
        QStringList treeIndexes;

        for ( auto& tv : m_mainWindow->projectTreeViews() )
        {
            QString treeViewState;
            tv->storeTreeViewStateToString( treeViewState );
            treeStates.append( treeViewState );

            QModelIndex mi = tv->treeView()->currentIndex();
            QString     encodedModelIndexString;
            caf::QTreeViewStateSerializer::encodeStringFromModelIndex( mi, encodedModelIndexString );
            treeIndexes.append( encodedModelIndexString );
        }

        project()->mainWindowTreeViewStates         = treeStates.join( RiaDefines::stringListSeparator() );
        project()->mainWindowCurrentModelIndexPaths = treeIndexes.join( RiaDefines::stringListSeparator() );
    }

    if ( m_mainPlotWindow )
    {
        QStringList treeStates;
        QStringList treeIndexes;

        for ( auto& tv : mainPlotWindow()->projectTreeViews() )
        {
            QString treeViewState;
            tv->storeTreeViewStateToString( treeViewState );
            treeStates.append( treeViewState );

            QModelIndex mi = tv->treeView()->currentIndex();
            QString     encodedModelIndexString;
            caf::QTreeViewStateSerializer::encodeStringFromModelIndex( mi, encodedModelIndexString );
            treeIndexes.append( encodedModelIndexString );
        }

        project()->plotWindowTreeViewStates         = treeStates.join( RiaDefines::stringListSeparator() );
        project()->plotWindowCurrentModelIndexPaths = treeIndexes.join( RiaDefines::stringListSeparator() );
    }
}

//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
void RiaGuiApplication::setWindowCaptionFromAppState()
{
    // The stuff being done here should really be handled by Qt automatically as a result of
    // setting applicationName and windowFilePath
    // Was unable to make this work in Qt4.4.0!

    QString capt = RI_APPLICATION_NAME;
#ifdef _DEBUG
    capt += " ##DEBUG##";
#endif

    {
        QString projFileName;

        if ( !m_project->fileName().isEmpty() )
        {
            QFileInfo fi( m_project->fileName() );
            projFileName = fi.fileName();
        }

        if ( projFileName.isEmpty() ) projFileName = "Untitled project";

        capt = projFileName + QString( "[*]" ) + QString( " - " ) + capt;
    }

    if ( m_mainWindow ) m_mainWindow->setWindowTitle( capt );
    if ( m_mainPlotWindow ) m_mainPlotWindow->setWindowTitle( capt );
}

//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
RimViewWindow* RiaGuiApplication::activePlotWindow() const
{
    RimViewWindow* viewWindow = nullptr;

    if ( m_mainPlotWindow )
    {
        QList<QMdiSubWindow*> subwindows = m_mainPlotWindow->subWindowList( QMdiArea::StackingOrder );
        if ( subwindows.size() > 0 )
        {
            viewWindow = RiuInterfaceToViewWindow::viewWindowFromWidget( subwindows.back()->widget() );
        }
    }

    return viewWindow;
}

//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
bool RiaGuiApplication::useShaders() const
{
    if ( !RiaPreferencesSystem::current()->useShaders() ) return false;

    bool isShadersSupported = true;
    if ( platformName() != "offscreen" ) // Avoid opengl access if we are in qt offscreen mode
    {
        isShadersSupported = caf::Viewer::isShadersSupported();
    }

    return isShadersSupported;
}

//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
RiaDefines::RINavigationPolicy RiaGuiApplication::navigationPolicy() const
{
    return m_preferences->navigationPolicy();
}

//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
void RiaGuiApplication::initialize()
{
    RiaApplication::initialize();

    applyGuiPreferences( nullptr );

    // Create main windows
    // The plot window is created to be able to set expanded state on created objects, but hidden by default
    getOrCreateAndShowMainWindow();
    getOrCreateMainPlotWindow();

    RiuGuiTheme::updateGuiTheme( m_preferences->guiTheme() );

    {
        auto logger = std::make_unique<RiuMessagePanelLogger>();
        logger->addMessagePanel( m_mainWindow->messagePanel() );
        logger->addMessagePanel( m_mainPlotWindow->messagePanel() );
        RiaLogging::setLoggerInstance( std::move( logger ) );

        RiaLogging::loggerInstance()->setLevel( int( RiaLogging::logLevelBasedOnPreferences() ) );
    }
    m_socketServer = new RiaSocketServer( this );
}

//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
RiaApplication::ApplicationStatus RiaGuiApplication::handleArguments( gsl::not_null<cvf::ProgramOptions*> progOpt )
{
    // Handling of the actual command line options
    // --------------------------------------------------------
    if ( cvf::Option o = progOpt->option( "ignoreArgs" ) )
    {
        return ApplicationStatus::KEEP_GOING;
    }

    if ( progOpt->option( "help" ) || progOpt->option( "?" ) )
    {
        this->showFormattedTextInMessageBoxOrConsole( "The current command line options in ResInsight are:\n" +
                                                      this->commandLineParameterHelp() );
        return RiaApplication::ApplicationStatus::EXIT_COMPLETED;
    }

    // Code generation
    // -----------------
    if ( cvf::Option o = progOpt->option( "generate" ) )
    {
        CVF_ASSERT( o.valueCount() == 1 );
        QString outputFile = cvfqt::Utils::toQString( o.value( 0 ) );

        QString errMsg;
        if ( !RiaApplication::generateCode( outputFile, &errMsg ) )
        {
            RiaLogging::error( QString( "Error: %1" ).arg( errMsg ) );
            return RiaApplication::ApplicationStatus::KEEP_GOING;
        }

        return RiaApplication::ApplicationStatus::EXIT_COMPLETED;
    }

    // Unit testing
    // --------------------------------------------------------
    if ( cvf::Option o = progOpt->option( "unittest" ) )
    {
        int testReturnValue = launchUnitTestsWithConsole();
        if ( testReturnValue == 0 )
        {
            return RiaApplication::ApplicationStatus::EXIT_COMPLETED;
        }
        else
        {
            RiaLogging::error( "Error running unit tests" );
            return RiaApplication::ApplicationStatus::EXIT_WITH_ERROR;
        }
    }

    if ( cvf::Option o = progOpt->option( "regressiontest" ) )
    {
        CVF_ASSERT( o.valueCount() == 1 );
        QString regressionTestPath = cvfqt::Utils::toQString( o.value( 0 ) );

        // Use a logger writing to stdout instead of message panel
        // This is useful when executing regression tests on a build server, and this is the reason for creating the
        // logger when parsing the command line options
        auto stdLogger = std::make_unique<RiaStdOutLogger>();
        stdLogger->setLevel( int( RILogLevel::RI_LL_DEBUG ) );

        RiaLogging::setLoggerInstance( std::move( stdLogger ) );

        RiaRegressionTestRunner::instance()->executeRegressionTests( regressionTestPath, QStringList() );
        return ApplicationStatus::EXIT_COMPLETED;
    }

    if ( cvf::Option o = progOpt->option( "updateregressiontestbase" ) )
    {
        CVF_ASSERT( o.valueCount() == 1 );
        QString regressionTestPath = cvfqt::Utils::toQString( o.value( 0 ) );
        RiaRegressionTestRunner::instance()->updateRegressionTest( regressionTestPath );
        return ApplicationStatus::EXIT_COMPLETED;
    }

    if ( cvf::Option o = progOpt->option( "startdir" ) )
    {
        CVF_ASSERT( o.valueCount() == 1 );
        setStartDir( cvfqt::Utils::toQString( o.value( 0 ) ) );
    }

    if ( cvf::Option o = progOpt->option( "size" ) )
    {
        int width  = o.safeValue( 0 ).toInt( -1 );
        int height = o.safeValue( 1 ).toInt( -1 );

        if ( width > 0 && height > 0 )
        {
            auto mainWindow = RiuMainWindow::instance();
            if ( mainWindow )
            {
                mainWindow->resize( width, height );
            }

            auto plotWindow = mainPlotWindow();
            if ( plotWindow )
            {
                plotWindow->resize( width, height );
            }
        }
    }

    int snapshotWidth  = -1;
    int snapshotHeight = -1;
    if ( cvf::Option o = progOpt->option( "snapshotsize" ) )
    {
        int width  = o.safeValue( 0 ).toInt( -1 );
        int height = o.safeValue( 1 ).toInt( -1 );

        if ( width > 0 && height > 0 )
        {
            snapshotWidth  = width;
            snapshotHeight = height;
        }
    }

    QString snapshotFolderFromCommandLine;
    if ( cvf::Option o = progOpt->option( "snapshotfolder" ) )
    {
        CVF_ASSERT( o.valueCount() == 1 );
        snapshotFolderFromCommandLine = cvfqt::Utils::toQString( o.value( 0 ) );
    }

    if ( cvf::Option o = progOpt->option( "summaryplot" ) )
    {
        RicSummaryPlotFeatureImpl::createSummaryPlotsFromArgumentLine( cvfqt::Utils::toQStringList( o.values() ) );
    }

    if ( cvf::Option o = progOpt->option( "openplotwindow" ) )
    {
        if ( m_mainWindow ) m_mainWindow->hide();
        getOrCreateAndShowMainPlotWindow();
    }

    QString projectFileName;

    if ( progOpt->hasOption( "last" ) )
    {
        projectFileName = preferences()->lastUsedProjectFileName;
    }

    if ( cvf::Option o = progOpt->option( "project" ) )
    {
        CVF_ASSERT( o.valueCount() == 1 );
        projectFileName = cvfqt::Utils::toQString( o.value( 0 ) );
    }

    if ( !projectFileName.isEmpty() )
    {
        if ( cvf::Option o = progOpt->option( "multiCaseSnapshots" ) )
        {
            QString              gridListFile = cvfqt::Utils::toQString( o.safeValue( 0 ) );
            std::vector<QString> gridFiles    = readFileListFromTextFile( gridListFile );
            runMultiCaseSnapshots( projectFileName, gridFiles, "multiCaseSnapshots" );

            return ApplicationStatus::EXIT_COMPLETED;
        }
    }

    if ( !projectFileName.isEmpty() )
    {
        cvf::ref<RiaProjectModifier>      projectModifier;
        RiaApplication::ProjectLoadAction projectLoadAction = RiaApplication::ProjectLoadAction::PLA_NONE;

        if ( cvf::Option o = progOpt->option( "replaceCase" ) )
        {
            if ( projectModifier.isNull() ) projectModifier = cvf::make_ref<RiaProjectModifier>();

            if ( o.valueCount() == 1 )
            {
                // One argument is available, use replace case for first occurrence in the project

                QString gridFileName = cvfqt::Utils::toQString( o.safeValue( 0 ) );
                projectModifier->setReplaceCaseFirstOccurrence( gridFileName );
            }
            else
            {
                size_t optionIdx = 0;
                while ( optionIdx < o.valueCount() )
                {
                    const int caseId       = o.safeValue( optionIdx++ ).toInt( -1 );
                    QString   gridFileName = cvfqt::Utils::toQString( o.safeValue( optionIdx++ ) );

                    if ( caseId != -1 && !gridFileName.isEmpty() )
                    {
                        projectModifier->setReplaceCase( caseId, gridFileName );
                    }
                }
            }
        }

        if ( cvf::Option o = progOpt->option( "replaceSourceCases" ) )
        {
            if ( projectModifier.isNull() ) projectModifier = cvf::make_ref<RiaProjectModifier>();

            if ( o.valueCount() == 1 )
            {
                // One argument is available, use replace case for first occurrence in the project

                std::vector<QString> gridFileNames = readFileListFromTextFile( cvfqt::Utils::toQString( o.safeValue( 0 ) ) );
                projectModifier->setReplaceSourceCasesFirstOccurrence( gridFileNames );
            }
            else
            {
                size_t optionIdx = 0;
                while ( optionIdx < o.valueCount() )
                {
                    const int            groupId       = o.safeValue( optionIdx++ ).toInt( -1 );
                    std::vector<QString> gridFileNames = readFileListFromTextFile( cvfqt::Utils::toQString( o.safeValue( optionIdx++ ) ) );

                    if ( groupId != -1 && !gridFileNames.empty() )
                    {
                        projectModifier->setReplaceSourceCasesById( groupId, gridFileNames );
                    }
                }
            }

            projectLoadAction = RiaApplication::ProjectLoadAction::PLA_CALCULATE_STATISTICS;
        }

        if ( cvf::Option o = progOpt->option( "replacePropertiesFolder" ) )
        {
            if ( projectModifier.isNull() ) projectModifier = cvf::make_ref<RiaProjectModifier>();

            if ( o.valueCount() == 1 )
            {
                QString propertiesFolder = cvfqt::Utils::toQString( o.safeValue( 0 ) );
                projectModifier->setReplacePropertiesFolderFirstOccurrence( propertiesFolder );
            }
            else
            {
                size_t optionIdx = 0;
                while ( optionIdx < o.valueCount() )
                {
                    const int caseId           = o.safeValue( optionIdx++ ).toInt( -1 );
                    QString   propertiesFolder = cvfqt::Utils::toQString( o.safeValue( optionIdx++ ) );

                    if ( caseId != -1 && !propertiesFolder.isEmpty() )
                    {
                        projectModifier->setReplacePropertiesFolder( caseId, propertiesFolder );
                    }
                }
            }
        }

        loadProject( projectFileName, projectLoadAction, projectModifier.p() );
    }

    if ( cvf::Option o = progOpt->option( "case" ) )
    {
        QStringList fileNames = RicImportGeneralDataFeature::fileNamesFromCaseNames( cvfqt::Utils::toQStringList( o.values() ) );

        bool createView = true;
        bool createPlot = true;

        RicImportGeneralDataFeature::OpenCaseResults results =
            RicImportGeneralDataFeature::openEclipseFilesFromFileNames( fileNames, createPlot, createView );
        if ( results && !results.eclipseSummaryFiles.empty() )
        {
            getOrCreateAndShowMainPlotWindow();
        }
    }

    if ( cvf::Option o = progOpt->option( "savesnapshots" ) )
    {
        bool snapshotViews = false;
        bool snapshotPlots = false;

        QStringList snapshotItemTexts = cvfqt::Utils::toQStringList( o.values() );
        if ( snapshotItemTexts.empty() )
        {
            // No options will keep backwards compatibility before we introduced snapshot of plots
            snapshotViews = true;
        }

        for ( const QString& s : snapshotItemTexts )
        {
            if ( s.toLower() == "all" )
            {
                snapshotViews = true;
                snapshotPlots = true;
            }
            else if ( s.toLower() == "views" )
            {
                snapshotViews = true;
            }
            else if ( s.toLower() == "plots" )
            {
                snapshotPlots = true;
            }
        }

        QString snapshotFolder;

        if ( snapshotFolderFromCommandLine.isEmpty() )
        {
            snapshotFolder = QDir::currentPath() + "/snapshots";
        }
        else
        {
            snapshotFolder = snapshotFolderFromCommandLine;
        }

        if ( snapshotPlots )
        {
            auto mainPlotWnd = mainPlotWindow();
            if ( mainPlotWnd )
            {
                mainPlotWnd->show();
                mainPlotWnd->raise();

                if ( snapshotHeight > -1 && snapshotWidth > -1 )
                {
                    RiuMainWindowTools::setWindowSizeOnWidgetsInMdiWindows( mainPlotWnd, snapshotWidth, snapshotHeight );
                }

                processEvents();

                RicSnapshotAllPlotsToFileFeature::exportSnapshotOfPlotsIntoFolder( snapshotFolder );
            }
        }

        if ( snapshotViews )
        {
            auto mainWnd = RiuMainWindow::instance();
            mainWnd->show();
            mainWnd->raise();

            if ( snapshotHeight > -1 && snapshotWidth > -1 )
            {
                RiuMainWindowTools::setFixedWindowSizeFor3dViews( mainWnd, snapshotWidth, snapshotHeight );
            }

            processEvents();

            RicSnapshotAllViewsToFileFeature::exportSnapshotOfViewsIntoFolder( snapshotFolder );
        }

        auto mainPlotWnd = mainPlotWindow();
        if ( mainPlotWnd )
        {
            mainPlotWnd->loadWinGeoAndDockToolBarLayout();
        }

        if ( RiuMainWindow::instance() ) RiuMainWindow::instance()->loadWinGeoAndDockToolBarLayout();

        return ApplicationStatus::EXIT_COMPLETED;
    }

    if ( cvf::Option o = progOpt->option( "commandFile" ) )
    {
        QString commandFile = cvfqt::Utils::toQString( o.safeValue( 0 ) );

        if ( !progOpt->hasOption( "startdir" ) )
        {
            QFileInfo commandFileInfo( commandFile );
            QString   commandDir = commandFileInfo.absolutePath();
            setStartDir( commandDir );
        }

        cvf::Option projectOption = progOpt->option( "commandFileProject" );
        cvf::Option caseOption    = progOpt->option( "commandFileReplaceCases" );
        if ( projectOption && caseOption )
        {
            projectFileName = cvfqt::Utils::toQString( projectOption.value( 0 ) );

            std::vector<int>     caseIds;
            std::vector<QString> caseListFiles;

            if ( caseOption.valueCount() == 1 )
            {
                caseListFiles.push_back( cvfqt::Utils::toQString( caseOption.safeValue( 0 ) ) );
            }
            else
            {
                size_t optionIdx = 0;
                while ( optionIdx < caseOption.valueCount() )
                {
                    const int caseId       = caseOption.safeValue( optionIdx++ ).toInt( -1 );
                    QString   caseListFile = cvfqt::Utils::toQString( caseOption.safeValue( optionIdx++ ) );

                    if ( caseId != -1 && !caseListFile.isEmpty() )
                    {
                        caseIds.push_back( caseId );
                        caseListFiles.push_back( caseListFile );
                    }
                }
            }

            if ( caseIds.empty() && !caseListFiles.empty() )
            {
                QString              caseListFile = caseListFiles[0];
                std::vector<QString> caseFiles    = readFileListFromTextFile( caseListFile );
                for ( const QString& caseFile : caseFiles )
                {
                    RiaProjectModifier projectModifier;
                    projectModifier.setReplaceCaseFirstOccurrence( caseFile );
                    loadProject( projectFileName, RiaApplication::ProjectLoadAction::PLA_NONE, &projectModifier );
                    executeCommandFile( commandFile );
                }
            }
            else
            {
                CVF_ASSERT( caseIds.size() == caseListFiles.size() );

                std::vector<std::vector<QString>> allCaseFiles;
                size_t                            maxFiles = 0;

                for ( size_t i = 0; i < caseIds.size(); ++i )
                {
                    std::vector<QString> caseFiles = readFileListFromTextFile( caseListFiles[i] );
                    allCaseFiles.push_back( caseFiles );
                    maxFiles = std::max( caseFiles.size(), maxFiles );
                }

                for ( size_t i = 0; i < caseIds.size(); ++i )
                {
                    RiaProjectModifier projectModifier;
                    for ( size_t j = 0; j < maxFiles; ++j )
                    {
                        if ( allCaseFiles[i].size() > j )
                        {
                            projectModifier.setReplaceCase( caseIds[i], allCaseFiles[i][j] );
                        }
                    }

                    loadProject( projectFileName, RiaApplication::ProjectLoadAction::PLA_NONE, &projectModifier );
                    executeCommandFile( commandFile );
                }
            }
        }
        else
        {
            executeCommandFile( commandFile );
        }
        return ApplicationStatus::EXIT_COMPLETED;
    }

    return ApplicationStatus::KEEP_GOING;
}

//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
int RiaGuiApplication::launchUnitTestsWithConsole()
{
    // Following code is taken from cvfAssert.cpp
#ifdef WIN32
    {
        // Allocate a new console for this app
        // Only one console can be associated with an app, so should fail if a console is already present.
        AllocConsole();

        FILE* consoleFilePointer;

        freopen_s( &consoleFilePointer, "CONOUT$", "w", stdout );
        freopen_s( &consoleFilePointer, "CONOUT$", "w", stderr );

        // Make cout, wcout, cin, wcin, wcerr, cerr, wclog and clog point to console as well
        std::ios::sync_with_stdio();
    }
#endif

    return launchUnitTests();
}

//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
RiuMainWindow* RiaGuiApplication::getOrCreateAndShowMainWindow()
{
    if ( !m_mainWindow )
    {
        createMainWindow();
    }
    else
    {
        m_mainWindow->show();
    }

    return m_mainWindow;
}

//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
RiuMainWindow* RiaGuiApplication::mainWindow()
{
    return m_mainWindow;
}

//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
RiuPlotMainWindow* RiaGuiApplication::getOrCreateMainPlotWindow()
{
    if ( !m_mainPlotWindow )
    {
        createMainPlotWindow();
        m_mainPlotWindow->initializeGuiNewProjectLoaded();
        loadAndUpdatePlotData();
    }
    return m_mainPlotWindow.get();
}

//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
void RiaGuiApplication::createMainWindow()
{
    CVF_ASSERT( m_mainWindow == nullptr );

    if ( RiaPreferences::current()->useUndoRedo() )
    {
        caf::CmdExecCommandManager::instance()->enableUndoCommandSystem( true );
    }

    m_mainWindow     = new RiuMainWindow;
    QString platform = cvf::System::is64Bit() ? "(64bit)" : "(32bit)";
    m_mainWindow->setWindowTitle( "ResInsight " + platform );
    m_mainWindow->setDefaultWindowSize();
    m_mainWindow->setDefaultToolbarVisibility();
    m_mainWindow->loadWinGeoAndDockToolBarLayout();
    m_mainWindow->showWindow();

    // if there is an existing logger, reconnect to it
    auto logger = dynamic_cast<RiuMessagePanelLogger*>( RiaLogging::loggerInstance() );
    if ( logger )
    {
        logger->addMessagePanel( m_mainWindow->messagePanel() );
    }
}

//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
void RiaGuiApplication::createMainPlotWindow()
{
    CVF_ASSERT( m_mainPlotWindow == nullptr );

    if ( RiaPreferences::current()->useUndoRedo() )
    {
        caf::CmdExecCommandManager::instance()->enableUndoCommandSystem( true );
    }

    m_mainPlotWindow = std::make_unique<RiuPlotMainWindow>();
    m_mainPlotWindow->setWindowTitle( "Plots - ResInsight" );
    m_mainPlotWindow->setDefaultWindowSize();
    m_mainPlotWindow->loadWinGeoAndDockToolBarLayout();
}

//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
RiuPlotMainWindow* RiaGuiApplication::getOrCreateAndShowMainPlotWindow()
{
    if ( !m_mainPlotWindow )
    {
        createMainPlotWindow();
        m_mainPlotWindow->initializeGuiNewProjectLoaded();
        loadAndUpdatePlotData();
    }

    if ( m_mainPlotWindow->isMinimized() )
    {
        m_mainPlotWindow->showNormal();
        m_mainPlotWindow->update();
    }
    else
    {
        m_mainPlotWindow->show();
    }

    m_mainPlotWindow->raise();
    m_mainPlotWindow->activateWindow();

    return m_mainPlotWindow.get();
}

//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
RiuPlotMainWindow* RiaGuiApplication::mainPlotWindow()
{
    return m_mainPlotWindow.get();
}

//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
RiuMainWindowBase* RiaGuiApplication::mainWindowByID( int mainWindowID )
{
    if ( mainWindowID == 0 )
        return m_mainWindow;
    else if ( mainWindowID == 1 )
        return m_mainPlotWindow.get();
    else
        return nullptr;
}

//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
RimViewWindow* RiaGuiApplication::activeViewWindow()
{
    RimViewWindow* viewWindow = nullptr;

    QWidget* mainWindowWidget = RiaGuiApplication::activeWindow();

    if ( dynamic_cast<RiuMainWindow*>( mainWindowWidget ) )
    {
        viewWindow = RiaGuiApplication::instance()->activeReservoirView();
    }
    else if ( dynamic_cast<RiuPlotMainWindow*>( mainWindowWidget ) )
    {
        RiuPlotMainWindow* mainPlotWindow = dynamic_cast<RiuPlotMainWindow*>( mainWindowWidget );

        QList<QMdiSubWindow*> subwindows = mainPlotWindow->subWindowList( QMdiArea::StackingOrder );
        if ( subwindows.size() > 0 )
        {
            viewWindow = RiuInterfaceToViewWindow::viewWindowFromWidget( subwindows.back()->widget() );
        }
    }

    return viewWindow;
}

//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
RiuMainWindowBase* RiaGuiApplication::activeMainWindow()
{
    QWidget*           mainWindowWidget = RiaGuiApplication::activeWindow();
    RiuMainWindowBase* mainWindow       = dynamic_cast<RiuMainWindowBase*>( mainWindowWidget );

    return mainWindow;
}

//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
bool RiaGuiApplication::isMain3dWindowVisible() const
{
    return m_mainWindow && m_mainWindow->isVisible();
}

//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
bool RiaGuiApplication::isMainPlotWindowVisible() const
{
    return m_mainPlotWindow && m_mainPlotWindow->isVisible();
}

//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
void RiaGuiApplication::addToRecentFiles( const QString& fileName )
{
    CVF_ASSERT( m_recentFileActionProvider && "The provider needs to be created before any attempts to use the recent file actions" );
    m_recentFileActionProvider->addFileName( fileName );
}

//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
std::vector<QAction*> RiaGuiApplication::recentFileActions() const
{
    CVF_ASSERT( m_recentFileActionProvider && "The provider needs to be created before any attempts to use the recent file actions" );
    return m_recentFileActionProvider->actions();
}

//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
void RiaGuiApplication::clearAllSelections()
{
    Riu3dSelectionManager::instance()->deleteAllItems( Riu3dSelectionManager::RUI_APPLICATION_GLOBAL );
    Riu3dSelectionManager::instance()->deleteAllItems( Riu3dSelectionManager::RUI_TEMPORARY );
    caf::SelectionManager::instance()->clearAll();
}

//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
void RiaGuiApplication::showFormattedTextInMessageBoxOrConsole( const QString& text )
{
    // Create a message dialog with cut/paste friendly text
    QDialog dlg;
    dlg.setModal( true );

    QGridLayout* layout = new QGridLayout;
    dlg.setLayout( layout );

    QTextEdit* textEdit = new QTextEdit( &dlg );
    layout->addWidget( textEdit, 0, 1, 1, 2 );
    layout->setColumnStretch( 1, 3 );

    QPushButton* okButton = new QPushButton;
    okButton->setText( "Ok" );
    layout->addWidget( okButton, 2, 2 );
    QObject::connect( okButton, SIGNAL( clicked() ), &dlg, SLOT( accept() ) );

    // Convert text to text edit friendly format
    QString formattedText = text;
    formattedText.replace( "&", "&amp;" );
    formattedText.replace( "<", "&lt;" );
    formattedText.replace( ">", "&gt;" );
    formattedText = QString( "<pre>%1</pre>" ).arg( formattedText );

    textEdit->setText( formattedText );
    textEdit->setReadOnly( true );

    // Resize dialog to fit text etc.
    textEdit->document()->adjustSize();
    QSizeF docSize = textEdit->document()->size();
    dlg.resize( 20 + docSize.width() + 2 * layout->margin(),
                20 + docSize.height() + 2 * layout->margin() + layout->spacing() + okButton->sizeHint().height() );

    dlg.exec();
}

//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
void RiaGuiApplication::invokeProcessEvents( QEventLoop::ProcessEventsFlags flags )
{
    processEvents( flags );
}

//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
void RiaGuiApplication::onFileSuccessfullyLoaded( const QString& fileName, RiaDefines::ImportFileType fileType )
{
    if ( uint( fileType ) & uint( RiaDefines::ImportFileType::ANY_ECLIPSE_FILE ) )
    {
        if ( fileType == RiaDefines::ImportFileType::ECLIPSE_SUMMARY_FILE )
        {
            auto plotWindow = getOrCreateAndShowMainPlotWindow();
            plotWindow->raise();
        }
        else if ( fileType != RiaDefines::ImportFileType::RESINSIGHT_PROJECT_FILE )
        {
            auto mainWindow = getOrCreateAndShowMainWindow();
            mainWindow->raise();
        }
    }

    if ( !RiaGuiApplication::hasValidProjectFileExtension( fileName ) )
    {
        caf::PdmUiModelChangeDetector::instance()->setModelChanged();
    }
}

//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
void RiaGuiApplication::onProjectBeingOpened()
{
    // When importing a project, do not maximize the first MDI window to be created
    m_maximizeWindowGuard = std::make_unique<RiuMdiMaximizeWindowGuard>();

    if ( m_mainWindow ) m_mainWindow->setBlockSubWindowActivatedSignal( true );
    if ( mainPlotWindow() ) mainPlotWindow()->setBlockSubWindowActivatedSignal( true );
}

//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
void RiaGuiApplication::onProjectOpeningError( const QString& errMsg )
{
    RiaLogging::errorInMessageBox( nullptr, "Error when opening project file", errMsg );
    if ( m_mainWindow ) m_mainWindow->setPdmRoot( nullptr );
}

//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
void RiaGuiApplication::onProjectOpened()
{
    if ( m_project->show3DWindow() )
    {
        getOrCreateAndShowMainWindow();
    }
    else
    {
        if ( m_mainWindow ) m_mainWindow->hide();
    }

    if ( m_project->showPlotWindow() )
    {
        if ( !m_mainPlotWindow )
        {
            createMainPlotWindow();
            m_mainPlotWindow->show();
        }
        else
        {
            m_mainPlotWindow->show();
            m_mainPlotWindow->raise();
        }
    }
    else if ( mainPlotWindow() )
    {
        mainPlotWindow()->hide();
    }

    loadAndUpdatePlotData();

    if ( m_mainWindow )
    {
        m_mainWindow->initializeGuiNewProjectLoaded();
    }
    if ( m_mainPlotWindow )
    {
        m_mainPlotWindow->initializeGuiNewProjectLoaded();
    }

    setWindowCaptionFromAppState();

    m_maximizeWindowGuard.reset();

    processEvents();

    if ( m_mainWindow ) m_mainWindow->setBlockSubWindowActivatedSignal( false );
    if ( mainPlotWindow() ) mainPlotWindow()->setBlockSubWindowActivatedSignal( false );

    // Make sure to process events before this function to avoid strange Qt crash
    RiuPlotMainWindowTools::refreshToolbars();

    if ( m_project->showPlotWindow() && m_project->showPlotWindowOnTop() )
    {
        m_mainPlotWindow->raise();
        m_mainPlotWindow->activateWindow();
    }
}

//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
void RiaGuiApplication::onProjectBeingClosed()
{
    RicHoloLensSessionManager::instance()->terminateSession();
    RicHoloLensSessionManager::refreshToolbarState();

    RiaViewRedrawScheduler::instance()->clearViewsScheduledForUpdate();
    RiaPlotWindowRedrawScheduler::instance()->clearAllScheduledUpdates();

    RiaGuiApplication::clearAllSelections();

    if ( m_mainWindow ) m_mainWindow->cleanupGuiBeforeProjectClose();
    if ( m_mainPlotWindow ) m_mainPlotWindow->cleanupGuiBeforeProjectClose();

    caf::EffectGenerator::clearEffectCache();
}

//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
void RiaGuiApplication::onProjectClosed()
{
    if ( m_mainWindow ) m_mainWindow->initializeGuiNewProjectLoaded();
    if ( m_mainPlotWindow ) m_mainPlotWindow->initializeGuiNewProjectLoaded();

    setWindowCaptionFromAppState();

    processEvents();
}

//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
void RiaGuiApplication::onProjectBeingSaved()
{
    setLastUsedDialogDirectory( "BINARY_GRID", QFileInfo( m_project->fileName() ).absolutePath() );
    storeTreeViewState();

    RiaSummaryTools::summaryCaseMainCollection()->onProjectBeingSaved();
}

//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
void RiaGuiApplication::onProjectSaved()
{
    setWindowCaptionFromAppState();
    m_recentFileActionProvider->addFileName( m_project->fileName() );
    caf::PdmUiModelChangeDetector::instance()->reset();
}

//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
void RiaGuiApplication::applyGuiPreferences( const RiaPreferences*                         oldPreferences,
                                             const std::vector<caf::FontHolderInterface*>& defaultFontObjects )
{
    if ( m_activeReservoirView && m_activeReservoirView->viewer() )
    {
        m_activeReservoirView->viewer()->updateNavigationPolicy();
        m_activeReservoirView->viewer()->enablePerfInfoHud( RiaPreferencesSystem::current()->show3dInformation() );
    }

    if ( useShaders() )
    {
        caf::EffectGenerator::setRenderingMode( caf::EffectGenerator::SHADER_BASED );
    }
    else
    {
        caf::EffectGenerator::setRenderingMode( caf::EffectGenerator::FIXED_FUNCTION );
    }

    if ( m_mainWindow )
    {
        for ( auto& tv : m_mainWindow->projectTreeViews() )
        {
            tv->enableAppendOfClassNameToUiItemText( RiaPreferencesSystem::current()->appendClassNameToUiText() );
        }
    }
    if ( mainPlotWindow() )
    {
        for ( auto& tv : mainPlotWindow()->projectTreeViews() )
        {
            tv->enableAppendOfClassNameToUiItemText( RiaPreferencesSystem::current()->appendClassNameToUiText() );
        }
    }

    for ( auto fontObject : defaultFontObjects )
    {
        fontObject->updateFonts();
    }

    if ( this->project() )
    {
        std::vector<RimViewWindow*> allViewWindows;
        project()->descendantsIncludingThisOfType( allViewWindows );

        RimWellPathCollection* wellPathCollection = this->project()->activeOilField()->wellPathCollection();

        bool existingViewsWithDifferentMeshLines = false;
        bool existingViewsWithCustomColors       = false;
        bool existingViewsWithCustomZScale       = false;
        if ( oldPreferences )
        {
            for ( auto viewWindow : allViewWindows )
            {
                auto rim3dView = dynamic_cast<Rim3dView*>( viewWindow );
                if ( rim3dView )
                {
                    if ( m_preferences->defaultMeshModeType() != oldPreferences->defaultMeshModeType() &&
                         rim3dView->meshMode() != oldPreferences->defaultMeshModeType() &&
                         rim3dView->meshMode() != m_preferences->defaultMeshModeType() )
                    {
                        existingViewsWithDifferentMeshLines = true;
                    }
                    if ( m_preferences->defaultViewerBackgroundColor() != oldPreferences->defaultViewerBackgroundColor() &&
                         rim3dView->backgroundColor() != oldPreferences->defaultViewerBackgroundColor() &&
                         rim3dView->backgroundColor() != m_preferences->defaultViewerBackgroundColor() )
                    {
                        existingViewsWithCustomColors = true;
                    }
                    if ( m_preferences->defaultScaleFactorZ() != oldPreferences->defaultScaleFactorZ() &&
                         rim3dView->scaleZ() != oldPreferences->defaultScaleFactorZ() &&
                         rim3dView->scaleZ() != m_preferences->defaultScaleFactorZ() )
                    {
                        existingViewsWithCustomZScale = true;
                    }

                    RimEclipseView* eclipseView = dynamic_cast<RimEclipseView*>( rim3dView );
                    if ( eclipseView )
                    {
                        if ( m_preferences->defaultWellLabelColor() != oldPreferences->defaultWellLabelColor() &&
                             eclipseView->wellCollection()->wellLabelColor() != oldPreferences->defaultWellLabelColor() &&
                             eclipseView->wellCollection()->wellLabelColor() != m_preferences->defaultWellLabelColor() )
                        {
                            existingViewsWithCustomColors = true;
                        }
                    }
                }
            }

            if ( oldPreferences->defaultWellLabelColor() != wellPathCollection->wellPathLabelColor() )
            {
                existingViewsWithCustomColors = true;
            }
        }

        bool applySettingsToAllViews = false;
        if ( existingViewsWithCustomColors || existingViewsWithCustomZScale || existingViewsWithDifferentMeshLines )
        {
            QStringList changedData;
            if ( existingViewsWithDifferentMeshLines ) changedData << "Mesh Visibility";
            if ( existingViewsWithCustomColors ) changedData << "Colors";
            if ( existingViewsWithCustomZScale ) changedData << "Z-Scale";

            QString listString = changedData.takeLast();
            if ( !changedData.empty() )
            {
                listString = changedData.join( ", " ) + " and " + listString;
            }

            QMessageBox::StandardButton reply;
            reply                   = QMessageBox::question( activeMainWindow(),
                                           QString( "Apply %1 to Existing Views or Plots?" ).arg( listString ),
                                           QString( "You have changed default %1 and have existing views or plots with "
                                                                      "different settings.\n" )
                                                   .arg( listString ) +
                                               QString( "Do you want to apply the new default settings to all existing "
                                                                          "views?" ),
                                           QMessageBox::Ok | QMessageBox::Cancel );
            applySettingsToAllViews = ( reply == QMessageBox::Ok );
        }

        for ( auto viewWindow : allViewWindows )
        {
            auto rim3dView = dynamic_cast<Rim3dView*>( viewWindow );
            if ( rim3dView )
            {
                if ( oldPreferences && ( applySettingsToAllViews || rim3dView->meshMode() == oldPreferences->defaultMeshModeType() ) )
                {
                    rim3dView->meshMode = m_preferences->defaultMeshModeType();
                }

                if ( oldPreferences &&
                     ( applySettingsToAllViews || rim3dView->backgroundColor() == oldPreferences->defaultViewerBackgroundColor() ) )
                {
                    rim3dView->setBackgroundColor( m_preferences->defaultViewerBackgroundColor() );
                    rim3dView->applyBackgroundColorAndFontChanges();
                }

                if ( oldPreferences && ( applySettingsToAllViews || rim3dView->scaleZ() == oldPreferences->defaultScaleFactorZ() ) )
                {
                    rim3dView->setScaleZ( m_preferences->defaultScaleFactorZ() );
                    rim3dView->updateScaling();
                    if ( rim3dView == activeViewWindow() )
                    {
                        if ( RiuMainWindow::instance() ) RiuMainWindow::instance()->updateScaleValue();
                    }
                }

                RimEclipseView* eclipseView = dynamic_cast<RimEclipseView*>( rim3dView );
                if ( eclipseView )
                {
                    if ( oldPreferences && ( applySettingsToAllViews ||
                                             eclipseView->wellCollection()->wellLabelColor() == oldPreferences->defaultWellLabelColor() ) )
                    {
                        eclipseView->wellCollection()->wellLabelColor = m_preferences->defaultWellLabelColor();
                    }
                    eclipseView->scheduleReservoirGridGeometryRegen();
                }
                rim3dView->scheduleCreateDisplayModelAndRedraw();
            }
        }

        if ( oldPreferences )
        {
            bool matchingColor = wellPathCollection->wellPathLabelColor() == oldPreferences->defaultWellLabelColor();
            if ( applySettingsToAllViews || matchingColor )
            {
                wellPathCollection->wellPathLabelColor = oldPreferences->defaultWellLabelColor();
            }

            if ( oldPreferences->defaultPlotFontSize() != m_preferences->defaultPlotFontSize() )
            {
                if ( m_mainWindow )
                {
                    m_mainWindow->applyFontSizesToDockedPlots();
                }
            }

            if ( oldPreferences->defaultPageLayout() != m_preferences->defaultPageLayout() )
            {
                for ( RimViewWindow* viewWindow : allViewWindows )
                {
                    RimPlotWindow* plotWindow = dynamic_cast<RimPlotWindow*>( viewWindow );
                    if ( plotWindow )
                    {
                        plotWindow->updateLayout();
                    }
                }
            }
        }

        std::vector<caf::PdmUiItem*> uiEditorsToUpdate;
        caf::SelectionManager::instance()->selectedItems( uiEditorsToUpdate );

        for ( caf::PdmUiItem* uiItem : uiEditorsToUpdate )
        {
            uiItem->updateConnectedEditors();
        }
    }
    caf::PdmUiItem::enableExtraDebugText( RiaPreferencesSystem::current()->appendFieldKeywordToToolTipText() );

    if ( oldPreferences )
    {
        onGuiPreferencesChanged();
    }
}

//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
int RiaGuiApplication::applicationResolution()
{
    return RiaGuiApplication::instance()->desktop()->logicalDpiX();
}

//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
void RiaGuiApplication::startMonitoringWorkProgress( caf::UiProcess* uiProcess )
{
    CAF_ASSERT( m_mainWindow );
    m_mainWindow->processMonitor()->startMonitorWorkProcess( uiProcess );
}

//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
void RiaGuiApplication::stopMonitoringWorkProgress()
{
    CAF_ASSERT( m_mainWindow );
    m_mainWindow->processMonitor()->stopMonitorWorkProcess();
}

//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
void RiaGuiApplication::slotWorkerProcessFinished( int exitCode, QProcess::ExitStatus exitStatus )
{
    if ( m_mainWindow == nullptr ) return;

    m_mainWindow->processMonitor()->stopMonitorWorkProcess();

    QProcessEnvironment processEnvironment = m_workerProcess->processEnvironment();

    // Execute delete later so that other slots that are hooked up
    // get a chance to run before we delete the object
    if ( m_workerProcess )
    {
        m_workerProcess->close();
    }
    m_workerProcess = nullptr;

    // Always make sure the command objects are executed before any return statement
    executeCommandObjects();

    // Either the work process crashed or was aborted by the user
    if ( exitStatus == QProcess::CrashExit )
    {
        //    MFLog::error("Simulation execution crashed or was aborted.");
        m_runningWorkerProcess = false;
        return;
    }

    // Exit code != 0 means we have an error
    if ( exitCode != 0 )
    {
        //  MFLog::error(QString("Simulation execution failed (exit code %1).").arg(exitCode));
        m_runningWorkerProcess = false;
        return;
    }

    // If multiple cases are present, invoke launchProcess() which will set next current case, and run script on this case
    if ( !m_scriptCaseIds.empty() )
    {
        launchProcess( m_currentProgram, m_currentArguments, processEnvironment );
    }
    else
    {
        // Disable concept of current case
        m_currentScriptCaseId  = -1;
        m_runningWorkerProcess = false;
    }
}

//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
void RiaGuiApplication::onLastWindowClosed()
{
    // Qt can send the lastWindowClosed signal multiple times. Disconnect to avoid reentry.
    QObject::disconnect( this, SIGNAL( lastWindowClosed() ), this, SLOT( onLastWindowClosed() ) );

    closeProject();
    quit();
}

//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
void RiaGuiApplication::runMultiCaseSnapshots( const QString&       templateProjectFileName,
                                               std::vector<QString> gridFileNames,
                                               const QString&       snapshotFolderName )
{
    if ( !m_mainWindow ) return;

    QByteArray curState = m_mainWindow->dockManager()->saveState( 0 );
    m_mainWindow->dockManager()->restoreState( RiuDockWidgetTools::defaultDockState( RiuDockWidgetTools::dockStateHideAll3DWindowName() ) );

    const size_t numGridFiles = gridFileNames.size();
    for ( size_t i = 0; i < numGridFiles; i++ )
    {
        QString gridFn = gridFileNames[i];

        RiaProjectModifier modifier;
        modifier.setReplaceCaseFirstOccurrence( gridFn );

        bool loadOk = loadProject( templateProjectFileName, ProjectLoadAction::PLA_NONE, &modifier );
        if ( loadOk )
        {
            RicSnapshotAllViewsToFileFeature::exportSnapshotOfViewsIntoFolder( snapshotFolderName );
        }
    }
    m_mainWindow->dockManager()->restoreState( curState );
}

//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
bool RiaGuiApplication::notify( QObject* receiver, QEvent* event )
{
    // Pre-allocating a memory exhaustion message
    // Doing som e trickery to avoid deadlock, as creating a messagebox actually triggers a call to this notify method.

    static QMessageBox* memoryExhaustedBox   = nullptr;
    static bool         allocatingMessageBox = false;
    if ( !memoryExhaustedBox && !allocatingMessageBox )
    {
        allocatingMessageBox = true;
        memoryExhaustedBox   = new QMessageBox( QMessageBox::Critical,
                                              "ResInsight Exhausted Memory",
                                              "Memory is Exhausted!\n ResInsight could not allocate the memory needed, and is now "
                                                "unstable and will probably crash soon." );
    }

    bool done = false;
    try
    {
        if ( event->type() == QEvent::KeyPress )
        {
            if ( activeWindow() != mainWindow() )
            {
                QKeyEvent*     keyEvent = static_cast<QKeyEvent*>( event );
                RimPlotWindow* plot     = dynamic_cast<RimPlotWindow*>( activePlotWindow() );
                if ( plot ) done = plot->handleGlobalKeyEvent( keyEvent );
            }
        }
        else if ( event->type() == QEvent::Wheel )
        {
            if ( activeWindow() != mainWindow() )
            {
                QWheelEvent*   wheelEvent = static_cast<QWheelEvent*>( event );
                RimPlotWindow* plot       = dynamic_cast<RimPlotWindow*>( activePlotWindow() );
                if ( plot ) done = plot->handleGlobalWheelEvent( wheelEvent );
            }
        }
        if ( !done )
        {
            done = QApplication::notify( receiver, event );
        }
    }
    catch ( const std::bad_alloc& )
    {
        done = true;
        if ( memoryExhaustedBox ) memoryExhaustedBox->exec();
        std::cout << "ResInsight: Memory is Exhausted!\n ResInsight could not allocate the memory needed, and is now "
                     "unstable "
                     "and will probably crash soon."
                  << std::endl;
        // If we really want to crash instead of limping forward:
        // throw;
    }

    return done;
}
