/***************************************************************************
                          colorscheme.cpp  -  description
                             -------------------
    begin                : Wed May 8 2002
    copyright            : (C) 2002 by Jason Harris
    email                : kstars@30doradus.org
 ***************************************************************************/

/***************************************************************************
 *                                                                         *
 *   This program 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 2 of the License, or     *
 *   (at your option) any later version.                                   *
 *                                                                         *
 ***************************************************************************/

#include "colorscheme.h"

#include <QFile>
#include <QDir>
#include <QTextStream>

#include <kconfig.h>
#include <QDebug>
#include <KLocalizedString>

#ifndef KSTARS_LITE
#include <KMessageBox>
#endif

#include <kconfiggroup.h>
#include <KSharedConfig>
#include <QStandardPaths>

#include "ksutils.h"
#include "Options.h"
#include "skyobjects/starobject.h"
#ifdef KSTARS_LITE
#include "skymaplite.h"
#else
#include "skyqpainter.h"
#endif
#include "kspaths.h"

ColorScheme::ColorScheme() : FileName() {
    //Each color has two names associated with it.  The KeyName is its
    //identification in the QMap, the *.colors file, and the config file.
    //The Name is what appears in the ViewOpsDialog ListBox.
    //In addition, we define default RGB strings for each item.
    //To add another color to the Palette, just add an entry for KeyName,
    //Name and Default here.

    appendItem("SkyColor",         i18n("Sky"),                    "#000000");
    appendItem("MessColor",        i18n("Messier Object"),         "#008f00");
    appendItem("NGCColor",         i18nc("New General Catalog object", "NGC Object"), "#006666");
    appendItem("ICColor",          i18nc("Index Catalog object", "IC Object"), "#382a7d");
    appendItem("HSTColor",
               i18nc("Object with extra attached URLs", "Object w/ Links"), "#930000");
    appendItem("SNameColor",       i18n("Star Name"),              "#577d7d");
    appendItem("DSNameColor",      i18n("Deep Sky Object Name"),   "#75759c");
    appendItem("PNameColor",       i18n("Planet Name"),            "#ac9800");
    appendItem("CNameColor",
               i18nc("Constellation Name", "Constell. Name"),      "#718488");
    appendItem("CLineColor",
               i18nc("Constellation Line", "Constell. Line"),      "#3d3d3d");
    appendItem("CBoundColor",
               i18nc("Constellation Boundary", "Constell. Boundary"), "#222f2f");
    appendItem("CBoundHighColor",
               i18nc("Highlighted Constellation Boundary", "Constell. Boundary Highlight"), "#445555");
    appendItem("MWColor",
               i18nc("refers to the band of stars in the sky due to the Galactic plane", "Milky Way"), "#0d1115");
    appendItem("EqColor",          i18n("Equator"),                "#909090");
    appendItem("EclColor",         i18n("Ecliptic"),               "#613d12");
    appendItem("HorzColor",        i18n("Horizon"),                "#091f14");
    appendItem("CompassColor",     i18n("Compass Labels"),         "#909055");
    appendItem("EquatorialGridColor", i18n("Equatorial Coordinate Grid"), "#445566");
    appendItem("HorizontalGridColor", i18n("Horizontal Coordinate Grid"), "#091f14");
    appendItem("BoxTextColor",     i18n("Info Box Text"),          "#d2dbef");
    appendItem("BoxGrabColor",     i18n("Info Box Selected"),      "#900000");
    appendItem("BoxBGColor",       i18n("Info Box Background"),    "#000000");
    appendItem("TargetColor",      i18n("Target Indicator"),       "#DD0000");
    appendItem("UserLabelColor",   i18n("User Labels"),            "#AAAAAA");
    appendItem("PlanetTrailColor", i18n("Planet Trails"),          "#993311");
    appendItem("AngularRuler",     i18n("Angular Distance Ruler"), "#445566");
    appendItem("ObsListColor",     i18n("Observing List Label"),   "#FF0000");
    appendItem("StarHopRouteColor", i18n("Star-Hop Route"),        "#00FFFF");
    appendItem("VisibleSatColor",  i18n("Visible Satellites"),     "#00FF00");
    appendItem("SatColor",         i18n("Satellites"),             "#FF0000");
    appendItem("SatLabelColor",    i18n("Satellites Labels"),      "#640000");
    appendItem("SupernovaColor",   i18n("Supernovae"),             "#FFA500");
    appendItem("ArtificialHorizonColor",   i18n("Artificial Horizon"),             "#C82828");
    appendItem("RAGuideError",     i18n("RA Guide Error"),         "#00FF00");
    appendItem("DEGuideError",     i18n("DEC Guide Error"),        "#00A5FF");
    appendItem("SolverFOVColor",     i18n("Solver FOV"),        "#FFFFFF");
    appendItem("FITSObjectLabelColor",     i18n("FITS Image Object Label"),        "#00FF00");

    //Load colors from config object
    loadFromConfig();

    //Default values for integer variables:
    StarColorMode = 0;
    StarColorIntensity = 4;
    DarkPalette = 0;
}

void ColorScheme::appendItem(QString key, QString name, QString def) {
    KeyName.append( key );
    Name.append( name );
    Default.append( def );

}

QColor ColorScheme::colorNamed( const QString &name ) const {
    if ( ! hasColorNamed( name ) ) {
        qWarning() << i18n( "No color named \"%1\" found in color scheme.", name ) ;
        // Return white if no color found
        return QColor( Qt::white );
    }
    return QColor( Palette[ name ] );
}

QColor ColorScheme::colorAt( int i ) const {
    return QColor( Palette[ KeyName.at(i) ] );
}

QString ColorScheme::nameAt( int i ) const {
    return Name.at(i);
}

QString ColorScheme::keyAt( int i ) const {
    return KeyName.at(i);
}

QString ColorScheme::nameFromKey( const QString &key ) const {
    return nameAt( KeyName.indexOf( key ) );
}

void ColorScheme::setColor( const QString &key, const QString &color ) {
    //We can blindly insert() the new value; if the key exists, the old value is replaced
    Palette.insert( key, color );

    KConfigGroup cg = KSharedConfig::openConfig()->group( "Colors" );
    cg.writeEntry( key, color );
}

bool ColorScheme::load( const QString &name ) {
    QString filename = name.toLower().trimmed();
    QFile file;
    int inew = 0, iold = 0;
    bool ok = false;

    //Parse default names which don't follow the regular file-naming scheme
    if ( name == i18nc("use default color scheme", "Default Colors") )
        filename = "classic.colors";
    if ( name == i18nc("use 'star chart' color scheme", "Star Chart") )
        filename = "chart.colors";
    if ( name == i18nc("use 'night vision' color scheme", "Night Vision") )
        filename = "night.colors";

    //Try the filename if it ends with ".colors"
    if ( filename.endsWith( QLatin1String( ".colors" ) ) )
        ok = KSUtils::openDataFile( file, filename );

    //If that didn't work, try assuming that 'name' is the color scheme name
    //convert it to a filename exactly as ColorScheme::save() does
    if ( ! ok ) {
        if ( !filename.isEmpty() ) {
            filename.replace( ' ', '-' ).append( ".colors" );
            ok = KSUtils::openDataFile( file, filename );
        }

        if ( ! ok ) {
            qDebug() << QString("Unable to load color scheme named %1. Also tried %2.").arg(name).arg(filename);
            return false;
        }
    }

    //If we reach here, the file should have been successfully opened
    QTextStream stream( &file );

    //first line is the star-color mode and star color intensity and dark palette
    QString line = stream.readLine();
    QStringList modes = line.split(":");

    // Star Color Mode
    if (modes.count() > 0)
    {
        int newmode = modes[0].toInt( &ok );
        if( ok )
            setStarColorMode( newmode );
    }

    // Star Intensity
    if (modes.count() > 1)
    {
        int newintens = modes[1].toInt( &ok );
        if ( ok )
            setStarColorIntensity( newintens );
    }

    // Dark Palette
    if (modes.count() > 2)
    {
        int newintens = modes[2].toInt( &ok );
        if ( ok )
            setDarkPalette(newintens == 1);
    }

    //More flexible method for reading in color values.  Any order is acceptable, and
    //missing entries are ignored.
    while ( !stream.atEnd() ) {
        line = stream.readLine();

        if ( line.count(':')==1 ) { //the new color preset format contains a ":" in each line, followed by the name of the color
            ++inew;
            if ( iold ) return false; //we read at least one line without a colon...file is corrupted.

            //If this line has a valid Key, set the color.
            QString tkey = line.mid( line.indexOf(':')+1 ).trimmed();
            QString tname = line.left( line.indexOf(':')-1 );

            if ( KeyName.contains( tkey ) ) {
                setColor( tkey, tname );
            } else { //attempt to translate from old color ID
                QString k( line.mid( 5 ).trimmed() + "Color" );
                if ( KeyName.contains( k ) ) {
                    setColor( k, tname );
                } else {
                    qWarning() << "Could not use the key \"" << tkey
                               << "\" from the color scheme file \"" << filename
                               << "\".  I also tried \"" << k << "\"." << endl;
                }
            }

        } else { // no ':' seen in the line, so we must assume the old format
            ++iold;
            if ( inew ) return false; //a previous line had a colon, this line doesn't.  File is corrupted.

            //Assuming the old *.colors format.  Loop through the KeyName list,
            //and assign each color.  Note that order matters here, but only here
            //(so if you don't use the old format, then order doesn't ever matter)
            foreach(const QString& key, KeyName)
                setColor( key, line.left( 7 ) );
        }
    }

    FileName = filename;
    return true;
}

bool ColorScheme::save( const QString &name ) {
    QFile file;

    //Construct a file name from the scheme name.  Make lowercase, replace spaces with "-",
    //and append ".colors".
    QString filename = name.toLower().trimmed();
    if ( !filename.isEmpty() ) {
        for( int i=0; i<filename.length(); ++i)
            if ( filename.at(i)==' ' ) filename.replace( i, 1, "-" );

        filename = filename.append( ".colors" );
        file.setFileName( KSPaths::writableLocation(QStandardPaths::GenericDataLocation) + filename ) ; //determine filename in local user KDE directory tree.

        if ( file.exists() || !file.open( QIODevice::ReadWrite | QIODevice::Append ) ) {
            QString message = i18n( "Local color scheme file could not be opened.\nScheme cannot be recorded." );
            #ifndef KSTARS_LITE
                KMessageBox::sorry( 0, message, i18n( "Could Not Open File" ) );
            #else
                qDebug() << message << i18n( "Could Not Open File" );
            #endif
            return false;
        } else {
            QTextStream stream( &file );
            stream << StarColorMode << ":" << StarColorIntensity << ":" << DarkPalette << endl;

            foreach(const QString& key, KeyName )
                stream << Palette[ key ] << " :" << key << endl;
            file.close();
        }

        file.setFileName( KSPaths::writableLocation(QStandardPaths::GenericDataLocation) + "colors.dat" ) ; //determine filename in local user KDE directory tree.

        if ( !file.open( QIODevice::ReadWrite | QIODevice::Append ) ) {
            QString message = i18n( "Local color scheme index file could not be opened.\nScheme cannot be recorded." );
            #ifndef KSTARS_LITE
                KMessageBox::sorry( 0, message, i18n( "Could Not Open File" ) );
            #else
                qDebug() << message << i18n( "Could Not Open File" );
            #endif
            return false;
        } else {
            QTextStream stream( &file );
            stream << name << ":" << filename << endl;
            file.close();
        }
    } else {
        QString message = i18n( "Invalid filename requested.\nScheme cannot be recorded." );
        #ifndef KSTARS_LITE
            KMessageBox::sorry( 0, message, i18n( "Could Not Open File" ) );
        #else
            qDebug() << message << i18n( "Invalid Filename" );
        #endif
        return false;
    }

    FileName = filename;
    saveToConfig();
    return true;
}

void ColorScheme::loadFromConfig() {
    KConfigGroup cg = KSharedConfig::openConfig()->group( "Colors" );

    for ( int i=0; i < KeyName.size(); ++i )
        setColor( KeyName.at(i), cg.readEntry( KeyName.at(i).toUtf8().constData(), Default.at( i ) ) );

    setStarColorModeIntensity( cg.readEntry( "StarColorMode", 0 ), cg.readEntry( "StarColorIntensity", 5 ) );
    setDarkPalette(cg.readEntry("DarkAppColors", false));

    FileName = cg.readEntry( "ColorSchemeFile", "moonless-night.colors" );
}

void ColorScheme::saveToConfig() {
    KConfigGroup cg = KSharedConfig::openConfig()->group( "Colors" );
    for ( int i=0; i < KeyName.size(); ++i ) {
        QString c = colorNamed( KeyName.at(i) ).name();
        cg.writeEntry( KeyName.at(i), c );
    }

    cg.writeEntry( "StarColorMode", starColorMode() );
    cg.writeEntry( "StarColorIntensity", starColorIntensity() );
    cg.writeEntry( "ColorSchemeFile", FileName);
    cg.writeEntry( "DarkAppColors", useDarkPalette());
}

void ColorScheme::setStarColorMode( int mode ) { 
    StarColorMode = mode;
    Options::setStarColorMode( mode );
#ifndef KSTARS_LITE
    SkyQPainter::initStarImages();
#endif
}

void ColorScheme::setDarkPalette( bool enable ) {
    DarkPalette = enable ? 1 : 0 ;
    Options::setDarkAppColors(enable);
#ifndef KSTARS_LITE
    SkyQPainter::initStarImages();
#endif
}

void ColorScheme::setStarColorIntensity( int intens ) { 
    StarColorIntensity = intens;
    Options::setStarColorIntensity( intens );
#ifndef KSTARS_LITE
    SkyQPainter::initStarImages();
#endif
}

void ColorScheme::setStarColorModeIntensity( int mode, int intens) {
    StarColorMode = mode;
    StarColorIntensity = intens;
    Options::setStarColorMode( mode );
    Options::setStarColorIntensity( intens );
#ifndef KSTARS_LITE
    SkyQPainter::initStarImages();
#endif
}
