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

#include "RimTernaryLegendConfig.h"

#include "RiaApplication.h"
#include "RiaColorTables.h"
#include "RiaNumericalTools.h"
#include "RiaPreferences.h"
#include "RiaResultNames.h"

#include "RimEclipseView.h"
#include "RimIntersectionCollection.h"
#include "RimViewLinker.h"

#include "RivTernarySaturationOverlayItem.h"
#include "RivTernaryScalarMapper.h"

#include "cafPdmUiPushButtonEditor.h"
#include "cafPdmUiTextEditor.h"

#include "cvfqtUtils.h"

#include <cmath>

CAF_PDM_SOURCE_INIT( RimTernaryLegendConfig, "RimTernaryLegendConfig" );

//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
RimTernaryLegendConfig::RimTernaryLegendConfig()
{
    CAF_PDM_InitObject( "Ternary Color Legend", ":/Legend.png" );
    CAF_PDM_InitField( &m_showLegend, "ShowTernaryLegend", true, "Show Ternary Legend" );
    m_showLegend.uiCapability()->setUiHidden( true );
    CAF_PDM_InitField( &precision, "Precision", 2, "Significant digits", "", "The number of significant digits displayed in the legend numbers", "" );
    CAF_PDM_InitField( &rangeMode,
                       "RangeType",
                       RangeModeEnum( RangeModeType::USER_DEFINED ),
                       "Range type",
                       "",
                       "Switches between automatic and user defined range on the legend",
                       "" );

    CAF_PDM_InitFieldNoDefault( &applyLocalMinMax, "m_applyLocalMinMax", "" );
    caf::PdmUiPushButtonEditor::configureEditorLabelLeft( &applyLocalMinMax );
    applyLocalMinMax = false;

    CAF_PDM_InitFieldNoDefault( &applyGlobalMinMax, "m_applyGlobalMinMax", "" );
    caf::PdmUiPushButtonEditor::configureEditorLabelLeft( &applyGlobalMinMax );
    applyGlobalMinMax = false;

    CAF_PDM_InitFieldNoDefault( &applyFullRangeMinMax, "m_applyFullRangeMinMax", "" );
    caf::PdmUiPushButtonEditor::configureEditorLabelLeft( &applyFullRangeMinMax );
    applyFullRangeMinMax = false;

    CAF_PDM_InitFieldNoDefault( &ternaryRangeSummary, "ternaryRangeSummary", "Range summary" );
    ternaryRangeSummary.uiCapability()->setUiEditorTypeName( caf::PdmUiTextEditor::uiEditorTypeName() );
    ternaryRangeSummary.uiCapability()->setUiLabelPosition( caf::PdmUiItemInfo::TOP );

    CAF_PDM_InitField( &userDefinedMaxValueSoil, "UserDefinedMaxSoil", 1.0, "Max", "", "Min value of the legend", "" );
    CAF_PDM_InitField( &userDefinedMinValueSoil, "UserDefinedMinSoil", 0.0, "Min", "", "Max value of the legend", "" );

    CAF_PDM_InitField( &userDefinedMaxValueSgas, "UserDefinedMaxSgas", 1.0, "Max", "", "Min value of the legend", "" );
    CAF_PDM_InitField( &userDefinedMinValueSgas, "UserDefinedMinSgas", 0.0, "Min", "", "Max value of the legend", "" );

    CAF_PDM_InitField( &userDefinedMaxValueSwat, "UserDefinedMaxSwat", 1.0, "Max", "", "Min value of the legend", "" );
    CAF_PDM_InitField( &userDefinedMinValueSwat, "UserDefinedMinSwat", 0.0, "Min", "", "Max value of the legend", "" );

    m_globalAutoMin.resize( 3, 0.0 );
    m_globalAutoMax.resize( 3, 1.0 );
    m_localAutoMin.resize( 3, 0.0 );
    m_localAutoMax.resize( 3, 1.0 );

    m_scalarMapper = new RivTernaryScalarMapper( RiaColorTables::undefinedCellColor() );

    recreateLegend();
    updateLegend();
}

//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
RimTernaryLegendConfig::~RimTernaryLegendConfig()
{
}

//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
void RimTernaryLegendConfig::fieldChangedByUi( const caf::PdmFieldHandle* changedField, const QVariant& oldValue, const QVariant& newValue )
{
    if ( changedField == &applyLocalMinMax )
    {
        userDefinedMaxValueSoil = m_localAutoMax[TERNARY_SOIL_IDX];
        userDefinedMinValueSoil = m_localAutoMin[TERNARY_SOIL_IDX];
        userDefinedMaxValueSgas = m_localAutoMax[TERNARY_SGAS_IDX];
        userDefinedMinValueSgas = m_localAutoMin[TERNARY_SGAS_IDX];
        userDefinedMaxValueSwat = m_localAutoMax[TERNARY_SWAT_IDX];
        userDefinedMinValueSwat = m_localAutoMin[TERNARY_SWAT_IDX];

        applyLocalMinMax = false;
    }
    else if ( changedField == &applyGlobalMinMax )
    {
        userDefinedMaxValueSoil = m_globalAutoMax[TERNARY_SOIL_IDX];
        userDefinedMinValueSoil = m_globalAutoMin[TERNARY_SOIL_IDX];
        userDefinedMaxValueSgas = m_globalAutoMax[TERNARY_SGAS_IDX];
        userDefinedMinValueSgas = m_globalAutoMin[TERNARY_SGAS_IDX];
        userDefinedMaxValueSwat = m_globalAutoMax[TERNARY_SWAT_IDX];
        userDefinedMinValueSwat = m_globalAutoMin[TERNARY_SWAT_IDX];

        applyGlobalMinMax = false;
    }
    else if ( changedField == &applyFullRangeMinMax )
    {
        userDefinedMaxValueSoil = 1.0;
        userDefinedMinValueSoil = 0.0;
        userDefinedMaxValueSgas = 1.0;
        userDefinedMinValueSgas = 0.0;
        userDefinedMaxValueSwat = 1.0;
        userDefinedMinValueSwat = 0.0;

        applyFullRangeMinMax = false;
    }

    updateLabelText();
    updateLegend();

    auto view = firstAncestorOrThisOfType<RimGridView>();
    if ( view )
    {
        RimViewLinker* viewLinker = view->assosiatedViewLinker();
        if ( viewLinker )
        {
            viewLinker->updateCellResult();
        }

        view->updateDisplayModelForCurrentTimeStepAndRedraw();
        view->intersectionCollection()->scheduleCreateDisplayModelAndRedraw2dIntersectionViews();
    }
}

//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
void RimTernaryLegendConfig::updateLegend()
{
    double soilLower = 0.0;
    double soilUpper = 1.0;
    double sgasLower = 0.0;
    double sgasUpper = 1.0;
    double swatLower = 0.0;
    double swatUpper = 1.0;

    ternaryRanges( soilLower, soilUpper, sgasLower, sgasUpper, swatLower, swatUpper );
    m_scalarMapper->setTernaryRanges( soilLower, soilUpper, sgasLower, sgasUpper );

    cvf::String soilRange;
    cvf::String sgasRange;
    cvf::String swatRange;

    int numberPrecision = 1;
    {
        QString tmpString = QString::number( soilLower, 'g', numberPrecision ) + " - " + QString::number( soilUpper, 'g', numberPrecision );
        soilRange         = cvfqt::Utils::toString( tmpString );
    }

    {
        QString tmpString = QString::number( sgasLower, 'g', numberPrecision ) + " - " + QString::number( sgasUpper, 'g', numberPrecision );
        sgasRange         = cvfqt::Utils::toString( tmpString );
    }

    {
        QString tmpString = QString::number( swatLower, 'g', numberPrecision ) + " - " + QString::number( swatUpper, 'g', numberPrecision );
        swatRange         = cvfqt::Utils::toString( tmpString );
    }

    if ( !m_legend.isNull() )
    {
        m_legend->setRangeText( soilRange, sgasRange, swatRange );

        m_legend->enableBackground( RiaPreferences::current()->showLegendBackground() );
    }
}

//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
void RimTernaryLegendConfig::setAutomaticRanges( TernaryArrayIndex ternaryIndex, double globalMin, double globalMax, double localMin, double localMax )
{
    double candidateGlobalAutoMin = RiaNumericalTools::roundToNumSignificantDigitsFloor( globalMin, precision );
    double candidateGlobalAutoMax = RiaNumericalTools::roundToNumSignificantDigitsCeil( globalMax, precision );

    double candidateLocalAutoMin = RiaNumericalTools::roundToNumSignificantDigitsFloor( localMin, precision );
    double candidateLocalAutoMax = RiaNumericalTools::roundToNumSignificantDigitsCeil( localMax, precision );

    m_globalAutoMin[ternaryIndex] = candidateGlobalAutoMin;
    m_globalAutoMax[ternaryIndex] = candidateGlobalAutoMax;
    m_localAutoMin[ternaryIndex]  = candidateLocalAutoMin;
    m_localAutoMax[ternaryIndex]  = candidateLocalAutoMax;

    updateLabelText();
}

//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
void RimTernaryLegendConfig::onRecreateLegend()
{
    // Due to possible visualization bug, we need to recreate the legend if the last viewer
    // has been removed, (and thus the opengl resources has been deleted) The text in
    // the legend disappeared because of this, so workaround: recreate the legend when needed:

    int        fontSize = this->fontSize();
    cvf::Font* font     = RiaApplication::instance()->sceneFont( fontSize );
    m_legend            = new RivTernarySaturationOverlayItem( font );
    m_legend->setLayout( cvf::OverlayItem::VERTICAL, cvf::OverlayItem::BOTTOM_LEFT );

    updateLegend();
}

//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
void RimTernaryLegendConfig::setUiValuesFromLegendConfig( const RimTernaryLegendConfig* otherLegendConfig )
{
    QString serializedObjectString = otherLegendConfig->writeObjectToXmlString();
    readObjectFromXmlString( serializedObjectString, caf::PdmDefaultObjectFactory::instance() );
    updateLegend();
}

//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
void RimTernaryLegendConfig::defineUiOrdering( QString uiConfigName, caf::PdmUiOrdering& uiOrdering )
{
    caf::PdmUiOrdering* formatGr = uiOrdering.addNewGroup( "Format" );
    formatGr->add( &precision );
    formatGr->add( &rangeMode );

    if ( rangeMode == RangeModeType::USER_DEFINED )
    {
        caf::PdmUiOrdering* ternaryGroupContainer = uiOrdering.addNewGroup( "Ternary " );
        {
            caf::PdmUiOrdering* ternaryGroup = ternaryGroupContainer->addNewGroup( RiaResultNames::sgas() );
            ternaryGroup->add( &userDefinedMinValueSgas );
            ternaryGroup->add( &userDefinedMaxValueSgas );
        }

        {
            caf::PdmUiOrdering* ternaryGroup = ternaryGroupContainer->addNewGroup( RiaResultNames::swat() );
            ternaryGroup->add( &userDefinedMinValueSwat );
            ternaryGroup->add( &userDefinedMaxValueSwat );
        }

        {
            caf::PdmUiOrdering* ternaryGroup = ternaryGroupContainer->addNewGroup( RiaResultNames::soil() );
            ternaryGroup->add( &userDefinedMinValueSoil );
            ternaryGroup->add( &userDefinedMaxValueSoil );
        }

        ternaryGroupContainer->add( &applyLocalMinMax );
        ternaryGroupContainer->add( &applyGlobalMinMax );
        ternaryGroupContainer->add( &applyFullRangeMinMax );
    }
    else
    {
        caf::PdmUiOrdering* group = uiOrdering.addNewGroup( "Summary" );
        group->add( &ternaryRangeSummary );
    }

    uiOrdering.skipRemainingFields( true );
}

//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
bool RimTernaryLegendConfig::showLegend() const
{
    return m_showLegend;
}

//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
void RimTernaryLegendConfig::setTitle( const QString& title )
{
    m_legend->setTitle( cvfqt::Utils::toString( title ) );
}

//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
void RimTernaryLegendConfig::defineEditorAttribute( const caf::PdmFieldHandle* field, QString uiConfigName, caf::PdmUiEditorAttribute* attribute )
{
    if ( &applyLocalMinMax == field )
    {
        caf::PdmUiPushButtonEditorAttribute* attrib = dynamic_cast<caf::PdmUiPushButtonEditorAttribute*>( attribute );
        if ( attrib )
        {
            attrib->m_buttonText = "Apply local min/max values";
        }
    }
    else if ( &applyGlobalMinMax == field )
    {
        caf::PdmUiPushButtonEditorAttribute* attrib = dynamic_cast<caf::PdmUiPushButtonEditorAttribute*>( attribute );
        if ( attrib )
        {
            attrib->m_buttonText = "Apply global min/max values";
        }
    }
    else if ( &applyFullRangeMinMax == field )
    {
        caf::PdmUiPushButtonEditorAttribute* attrib = dynamic_cast<caf::PdmUiPushButtonEditorAttribute*>( attribute );
        if ( attrib )
        {
            attrib->m_buttonText = "Apply full range";
        }
    }
}

//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
void RimTernaryLegendConfig::ternaryRanges( double& soilLower,
                                            double& soilUpper,
                                            double& sgasLower,
                                            double& sgasUpper,
                                            double& swatLower,
                                            double& swatUpper ) const
{
    if ( rangeMode() == RangeModeType::AUTOMATIC_CURRENT_TIMESTEP )
    {
        soilLower = m_localAutoMin[TERNARY_SOIL_IDX];
        soilUpper = m_localAutoMax[TERNARY_SOIL_IDX];
        sgasLower = m_localAutoMin[TERNARY_SGAS_IDX];
        sgasUpper = m_localAutoMax[TERNARY_SGAS_IDX];
        swatLower = m_localAutoMin[TERNARY_SWAT_IDX];
        swatUpper = m_localAutoMax[TERNARY_SWAT_IDX];
    }
    else if ( rangeMode() == RangeModeType::AUTOMATIC_ALLTIMESTEPS )
    {
        soilLower = m_globalAutoMin[TERNARY_SOIL_IDX];
        soilUpper = m_globalAutoMax[TERNARY_SOIL_IDX];
        sgasLower = m_globalAutoMin[TERNARY_SGAS_IDX];
        sgasUpper = m_globalAutoMax[TERNARY_SGAS_IDX];
        swatLower = m_globalAutoMin[TERNARY_SWAT_IDX];
        swatUpper = m_globalAutoMax[TERNARY_SWAT_IDX];
    }
    else
    {
        soilLower = userDefinedMinValueSoil;
        soilUpper = userDefinedMaxValueSoil;
        sgasLower = userDefinedMinValueSgas;
        sgasUpper = userDefinedMaxValueSgas;
        swatLower = userDefinedMinValueSwat;
        swatUpper = userDefinedMaxValueSwat;
    }
}

//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
void RimTernaryLegendConfig::updateLabelText()
{
    {
        userDefinedMinValueSoil.uiCapability()->setUiName( "Min" );
        userDefinedMaxValueSoil.uiCapability()->setUiName( "Max" );

        if ( m_globalAutoMin[TERNARY_SOIL_IDX] != cvf::UNDEFINED_DOUBLE )
        {
            userDefinedMinValueSoil.uiCapability()->setUiName( QString( "Min " ) + "(" +
                                                               QString::number( m_globalAutoMin[TERNARY_SOIL_IDX], 'g', precision ) + ")" );
        }

        if ( m_globalAutoMax[TERNARY_SOIL_IDX] != cvf::UNDEFINED_DOUBLE )
        {
            userDefinedMaxValueSoil.uiCapability()->setUiName( QString( "Max " ) + "(" +
                                                               QString::number( m_globalAutoMax[TERNARY_SOIL_IDX], 'g', precision ) + ")" );
        }
    }

    {
        userDefinedMinValueSgas.uiCapability()->setUiName( "Min" );
        userDefinedMaxValueSgas.uiCapability()->setUiName( "Max" );

        if ( m_globalAutoMin[TERNARY_SGAS_IDX] != cvf::UNDEFINED_DOUBLE )
        {
            userDefinedMinValueSgas.uiCapability()->setUiName( QString( "Min " ) + "(" +
                                                               QString::number( m_globalAutoMin[TERNARY_SGAS_IDX], 'g', precision ) + ")" );
        }

        if ( m_globalAutoMax[TERNARY_SGAS_IDX] != cvf::UNDEFINED_DOUBLE )
        {
            userDefinedMaxValueSgas.uiCapability()->setUiName( QString( "Max " ) + "(" +
                                                               QString::number( m_globalAutoMax[TERNARY_SGAS_IDX], 'g', precision ) + ")" );
        }
    }

    {
        userDefinedMinValueSwat.uiCapability()->setUiName( "Min" );
        userDefinedMaxValueSwat.uiCapability()->setUiName( "Max" );

        if ( m_globalAutoMin[TERNARY_SWAT_IDX] != cvf::UNDEFINED_DOUBLE )
        {
            userDefinedMinValueSwat.uiCapability()->setUiName( QString( "Min " ) + "(" +
                                                               QString::number( m_globalAutoMin[TERNARY_SWAT_IDX], 'g', precision ) + ")" );
        }

        if ( m_globalAutoMax[TERNARY_SWAT_IDX] != cvf::UNDEFINED_DOUBLE )
        {
            userDefinedMaxValueSwat.uiCapability()->setUiName( QString( "Max " ) + "(" +
                                                               QString::number( m_globalAutoMax[TERNARY_SWAT_IDX], 'g', precision ) + ")" );
        }
    }

    if ( rangeMode == RangeModeType::AUTOMATIC_ALLTIMESTEPS )
    {
        QString tmpString;
        tmpString = QString( "SOIL : " ) + QString::number( m_globalAutoMin[TERNARY_SOIL_IDX], 'g', precision ) + " - " +
                    QString::number( m_globalAutoMax[TERNARY_SOIL_IDX], 'g', precision ) + "\n";
        tmpString += QString( "SGAS : " ) + QString::number( m_globalAutoMin[TERNARY_SGAS_IDX], 'g', precision ) + " - " +
                     QString::number( m_globalAutoMax[TERNARY_SGAS_IDX], 'g', precision ) + "\n";
        tmpString += QString( "SWAT : " ) + QString::number( m_globalAutoMin[TERNARY_SWAT_IDX], 'g', precision ) + " - " +
                     QString::number( m_globalAutoMax[TERNARY_SWAT_IDX], 'g', precision ) + "\n";

        ternaryRangeSummary = tmpString;
    }
    else if ( rangeMode == RangeModeType::AUTOMATIC_CURRENT_TIMESTEP )
    {
        QString tmpString;
        tmpString = QString( "SOIL : " ) + QString::number( m_localAutoMin[TERNARY_SOIL_IDX], 'g', precision ) + " - " +
                    QString::number( m_localAutoMax[TERNARY_SOIL_IDX], 'g', precision ) + "\n";
        tmpString += QString( "SGAS : " ) + QString::number( m_localAutoMin[TERNARY_SGAS_IDX], 'g', precision ) + " - " +
                     QString::number( m_localAutoMax[TERNARY_SGAS_IDX], 'g', precision ) + "\n";
        tmpString += QString( "SWAT : " ) + QString::number( m_localAutoMin[TERNARY_SWAT_IDX], 'g', precision ) + " - " +
                     QString::number( m_localAutoMax[TERNARY_SWAT_IDX], 'g', precision ) + "\n";

        ternaryRangeSummary = tmpString;
    }
}

//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
const RivTernaryScalarMapper* RimTernaryLegendConfig::scalarMapper() const
{
    return m_scalarMapper.p();
}

//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
const caf::TitledOverlayFrame* RimTernaryLegendConfig::titledOverlayFrame() const
{
    return m_legend.p();
}

//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
caf::TitledOverlayFrame* RimTernaryLegendConfig::titledOverlayFrame()
{
    return m_legend.p();
}

//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
void RimTernaryLegendConfig::updateFonts()
{
    recreateLegend();
}

//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
caf::PdmFieldHandle* RimTernaryLegendConfig::objectToggleField()
{
    return &m_showLegend;
}
