/***************************************************************************
    copyright            : (C) 2006 by Carsten Niehaus
    email                : cniehaus@kde.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 "gradientwidget_impl.h"

#include <QTimer>

#include <kdebug.h>
#include <klocale.h>

#include <element.h>
#include <math.h>

#include "kalziumdataobject.h"
#include "prefs.h"

// used to convert the double variables to int's. (slider <-> spinbox)
#define MULTIPLIKATOR 1000

GradientWidgetImpl::GradientWidgetImpl( QWidget *parent )
        : QWidget( parent ), m_play(false)
{
    setupUi( this );
    
    scheme_combo->addItems( KalziumElementProperty::instance()->schemeList() );
    gradient_combo->addItems( KalziumElementProperty::instance()->gradientList() );

    connect( gradient_spinbox, SIGNAL(valueChanged(double)), this, SLOT(doubleToSlider(double)));
    connect( gradient_slider, SIGNAL(valueChanged(int)), this, SLOT(intToSpinbox(int)));

    m_timer = new QTimer( this );
    connect( Play, SIGNAL (clicked()), this, SLOT(play()));
    connect( m_timer, SIGNAL(timeout()), this, SLOT(tick()) );

    Play->setIcon( KIcon( "media-playback-start" ) );
}

GradientWidgetImpl::~GradientWidgetImpl()
{
    delete m_timer;
}

void GradientWidgetImpl::slotGradientChanged()
{
    if ( !gradient_slider->isEnabled() ) {
        gradient_spinbox->setEnabled(true);
        gradient_slider->setEnabled(true);
        Play->setEnabled(true);
    }

    KalziumElementProperty *elementProperty = KalziumElementProperty::instance();
    double dblMax = elementProperty->gradient()->maxValue();
    double dblMin = elementProperty->gradient()->minValue();

    // saving the decimals in the int
    const int intMax = dblMax * MULTIPLIKATOR;
    const int intMin = dblMin * MULTIPLIKATOR;

    // now we have the slider numbers, so put the speed to a adequate value.
    Speed->setMaximum( intMax / 100 );
    Speed->setValue( ( intMax / 100 ) / 2 );

    gradient_slider->setMaximum(intMax);
    gradient_slider->setMinimum(intMin);

    lblUnit->setText( elementProperty->gradient()->unit() );

    gradient_spinbox->setMaximum(dblMax);
    gradient_spinbox->setMinimum(dblMin);
    gradient_spinbox->setDecimals(elementProperty->gradient()->decimals());

    switch ( elementProperty->gradientId() ) {
    case KalziumElementProperty::DISCOVERYDATE:
        gradient_spinbox->setValue(dblMax);
        break;

    case KalziumElementProperty::SOMGradientType:
        gradient_spinbox->setValue(dblMin + 293);
        break;

    default:
        gradient_spinbox->setValue(dblMin);
        break;
    }

    // Disable Gradient widgets if no gradient is selected.
    if ( gradient_combo->currentIndex() == KalziumElementProperty::NOGRADIENT) {
        gradient_spinbox->setEnabled(false);
        gradient_slider->setEnabled(false);
        Play->setEnabled(false);
        text->clear();
    }
}


void GradientWidgetImpl::doubleToSlider(double doubleVar)
{
    //the signals need to be blocked as both will return to this slot. But no
    //matter which UI elements (slider oder spinbox) was changed, the other
    //has to be set to the same value

    gradient_slider->blockSignals( true );

    // setting the decimals in int
    int intvar = doubleVar * MULTIPLIKATOR;

    gradient_slider->setValue(intvar);

    gradient_slider->blockSignals( false );

    emit gradientValueChanged( doubleVar );

    setNewValue( doubleVar );
}

void GradientWidgetImpl::intToSpinbox(int var)
{
    gradient_spinbox->blockSignals( true );

    // put int back to double with decimals
    double doublevar = var;
    doublevar = doublevar / MULTIPLIKATOR;

    gradient_spinbox->setValue(doublevar);

    gradient_spinbox->blockSignals( false );

    emit gradientValueChanged( doublevar );

    setNewValue( doublevar );
}


void GradientWidgetImpl::setNewValue( double newValue )
{
    // Info text currently only for State of mater typ available.
    if (gradient_combo->currentIndex() != KalziumElementProperty::SOMGradientType) {
        text->clear();
        return;
    }

    static const int threshold = 25;

    const QString unitSymbol = lblUnit->text();

    QStringList listMeltingPoint;
    QStringList listBoilingPoint;
    QStringList listBoilingPointValue;
    QStringList listMeltingPointValue;

    foreach (Element * element, KalziumDataObject::instance()->ElementList) {
        double melting = element->dataAsVariant( ChemicalDataObject::meltingpoint, Prefs::temperatureUnit() ).toDouble();
        if ( ( melting > 0.0 ) && fabs( melting - newValue ) <= threshold ) {
            listMeltingPoint << element->dataAsString( ChemicalDataObject::name );
            listMeltingPointValue << QString::number( melting );
        }

        double boiling = element->dataAsVariant( ChemicalDataObject::boilingpoint, Prefs::temperatureUnit() ).toDouble();
        if ( ( boiling > 0.0 ) && fabs( boiling - newValue ) <= threshold ) {
            listBoilingPoint << element->dataAsString( ChemicalDataObject::name );
            listBoilingPointValue << QString::number( boiling );
        }
    }
    QString htmlcode;
    if ( listMeltingPoint.count() > 0 ) {
        htmlcode += i18n( "Elements with melting point around this temperature:" ) + '\n';
        for ( int i = 0; i < listMeltingPoint.count(); i++ ) {
            htmlcode += " - " + i18nc( "For example: Carbon (300K)", "%1 (%2%3)",
                                       listMeltingPoint.at( i ), listMeltingPointValue.at( i ), unitSymbol ) + '\n';
        }
        htmlcode += '\n';
    } else {
        htmlcode += i18n( "No elements with a melting point around this temperature" );
        htmlcode += "\n\n";
    }
    if ( listBoilingPoint.count() > 0 ) {
        htmlcode += i18n( "Elements with boiling point around this temperature:" ) + '\n';
        for ( int i = 0; i < listBoilingPoint.count(); i++ ) {
            htmlcode += " - " + i18nc( "For example: Carbon (300K)", "%1 (%2%3)",
                                       listBoilingPoint.at( i ), listBoilingPointValue.at( i ), unitSymbol )  + '\n';
        }
        htmlcode += '\n';
    } else {
        htmlcode += i18n( "No elements with a boiling point around this temperature" );
        htmlcode += '\n';
    }

    text->setText( /*m_htmlBegin +*/ htmlcode /*+ m_htmlEnd*/ );
}

void GradientWidgetImpl::play(void)
{
    if ( m_play ) {   //Currently playing
        //The Mode is 'Play' so stop
        stop();
        return;
    }

    //The mode is not 'play'
    //If the slider is at the maximum position bring it to the minimum
    if ((gradient_slider)->value() >= gradient_slider->maximum()) {
        gradient_slider->setValue ( gradient_slider->minimum () );
    }
    //start the timer at 200 milisecond time interval with single shot disabled
    m_timer->start( 200 );

    m_play = true;          //start playing
    Play->setIcon( KIcon( "media-playback-pause" ) );
}

void GradientWidgetImpl::stop(void)
{
    //Currently playing, stop the timer.
    m_timer -> stop();
    Play->setIcon( KIcon( "media-playback-start" ) );
    m_play = false;         //Stop
}

void GradientWidgetImpl::tick(void)
{
    int increment = Speed->value();
    int temp = gradient_slider->value();
    int max = gradient_slider->maximum();
    if (temp + increment > max)
        stop();
    gradient_slider -> setValue ( temp + increment );
}
// #include "gradientwidget_impl.moc"
