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

#include "RiuGuiTheme.h"

#include "RiaApplication.h"
#include "RiaGuiApplication.h"
#include "RiuThemesDirectory.h"

#include "cafAppEnum.h"
#include "cafPdmUiTreeViewEditor.h"

#include <QAbstractItemModel>
#include <QColor>
#include <QCompleter>
#include <QFile>
#include <QFileInfo>
#include <QIODevice>
#include <QRegExp>
#include <QRegularExpression>
#include <QString>
#include <QStringListModel>
#include <QStyle>
#include <QWidget>

#include "qwt_legend_label.h"
#include "qwt_picker.h"
#include "qwt_plot.h"
#include "qwt_plot_curve.h"
#include "qwt_plot_grid.h"
#include "qwt_plot_marker.h"
#include "qwt_symbol.h"

RiaDefines::ThemeEnum RiuGuiTheme::s_currentTheme = RiaDefines::ThemeEnum::DEFAULT;

QMap<RiaDefines::ThemeEnum, QMap<QString, QString>>                 RiuGuiTheme::s_variableValueMap         = {};
QMap<RiaDefines::ThemeEnum, QMap<QString, QString>>                 RiuGuiTheme::s_variableGuiTextMap       = {};
QMap<QString, QMap<QString, QMap<QString, QMap<QString, QString>>>> RiuGuiTheme::s_qwtPlotItemPropertiesMap = {};
QMap<QString, CustomStyleSheetApplicator>                           RiuGuiTheme::s_customStyleSheetApplicators =
    { { QString( "QwtPlot\\[\"(?<plotName>[a-zA-Z0-9-_\\*]+)\"\\]::curve\\[\"(?<itemName>[a-zA-Z0-9-_\\*]+)\"\\]\\s*\\{("
                 "?<properties>([\\n\\r]*\\s*((line-color|symbol-color):"
                 "\\s*([a-zA-Z0-9#]+)\\s*;))*)[\\n\\r]*\\s*\\}" ),
        []( QRegularExpressionMatch& match )
        {
            QRegExp plotNameRegExp( match.captured( "plotName" ) );
            QRegExp itemNameRegExp( match.captured( "itemName" ) );

            QRegularExpression lineColorRegExp( "line-color:\\s*([#0-9a-zA-Z]+)" );
            QString            lineColor = lineColorRegExp.match( match.captured( "properties" ) ).captured( 1 );
            QRegularExpression symbolColorRegExp( "symbol-color:\\s*([#0-9a-zA-Z]+)" );
            QString            symbolColor = symbolColorRegExp.match( match.captured( "properties" ) ).captured( 1 );
            if ( !lineColor.isEmpty() )
            {
                storeQwtStyleSheetProperty( match.captured( "plotName" ), QString( "curve" ), match.captured( "itemName" ), "line-color", lineColor );
            }
            if ( !symbolColor.isEmpty() )
            {
                // Symbols get the same color assigned as curves.
                storeQwtStyleSheetProperty( match.captured( "plotName" ),
                                            QString( "curve" ),
                                            match.captured( "itemName" ),
                                            "symbol-color",
                                            symbolColor );
            }
            if ( lineColor.isEmpty() && symbolColor.isEmpty() ) return;

            const QWidgetList topLevelWidgets = RiaGuiApplication::instance()->topLevelWidgets();
            for ( QWidget* widget : topLevelWidgets )
            {
                for ( QwtPlot* plotWidget : widget->findChildren<QwtPlot*>() )
                {
                    if ( plotNameRegExp.exactMatch( plotWidget->property( "qss-class" ).toString() ) )
                    {
                        for ( QwtPlotItem* item : plotWidget->itemList() )
                        {
                            if ( QwtPlotCurve* curve = dynamic_cast<QwtPlotCurve*>( item ) )
                            {
                                if ( itemNameRegExp.exactMatch( item->title().text() ) || match.captured( "itemName" ) == "*" )
                                {
                                    QPen pen = curve->pen();
                                    pen.setColor( QColor( lineColor ) );
                                    curve->setPen( pen );

                                    if ( curve->symbol() && curve->symbol()->style() != QwtSymbol::NoSymbol )
                                    {
                                        QPen pen = curve->symbol()->pen();
                                        pen.setColor( QColor( symbolColor ) );
                                        QwtSymbol* symbol = cloneCurveSymbol( curve );
                                        symbol->setPen( pen );
                                        curve->setSymbol( symbol );
                                    }
                                }
                            }
                        }
                    }
                    plotWidget->replot();
                }
            }
        } },
      { QString( "QwtPlot\\[\"(?<plotName>[a-zA-Z0-9-_\\*]+)\"\\]::grid\\[\"(?<itemName>[a-zA-Z0-9-_\\*]+)\"\\]\\s*\\{("
                 "?<properties>([\\n\\r]*\\s*((color):"
                 "\\s*([a-zA-Z0-9#]+)\\s*;))*)[\\n\\r]*\\s*\\}" ),
        []( QRegularExpressionMatch& match )
        {
            QRegExp            plotNameRegExp( match.captured( "plotName" ) );
            QRegExp            itemNameRegExp( match.captured( "itemName" ) );
            QRegularExpression colorRegExp( "color:\\s*([#0-9a-zA-Z]+)" );
            QString            color           = colorRegExp.match( match.captured( "properties" ) ).captured( 1 );
            const QWidgetList  topLevelWidgets = RiaGuiApplication::instance()->topLevelWidgets();

            if ( !color.isEmpty() )
            {
                storeQwtStyleSheetProperty( match.captured( "plotName" ), QString( "grid" ), match.captured( "itemName" ), "color", color );
            }
            for ( QWidget* widget : topLevelWidgets )
            {
                for ( QwtPlot* plotWidget : widget->findChildren<QwtPlot*>() )
                {
                    if ( plotNameRegExp.exactMatch( plotWidget->property( "qss-class" ).toString() ) || match.captured( "plotName" ) == "*" )
                    {
                        for ( QwtPlotItem* item : plotWidget->itemList() )
                        {
                            if ( QwtPlotGrid* grid = dynamic_cast<QwtPlotGrid*>( item ) )
                            {
                                if ( itemNameRegExp.exactMatch( item->title().text() ) || match.captured( "itemName" ) == "*" )
                                {
                                    QPen pen = grid->majorPen();
                                    pen.setColor( QColor( color ) );
                                    grid->setPen( pen );
                                }
                            }
                        }
                    }
                    plotWidget->replot();
                }
            }
        } },
      { QString( "QwtPlot\\[\"(?<plotName>[a-zA-Z0-9-_\\*]+)\"\\]::legend\\s*\\{("
                 "?<properties>([\\n\\r]*\\s*((text-color):"
                 "\\s*([a-zA-Z0-9#]+)\\s*;))*)[\\n\\r]*\\s*\\}" ),
        []( QRegularExpressionMatch& match )
        {
            QRegExp            plotNameRegExp( match.captured( "plotName" ) );
            QRegExp            itemNameRegExp( match.captured( "itemName" ) );
            QRegularExpression colorRegExp( "text-color:\\s*([#0-9a-zA-Z]+)" );
            QString            color           = colorRegExp.match( match.captured( "properties" ) ).captured( 1 );
            const QWidgetList  topLevelWidgets = RiaGuiApplication::instance()->topLevelWidgets();

            if ( !color.isEmpty() )
            {
                storeQwtStyleSheetProperty( match.captured( "plotName" ), QString( "legend" ), match.captured( "itemName" ), "text-color", color );
            }
            for ( QWidget* widget : topLevelWidgets )
            {
                for ( QwtPlot* plotWidget : widget->findChildren<QwtPlot*>() )
                {
                    if ( plotNameRegExp.exactMatch( plotWidget->property( "qss-class" ).toString() ) || match.captured( "plotName" ) == "*" )
                    {
                        for ( QwtLegendLabel* label : plotWidget->findChildren<QwtLegendLabel*>() )
                        {
                            QwtText text = label->text();
                            text.setColor( QColor( color ) );
                            label->setText( text );
                            label->repaint();
                        }
                    }
                    plotWidget->replot();
                }
            }
        } },
      { QString( "QwtPlot\\[\"(?<plotName>[a-zA-Z0-9-_\\*]+)\"\\]::lineMarker\\[\"(?<itemName>[a-zA-Z0-9-_\\*]+)\"\\]"
                 "\\s*\\{("
                 "?<properties>([\\n\\r]*\\s*((color|text-color):"
                 "\\s*([a-zA-Z0-9#]+)\\s*;))*)[\\n\\r]*\\s*\\}" ),
        []( QRegularExpressionMatch& match )
        {
            QRegExp            plotNameRegExp( match.captured( "plotName" ) );
            QRegExp            itemNameRegExp( match.captured( "itemName" ) );
            QRegularExpression colorRegExp( "color:\\s*([#0-9a-zA-Z]+)" );
            QString            color = colorRegExp.match( match.captured( "properties" ) ).captured( 1 );
            QRegularExpression textColorRegExp( "text-color:\\s*([#0-9a-zA-Z]+)" );
            QString            textColor = textColorRegExp.match( match.captured( "properties" ) ).captured( 1 );

            const QWidgetList topLevelWidgets = RiaGuiApplication::instance()->topLevelWidgets();

            if ( !color.isEmpty() )
            {
                storeQwtStyleSheetProperty( match.captured( "plotName" ), QString( "lineMarker" ), match.captured( "itemName" ), "color", color );
            }
            if ( !textColor.isEmpty() )
            {
                storeQwtStyleSheetProperty( match.captured( "plotName" ),
                                            QString( "lineMarker" ),
                                            match.captured( "itemName" ),
                                            "text-color",
                                            textColor );
            }
            for ( QWidget* widget : topLevelWidgets )
            {
                for ( QwtPlot* plotWidget : widget->findChildren<QwtPlot*>() )
                {
                    if ( plotNameRegExp.exactMatch( plotWidget->property( "qss-class" ).toString() ) || match.captured( "plotName" ) == "*" )
                    {
                        for ( QwtPlotItem* item : plotWidget->itemList() )
                        {
                            if ( QwtPlotMarker* marker = dynamic_cast<QwtPlotMarker*>( item ) )
                            {
                                if ( marker->symbol() == nullptr || marker->symbol()->style() == QwtSymbol::NoSymbol )
                                {
                                    if ( itemNameRegExp.exactMatch( item->title().text() ) || match.captured( "itemName" ) == "*" )
                                    {
                                        QPen pen = marker->linePen();
                                        pen.setColor( QColor( color ) );
                                        marker->setLinePen( pen );
                                        marker->label().setColor( QColor( textColor ) );
                                    }
                                }
                            }
                        }
                    }
                    plotWidget->replot();
                }
            }
        } },
      { QString( "QwtPlot\\[\"(?<plotName>[a-zA-Z0-9-_\\*]+)\"\\]::pointMarker\\[\"(?<itemName>[a-zA-Z0-9-_\\*]+)\"\\]"
                 "\\s*\\{("
                 "?<properties>([\\n\\r]*\\s*((color|text-color):"
                 "\\s*([a-zA-Z0-9#]+)\\s*;))*)[\\n\\r]*\\s*\\}" ),
        []( QRegularExpressionMatch& match )
        {
            QRegExp            plotNameRegExp( match.captured( "plotName" ) );
            QRegExp            itemNameRegExp( match.captured( "itemName" ) );
            QRegularExpression colorRegExp( "color:\\s*([#0-9a-zA-Z]+)" );
            QString            color = colorRegExp.match( match.captured( "properties" ) ).captured( 1 );
            QRegularExpression textColorRegExp( "text-color:\\s*([#0-9a-zA-Z]+)" );
            QString            textColor = textColorRegExp.match( match.captured( "properties" ) ).captured( 1 );

            const QWidgetList topLevelWidgets = RiaGuiApplication::instance()->topLevelWidgets();

            if ( !color.isEmpty() )
            {
                storeQwtStyleSheetProperty( match.captured( "plotName" ), QString( "pointMarker" ), match.captured( "itemName" ), "color", color );
            }
            if ( !textColor.isEmpty() )
            {
                storeQwtStyleSheetProperty( match.captured( "plotName" ),
                                            QString( "pointMarker" ),
                                            match.captured( "itemName" ),
                                            "text-color",
                                            textColor );
            }
            for ( QWidget* widget : topLevelWidgets )
            {
                for ( QwtPlot* plotWidget : widget->findChildren<QwtPlot*>() )
                {
                    if ( plotNameRegExp.exactMatch( plotWidget->property( "qss-class" ).toString() ) || match.captured( "plotName" ) == "*" )
                    {
                        for ( QwtPlotItem* item : plotWidget->itemList() )
                        {
                            if ( QwtPlotMarker* marker = dynamic_cast<QwtPlotMarker*>( item ) )
                            {
                                if ( marker->symbol() && marker->symbol()->style() != QwtSymbol::NoSymbol )
                                {
                                    if ( itemNameRegExp.exactMatch( item->title().text() ) || match.captured( "itemName" ) == "*" )
                                    {
                                        QPen pen = marker->symbol()->pen();
                                        pen.setColor( QColor( color ) );
                                        QwtSymbol* symbol = cloneMarkerSymbol( marker );
                                        symbol->setPen( pen );
                                        marker->setSymbol( symbol );
                                        marker->label().setColor( QColor( textColor ) );
                                    }
                                }
                            }
                        }
                    }
                    plotWidget->replot();
                }
            }
        } },
      { QString( "QwtPlot\\[\"(?<plotName>[a-zA-Z0-9-_\\*]+)\"\\]::picker"
                 "\\s*\\{("
                 "?<properties>([\\n\\r]*\\s*((text-color):"
                 "\\s*([a-zA-Z0-9#]+)\\s*;))*)[\\n\\r]*\\s*\\}" ),
        []( QRegularExpressionMatch& match )
        {
            QRegExp            plotNameRegExp( match.captured( "plotName" ) );
            QRegExp            itemNameRegExp( match.captured( "itemName" ) );
            QRegularExpression textColorRegExp( "text-color:\\s*([#a-zA-Z0-9]+)" );
            QString            textColor = textColorRegExp.match( match.captured( "properties" ) ).captured( 1 );

            const QWidgetList topLevelWidgets = RiaGuiApplication::instance()->topLevelWidgets();

            if ( !textColor.isEmpty() )
            {
                storeQwtStyleSheetProperty( match.captured( "plotName" ), QString( "picker" ), "*", "text-color", textColor );
            }
            for ( QWidget* widget : topLevelWidgets )
            {
                for ( QwtPlot* plotWidget : widget->findChildren<QwtPlot*>() )
                {
                    if ( plotNameRegExp.exactMatch( plotWidget->property( "qss-class" ).toString() ) || match.captured( "plotName" ) == "*" )
                    {
                        QWidget* canvas = plotWidget->canvas();
                        if ( canvas )
                        {
                            for ( QwtPicker* picker : canvas->findChildren<QwtPicker*>() )
                            {
                                QPen pen = picker->trackerPen();
                                pen.setColor( QColor( textColor ) );
                                picker->setTrackerPen( pen );
                            }
                        }
                    }
                    plotWidget->replot();
                }
            }
        } } };

//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
RiaDefines::ThemeEnum RiuGuiTheme::currentGuiTheme()
{
    return s_currentTheme;
}

//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
void RiuGuiTheme::updateGuiTheme( RiaDefines::ThemeEnum theme )
{
    s_currentTheme = theme;
    s_qwtPlotItemPropertiesMap.clear();

    applyStyleSheet( theme );
    const QWidgetList allWidgets = RiaGuiApplication::instance()->allWidgets();
    for ( QWidget* widget : allWidgets )
    {
        widget->style()->unpolish( widget );
        widget->style()->polish( widget );
        if ( caf::PdmUiTreeViewWidget* treeViewWidget = dynamic_cast<caf::PdmUiTreeViewWidget*>( widget ) )
        {
            treeViewWidget->setStyle( new caf::PdmUiTreeViewStyle );
        }
    }
}

//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
bool RiuGuiTheme::applyStyleSheet( RiaDefines::ThemeEnum theme )
{
    QString styleSheetPath = getStyleSheetPath( theme );
    QFile   styleSheetFile( styleSheetPath );
    if ( styleSheetFile.exists() )
    {
        if ( styleSheetFile.open( QIODevice::ReadOnly ) )
        {
            RiaGuiApplication* app        = RiaGuiApplication::instance();
            QString            styleSheet = styleSheetFile.readAll();
            preparseStyleSheet( theme, styleSheet );
            app->setStyleSheet( styleSheet );
        }
        return true;
    }
    return false;
}

//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
void RiuGuiTheme::changeVariableValue( RiaDefines::ThemeEnum theme, const QString& variableName, const QString& newValue )
{
    if ( !s_variableValueMap.keys().contains( theme ) )
    {
        s_variableValueMap.insert( theme, {} );
    }
    if ( !s_variableValueMap[theme].keys().contains( variableName ) )
    {
        s_variableValueMap[theme].insert( variableName, newValue );
    }
    else
    {
        s_variableValueMap[theme][variableName] = newValue;
    }
}

QMap<QString, QString> RiuGuiTheme::getVariableValueMap( RiaDefines::ThemeEnum theme )
{
    if ( !s_variableValueMap.keys().contains( theme ) )
    {
        QFile styleSheetFile( getStyleSheetPath( theme ) );
        if ( styleSheetFile.exists() )
        {
            if ( styleSheetFile.open( QIODevice::ReadOnly ) )
            {
                QString styleSheet = styleSheetFile.readAll();
                preparseStyleSheet( theme, styleSheet );
                return s_variableValueMap[theme];
            }
        }
        return QMap<QString, QString>();
    }
    else
    {
        return s_variableValueMap[theme];
    }
}

//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
QMap<QString, QString> RiuGuiTheme::getVariableGuiTextMap( RiaDefines::ThemeEnum theme )
{
    if ( !s_variableGuiTextMap.keys().contains( theme ) )
    {
        QFile styleSheetFile( getStyleSheetPath( theme ) );
        if ( styleSheetFile.exists() )
        {
            if ( styleSheetFile.open( QIODevice::ReadOnly ) )
            {
                QString styleSheet = styleSheetFile.readAll();
                preparseStyleSheet( theme, styleSheet );
                return s_variableGuiTextMap[theme];
            }
        }
        return QMap<QString, QString>();
    }
    else
    {
        return s_variableGuiTextMap[theme];
    }
}

//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
QString RiuGuiTheme::applyVariableValueMapToStyleSheet( RiaDefines::ThemeEnum theme )
{
    QFileInfo info( getStyleSheetPath( theme ) );
    QString   absoluteStyleSheetPath = QString( "%0/%1" ).arg( GUI_THEMES_DIR ).arg( info.fileName() );
    QFile     styleSheetFile( absoluteStyleSheetPath );
    if ( styleSheetFile.exists() )
    {
        QString styleSheet;
        if ( styleSheetFile.open( QIODevice::ReadOnly | QIODevice::Truncate | QIODevice::Text ) )
        {
            styleSheet = styleSheetFile.readAll();
            QRegularExpression              variableRegExp( "(?<prefix>[ \\t]*(?<name>\\$[a-zA-z0-9_]+)[ \\t]*:[ "
                                                            "\\t]*)(?<value>[a-zA-Z-_0-9#]+)(?<suffix>;[ \\t]*(\\/\\/[ "
                                                            "\\t]*(?<descriptor>(.*)))?)" );
            QRegularExpressionMatchIterator matchIterator = variableRegExp.globalMatch( styleSheet );
            while ( matchIterator.hasNext() )
            {
                QRegularExpressionMatch match = matchIterator.next();
                styleSheet.replace( match.captured( 0 ),
                                    QString( "%0%1%2" )
                                        .arg( match.captured( "prefix" ) )
                                        .arg( s_variableValueMap[theme].value( match.captured( "name" ) ) )
                                        .arg( match.captured( "suffix" ) ) );
            }
        }
        if ( styleSheetFile.open( QIODevice::WriteOnly | QIODevice::Truncate | QIODevice::Text ) )
        {
            styleSheetFile.write( styleSheet.toLatin1() );
            return styleSheet;
        }
    }
    return QString();
}

//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
bool RiuGuiTheme::writeStyleSheetToFile( RiaDefines::ThemeEnum theme, const QString& styleSheet )
{
    QFileInfo info( getStyleSheetPath( theme ) );
    QString   absoluteStyleSheetPath = QString( "%0/%1" ).arg( GUI_THEMES_DIR ).arg( info.fileName() );
    QFile     styleSheetFile( absoluteStyleSheetPath );
    if ( styleSheetFile.exists() )
    {
        if ( styleSheetFile.open( QIODevice::ReadWrite | QIODevice::Truncate | QIODevice::Text ) )
        {
            QString modifiedStyleSheet = styleSheet;
            formatStyleSheetForWriting( modifiedStyleSheet );
            styleSheetFile.write( modifiedStyleSheet.toLatin1() );
            preparseStyleSheet( theme, modifiedStyleSheet );
            RiaGuiApplication* app = RiaGuiApplication::instance();
            app->setStyleSheet( modifiedStyleSheet );
            const QWidgetList topLevelWidgets = RiaGuiApplication::instance()->allWidgets();
            for ( QWidget* widget : topLevelWidgets )
            {
                widget->style()->unpolish( widget );
                widget->style()->polish( widget );
            }
            return true;
        }
    }
    return false;
}

//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
QString RiuGuiTheme::loadStyleSheet( RiaDefines::ThemeEnum theme )
{
    QFile   styleSheetFile( getStyleSheetPath( theme ) );
    QString styleSheet;
    if ( styleSheetFile.exists() )
    {
        if ( styleSheetFile.open( QIODevice::ReadOnly ) )
        {
            styleSheet = styleSheetFile.readAll();
        }
    }
    return styleSheet;
}

//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
QAbstractItemModel* RiuGuiTheme::getQssCompletionModel( QCompleter* completer )
{
    QFile file( ":utility/qss-keywords.txt" );
    if ( !file.open( QFile::ReadOnly ) ) return new QStringListModel( completer );

    QStringList words;

    while ( !file.atEnd() )
    {
        QByteArray line = file.readLine();
        if ( !line.isEmpty() ) words << QString::fromUtf8( line.trimmed() );
    }

    return new QStringListModel( words, completer );
}

//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
QColor RiuGuiTheme::getColorByVariableName( const QString& variable, RiaDefines::ThemeEnum theme /*= UNDEFINED */ )
{
    RiaDefines::ThemeEnum eTheme = s_currentTheme;

    if ( theme != RiaDefines::ThemeEnum::UNDEFINED )
    {
        eTheme = theme;
    }

    if ( s_variableValueMap.keys().contains( eTheme ) && s_variableValueMap[eTheme].keys().contains( "$" + variable ) )
    {
        return QColor( s_variableValueMap[eTheme]["$" + variable] );
    }
    return Qt::black;
}

//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
QString RiuGuiTheme::getQwtStyleSheetProperty( QString plotName, const QString& itemType, QString itemName, const QString& propertyName )
{
    if ( !s_qwtPlotItemPropertiesMap.keys().contains( plotName ) )
    {
        if ( !s_qwtPlotItemPropertiesMap.keys().contains( "*" ) )
        {
            return QString();
        }
        else
        {
            plotName = "*";
        }
    }
    if ( !s_qwtPlotItemPropertiesMap[plotName].keys().contains( itemType ) )
    {
        if ( s_qwtPlotItemPropertiesMap.keys().contains( "*" ) && s_qwtPlotItemPropertiesMap["*"].keys().contains( itemType ) )
        {
            plotName = "*";
        }
        else
        {
            return QString();
        }
    }
    if ( !s_qwtPlotItemPropertiesMap[plotName][itemType].keys().contains( itemName ) )
    {
        if ( !s_qwtPlotItemPropertiesMap[plotName][itemType].keys().contains( "*" ) )
        {
            if ( s_qwtPlotItemPropertiesMap.keys().contains( "*" ) && s_qwtPlotItemPropertiesMap["*"].keys().contains( itemType ) &&
                 s_qwtPlotItemPropertiesMap["*"][itemType].keys().contains( "*" ) )
            {
                plotName = "*";
                itemName = "*";
            }
            else
            {
                return QString();
            }
        }
        else
        {
            itemName = "*";
        }
    }
    return s_qwtPlotItemPropertiesMap[plotName][itemType][itemName].value( propertyName );
}

//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
void RiuGuiTheme::styleQwtItem( QwtPlotItem* item )
{
    QwtPlot* plot = item->plot();
    QString  plotName;
    if ( plot && plot->property( "qss-class" ).isValid() )
    {
        plotName = plot->property( "qss-class" ).toString();
    }
    if ( QwtPlotCurve* curve = dynamic_cast<QwtPlotCurve*>( item ) )
    {
        QPen    pen   = curve->pen();
        QString color = getQwtStyleSheetProperty( plotName, "curve", curve->title().text(), "line-color" );
        if ( !color.isEmpty() ) pen.setColor( QColor( color ) );
        curve->setPen( pen );

        if ( curve->symbol() && curve->symbol()->style() != QwtSymbol::NoSymbol )
        {
            pen                 = curve->symbol()->pen();
            QString symbolColor = getQwtStyleSheetProperty( plotName, "curve", curve->title().text(), "symbol-color" );
            if ( !color.isEmpty() ) pen.setColor( QColor( symbolColor ) );
            QwtSymbol* symbol = cloneCurveSymbol( curve );
            symbol->setPen( pen );
            curve->setSymbol( symbol );
        }
    }
    else if ( QwtPlotGrid* grid = dynamic_cast<QwtPlotGrid*>( item ) )
    {
        QPen    pen   = grid->majorPen();
        QString color = getQwtStyleSheetProperty( plotName, "grid", grid->title().text(), "color" );
        if ( !color.isEmpty() ) pen.setColor( QColor( color ) );
        grid->setPen( pen );
    }
    else if ( QwtPlotMarker* marker = dynamic_cast<QwtPlotMarker*>( item ) )
    {
        if ( marker->symbol() == nullptr || marker->symbol()->style() == QwtSymbol::NoSymbol )
        {
            QPen    pen   = marker->linePen();
            QString color = getQwtStyleSheetProperty( plotName, "lineMarker", marker->title().text(), "color" );
            if ( !color.isEmpty() ) pen.setColor( QColor( color ) );
            marker->setLinePen( pen );
            QString textColor = getQwtStyleSheetProperty( plotName, "lineMarker", marker->title().text(), "text-color" );
            if ( !textColor.isEmpty() ) marker->label().setColor( QColor( textColor ) );
        }
        else
        {
            QwtSymbol* symbol = cloneMarkerSymbol( marker );
            QPen       pen    = symbol->pen();
            QString    color  = getQwtStyleSheetProperty( plotName, "pointMarker", marker->title().text(), "color" );
            if ( !color.isEmpty() ) pen.setColor( QColor( color ) );
            symbol->setPen( pen );
            marker->setSymbol( symbol );
            QString textColor = getQwtStyleSheetProperty( plotName, "pointMarker", marker->title().text(), "text-color" );
            if ( !textColor.isEmpty() ) marker->label().setColor( QColor( textColor ) );
        }
    }
    else if ( QwtPlotMarker* marker = dynamic_cast<QwtPlotMarker*>( item ) )
    {
        if ( marker->symbol() == nullptr || marker->symbol()->style() == QwtSymbol::NoSymbol )
        {
            QPen    pen   = marker->linePen();
            QString color = getQwtStyleSheetProperty( plotName, "lineMarker", marker->title().text(), "color" );
            if ( !color.isEmpty() ) pen.setColor( QColor( color ) );
            marker->setLinePen( pen );
            QString textColor = getQwtStyleSheetProperty( plotName, "lineMarker", marker->title().text(), "text-color" );
            if ( !textColor.isEmpty() ) marker->label().setColor( QColor( textColor ) );
        }
        else
        {
            QwtSymbol* symbol = cloneMarkerSymbol( marker );
            QPen       pen    = symbol->pen();
            QString    color  = getQwtStyleSheetProperty( plotName, "pointMarker", marker->title().text(), "color" );
            if ( !color.isEmpty() ) pen.setColor( QColor( color ) );
            symbol->setPen( pen );
            marker->setSymbol( symbol );
            QString textColor = getQwtStyleSheetProperty( plotName, "pointMarker", marker->title().text(), "text-color" );
            if ( !textColor.isEmpty() ) marker->label().setColor( QColor( textColor ) );
        }
    }
}

//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
void RiuGuiTheme::styleQwtItem( QwtPicker* item )
{
    QWidget* canvas = item->parentWidget();
    if ( canvas )
    {
        QwtPlot* plot = dynamic_cast<QwtPlot*>( canvas->parentWidget() );
        if ( plot )
        {
            QString plotName;
            if ( plot && plot->property( "qss-class" ).isValid() )
            {
                plotName = plot->property( "qss-class" ).toString();
            }

            QPen    pen       = item->trackerPen();
            QString textColor = getQwtStyleSheetProperty( plotName, "picker", "*", "text-color" );
            if ( !textColor.isEmpty() ) pen.setColor( QColor( textColor ) );
            item->setTrackerPen( pen );
        }
    }
}

//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
void RiuGuiTheme::preparseStyleSheet( RiaDefines::ThemeEnum theme, QString& styleSheet )
{
    QMap<QString, QPalette::ColorRole> colorRoleMap;
    colorRoleMap.insert( "AlternateBase", QPalette::ColorRole::AlternateBase );
    colorRoleMap.insert( "Base", QPalette::ColorRole::Base );
    colorRoleMap.insert( "BrightText", QPalette::ColorRole::BrightText );
    colorRoleMap.insert( "Button", QPalette::ColorRole::Button );
    colorRoleMap.insert( "ButtonText", QPalette::ColorRole::ButtonText );
    colorRoleMap.insert( "Dark", QPalette::ColorRole::Dark );
    colorRoleMap.insert( "Highlight", QPalette::ColorRole::Highlight );
    colorRoleMap.insert( "HighlightedText", QPalette::ColorRole::HighlightedText );
    colorRoleMap.insert( "Light", QPalette::ColorRole::Light );
    colorRoleMap.insert( "Link", QPalette::ColorRole::Link );
    colorRoleMap.insert( "LinkVisited", QPalette::ColorRole::LinkVisited );
    colorRoleMap.insert( "Mid", QPalette::ColorRole::Mid );
    colorRoleMap.insert( "Midlight", QPalette::ColorRole::Midlight );
    colorRoleMap.insert( "NoRole", QPalette::ColorRole::NoRole );
    colorRoleMap.insert( "Shadow", QPalette::ColorRole::Shadow );
    colorRoleMap.insert( "Text", QPalette::ColorRole::Text );
    colorRoleMap.insert( "ToolTipBase", QPalette::ColorRole::ToolTipBase );
    colorRoleMap.insert( "ToolTipText", QPalette::ColorRole::ToolTipText );
    colorRoleMap.insert( "Window", QPalette::ColorRole::Window );
    colorRoleMap.insert( "WindowText", QPalette::ColorRole::WindowText );

    QMap<QString, QPalette::ColorGroup> colorGroupMap;
    colorGroupMap.insert( "Active", QPalette::ColorGroup::Active );
    colorGroupMap.insert( "Disabled", QPalette::ColorGroup::Disabled );
    colorGroupMap.insert( "Inactive", QPalette::ColorGroup::Inactive );

    QString regExp = "QPalette::(";
    regExp += colorRoleMap.keys().join( "|" );
    regExp += "){1}(::(";
    regExp += colorGroupMap.keys().join( "|" );
    regExp += "){1})?";

    QRegularExpression              paletteRegExp( regExp );
    QRegularExpressionMatchIterator matchIterator = paletteRegExp.globalMatch( styleSheet );
    QPalette                        palette       = RiaGuiApplication::instance()->palette();

    while ( matchIterator.hasNext() )
    {
        QRegularExpressionMatch match      = matchIterator.next();
        QPalette::ColorGroup    colorGroup = QPalette::ColorGroup::Active;
        if ( match.lastCapturedIndex() == 3 )
        {
            colorGroup = colorGroupMap.value( match.captured( 3 ) );
        }
        QColor color = palette.color( colorGroup, colorRoleMap.value( match.captured( 1 ) ) );
        styleSheet.replace( match.captured( 0 ), color.name() );
    }

    QRegularExpression variableRegExp( "[ \\t]*(?<name>\\$[a-zA-z0-9_]+)[ \\t]*:[ \\t]*(?<value>[a-zA-Z-_0-9#]+);[ \\t]*(\\/\\/[ "
                                       "\\t]*(?<descriptor>(.*)))?[\\n\\r]*" );
    matchIterator = variableRegExp.globalMatch( styleSheet );

    if ( !s_variableValueMap.keys().contains( theme ) )
    {
        s_variableValueMap.insert( theme, {} );
    }
    else
    {
        s_variableValueMap[theme].clear();
    }

    while ( matchIterator.hasNext() )
    {
        QRegularExpressionMatch match = matchIterator.next();
        styleSheet.replace( match.captured( 0 ), "" );

        QRegularExpression              replaceRegExp( "(\\" + match.captured( "name" ) + ")([ ;\\n\\t]+)" );
        QRegularExpressionMatchIterator replaceMatchIterator = replaceRegExp.globalMatch( styleSheet );
        while ( replaceMatchIterator.hasNext() )
        {
            QRegularExpressionMatch replaceMatch = replaceMatchIterator.next();
            if ( s_variableValueMap[theme].keys().contains( match.captured( "name" ) ) )
            {
                styleSheet = styleSheet.left( replaceMatch.capturedStart( 1 ) ) + s_variableValueMap[theme].value( match.captured( "name" ) ) +
                             replaceMatch.captured( 2 ) + styleSheet.right( styleSheet.length() - replaceMatch.capturedEnd( 0 ) );
            }
            else
            {
                styleSheet = styleSheet.left( replaceMatch.capturedStart( 0 ) ) + match.captured( "value" ) + replaceMatch.captured( 2 ) +
                             styleSheet.right( styleSheet.length() - replaceMatch.capturedEnd( 0 ) );
            }
            // Positions got updated, we need to reassign the iterator.
            replaceMatchIterator = replaceRegExp.globalMatch( styleSheet );
        }
        if ( !s_variableValueMap[theme].keys().contains( match.captured( "name" ) ) )
        {
            s_variableValueMap[theme].insert( match.captured( "name" ), match.captured( "value" ) );
            s_variableGuiTextMap[theme].insert( match.captured( "name" ), match.captured( "descriptor" ) );
        }
    }

    QMap<QString, CustomStyleSheetApplicator>::iterator applicatorIterator;
    for ( applicatorIterator = s_customStyleSheetApplicators.begin(); applicatorIterator != s_customStyleSheetApplicators.end();
          applicatorIterator++ )
    {
        matchIterator = QRegularExpression( applicatorIterator.key() ).globalMatch( styleSheet );
        while ( matchIterator.hasNext() )
        {
            QRegularExpressionMatch match = matchIterator.next();
            applicatorIterator.value()( match );
            styleSheet.replace( match.captured( 0 ), "" );
        }
    }

    styleSheet = styleSheet.simplified();
}

//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
QString RiuGuiTheme::getStyleSheetPath( RiaDefines::ThemeEnum theme )
{
    return QString( ":/themes/%0.qss" ).arg( caf::AppEnum<RiaDefines::ThemeEnum>( theme ).text().toLower() );
}

//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
void RiuGuiTheme::storeQwtStyleSheetProperty( const QString& plotName,
                                              const QString& itemType,
                                              const QString& itemName,
                                              const QString& propertyName,
                                              const QString& value )
{
    if ( !s_qwtPlotItemPropertiesMap.keys().contains( plotName ) )
    {
        s_qwtPlotItemPropertiesMap.insert( plotName, QMap<QString, QMap<QString, QMap<QString, QString>>>{} );
    }
    if ( !s_qwtPlotItemPropertiesMap[plotName].keys().contains( itemType ) )
    {
        s_qwtPlotItemPropertiesMap[plotName].insert( itemType, QMap<QString, QMap<QString, QString>>{} );
    }
    if ( !s_qwtPlotItemPropertiesMap[plotName][itemType].keys().contains( itemName ) )
    {
        s_qwtPlotItemPropertiesMap[plotName][itemType].insert( itemName, QMap<QString, QString>{} );
    }
    s_qwtPlotItemPropertiesMap[plotName][itemType][itemName].insert( propertyName, value );
}

//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
QwtSymbol* RiuGuiTheme::cloneMarkerSymbol( QwtPlotMarker* marker )
{
    QwtSymbol* symbol = new QwtSymbol();
    symbol->setBrush( marker->symbol()->brush() );
    symbol->setPen( marker->symbol()->pen() );
    symbol->setStyle( marker->symbol()->style() );
    if ( marker->symbol()->style() == QwtSymbol::Style::Pixmap )
    {
        symbol->setPixmap( marker->symbol()->pixmap() );
    }
    else if ( marker->symbol()->style() == QwtSymbol::Style::Graphic )
    {
        symbol->setGraphic( marker->symbol()->graphic() );
    }
    symbol->setSize( marker->symbol()->size() );
    symbol->setPinPoint( marker->symbol()->pinPoint() );
    return symbol;
}

//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
QwtSymbol* RiuGuiTheme::cloneCurveSymbol( QwtPlotCurve* curve )
{
    QwtSymbol* symbol = new QwtSymbol();
    symbol->setBrush( curve->symbol()->brush() );
    symbol->setPen( curve->symbol()->pen() );
    symbol->setStyle( curve->symbol()->style() );
    if ( curve->symbol()->style() == QwtSymbol::Style::Pixmap )
    {
        symbol->setPixmap( curve->symbol()->pixmap() );
    }
    else if ( curve->symbol()->style() == QwtSymbol::Style::Graphic )
    {
        symbol->setGraphic( curve->symbol()->graphic() );
    }
    symbol->setSize( curve->symbol()->size() );
    symbol->setPinPoint( curve->symbol()->pinPoint() );
    return symbol;
}

//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
void RiuGuiTheme::formatStyleSheetForWriting( QString& styleSheet )
{
    styleSheet.replace( "\\r", "" );
}
