#define ICON_SIZE        QSize(48,48)

#include "playercore.h"

#include <math.h>

#include <phonon/volumeslider.h>
#include <phonon/seekslider.h>

#include <phonon/backendcapabilities.h>
#include <phonon/effectwidget.h>
#include <phonon/objectdescription.h>
#include <phonon/effectparameter.h>
#include <phonon/effect.h>

#include <QLabel>
#include <QList>
#include <QHash>
#include <QPushButton>
#include <QHBoxLayout>
#include <QSlider>
#include <QTimer>
#include <QAction>
#include <QDir>
#include <QMenu>
#include <QDialogButtonBox>
#include <QScrollArea>
#include <QToolBar>

#include <SMasterIcons>
#include <SDataBase>
#include <Silicon>
#include <SPage>
#include <SDialog>
#include <SEffectWidget>

class PlayerCorePrivate
{
public:
    Phonon::SeekSlider   *seek_slider;
    Phonon::VolumeSlider *volume_slider;

    Phonon::MediaObject  *mediaObject;
    Phonon::AudioOutput  *audioOutput;
    Phonon::Path          path;

    QLabel *currentTime;
    QLabel *totalTime;
    QMenu  *effectsMenu;

    QList<Phonon::EffectDescription> effect_list;
    QList<int> inserted_effects;
    QHash<int,Phonon::Effect *> desc_effect;
    QList<QAction *> effects_act_list;
    Phonon::EffectDescription chosenEffect;
    Phonon::Effect *effect;
    QVariantList savedParamValues;

    QAction *play_action;
    QAction *next_action;
    QAction *prev_action;
    QAction *stop_action;

    QToolBar *toolbar;

    SDataBase *database;
    SPage *page;

    QIcon play_icon;
    QIcon pause_icon;
};

PlayerCore::PlayerCore( SPage *page , QWidget *parent) :
    QToolBar(parent)
{
    setToolButtonStyle( Qt::ToolButtonTextBesideIcon );
    setStyleSheet( "QToolBar{ border-style:solid ; margin:0px }" );

    p = new PlayerCorePrivate;
    p->page = page;

    p->play_icon  = SMasterIcons::icon(ICON_SIZE,"media-playback-start.png");
    p->pause_icon = SMasterIcons::icon(ICON_SIZE,"media-playback-pause.png");

    QDir config( Silicon::config() + "/TagargPlayer" );
    if( !config.exists() )
        config.mkpath( config.path() );

    p->database = new SDataBase( config.path() + "/effects" );

    p->audioOutput = new Phonon::AudioOutput( Phonon::MusicCategory, this );
    p->mediaObject = new Phonon::MediaObject( this );
        p->mediaObject->setTickInterval( 100 );

    p->path = Phonon::createPath( p->mediaObject, p->audioOutput );

    p->seek_slider = new Phonon::SeekSlider( p->mediaObject );
    p->volume_slider = new Phonon::VolumeSlider( p->audioOutput );
        p->volume_slider->setFixedWidth( 173 );

    setupActions();
    settingUpEffects();

    connect( p->mediaObject , SIGNAL(finished()) , SIGNAL(finished()) );
}

void PlayerCore::setupActions()
{
    p->effectsMenu = new QMenu( tr( "Effects" ) );

    p->play_action = new QAction( p->play_icon                                             , tr("play") , this );
    p->next_action = new QAction( SMasterIcons::icon(ICON_SIZE,"media-skip-forward.png")   , QString()  , this );
    p->prev_action = new QAction( SMasterIcons::icon(ICON_SIZE,"media-skip-backward.png")  , QString()  , this );
    p->stop_action = new QAction( SMasterIcons::icon(ICON_SIZE,"media-playback-stop.png")  , QString()  , this );

    addAction( p->play_action );
    addAction( p->prev_action );
    addAction( p->next_action );
    addSeparator();
    addAction( p->stop_action );
    addSeparator();
    addWidget( p->seek_slider );
    addWidget( p->volume_slider );

    p->page->menuPanel()->addMenu( p->effectsMenu );

    connect( p->effectsMenu , SIGNAL(triggered( QAction* ) ) , SLOT(effect_triggered( QAction* )) );
    connect( p->play_action , SIGNAL(triggered())            , SLOT(playPause())                  );
    connect( p->next_action , SIGNAL(triggered())            , SLOT(finish())                     );
    connect( p->prev_action , SIGNAL(triggered())            , SIGNAL(prev())                     );
    connect( p->stop_action , SIGNAL(triggered())            , SLOT(stop())                       );
}

void PlayerCore::settingUpEffects()
{
    p->effect_list = Phonon::BackendCapabilities::availableAudioEffects();

    for( int i=0 ; i<p->effect_list.count() ; i++ )
    {
        QAction *act = new QAction( p->effect_list.at( i ).name() , this );
            act->setToolTip( p->effect_list.at( i ).description() );
            act->setData( i );

        if( p->database->checkHeadExist( p->effect_list.at( i ).name()) )
        {
            int effect_index = i;
            Phonon::EffectDescription chosenEffect = p->effect_list.at( i );
            Phonon::Effect *effect = new Phonon::Effect( chosenEffect ) ;

            p->inserted_effects << effect_index;
            p->desc_effect.insert( effect_index , effect );
            p->path.insertEffect( effect );

            QList<Phonon::EffectParameter> parm_list = effect->parameters();

            for( int i=0 ; i<parm_list.count() ; i++ )
            {
                Phonon::EffectParameter parameter = parm_list.at( i );
                if( !p->database->checkChildExist( chosenEffect.name() , parameter.name()) )
                    continue;

                QString value = p->database->read( chosenEffect.name() , parameter.name() );

                QVariant::Type v_type = effect->parameterValue( parameter ).type();

                switch( static_cast<int>( v_type ) )
                {
                    case QVariant::Int :
                        effect->setParameterValue( parameter , value.toInt() );
                        break;

                    case QVariant::Double :
                        effect->setParameterValue( parameter , value.toDouble() );
                        break;

                    case QVariant::String :
                        effect->setParameterValue( parameter , value );
                        break;

                    case QVariant::Bool :
                        effect->setParameterValue( parameter , value == "true" );
                        break;
                }
            }
        }

        p->effects_act_list << act;
        p->effectsMenu->addAction( act );
    }
}

void PlayerCore::effect_triggered( QAction *act )
{
    if( !p->page->dialogs().isEmpty() )
        return;

    int effect_index = act->data().toInt();
    p->chosenEffect = p->effect_list[effect_index];

    if( !p->inserted_effects.contains( effect_index ) )
    {
        p->effect = new Phonon::Effect( p->chosenEffect ) ;

        p->inserted_effects << effect_index;
        p->desc_effect.insert( effect_index , p->effect );
        p->path.insertEffect( p->effect );
    }
    else
    {
        p->effect = p->desc_effect.value( effect_index );
    }

    SDialog *effectDialog = new SDialog( p->page , 473 , 203 );

    QLabel *description = new QLabel( "<b>Description:</b><br>" + p->chosenEffect.description(), effectDialog );
        description->setWordWrap( true );

    p->savedParamValues.clear();
    foreach( Phonon::EffectParameter param, p->effect->parameters() )
    {
        p->savedParamValues << p->effect->parameterValue( param );
    }

    QWidget *scrollWidget;
    if( p->chosenEffect.name().contains( "equalizer",Qt::CaseInsensitive ) )
        scrollWidget = new SEffectWidget( p->effect );
    else
        scrollWidget = new Phonon::EffectWidget( p->effect );

    QDialogButtonBox *bbox = new QDialogButtonBox( QDialogButtonBox::Ok | QDialogButtonBox::Cancel, Qt::Horizontal, effectDialog );
    QScrollArea *scrollArea = new QScrollArea( effectDialog );
        scrollArea->setWidget( scrollWidget );
        scrollArea->setFrameShadow( QFrame::Plain );
        scrollArea->setWidgetResizable( true );
        scrollArea->viewport()->setAutoFillBackground( false );
        scrollArea->viewport()->setStyleSheet( "QWidget:default{background-color : transparent}" );

    QVBoxLayout *topLayout = new QVBoxLayout( effectDialog );
        topLayout->addWidget( description );
        topLayout->addWidget( scrollArea );
        topLayout->addWidget( bbox );

    connect( bbox->button( QDialogButtonBox::Ok )     , SIGNAL(clicked()), effectDialog, SLOT(accept()) );
    connect( bbox->button( QDialogButtonBox::Cancel ) , SIGNAL(clicked()), effectDialog, SLOT(reject()) );

    connect( effectDialog , SIGNAL(accepted()) , SLOT(acceptEffect()) );
    connect( effectDialog , SIGNAL(rejected()) , SLOT(rejectEffect()) );
}

void PlayerCore::rejectEffect()
{
    //we need to restore the paramaters values
    int currentIndex = 0;
    foreach( Phonon::EffectParameter param, p->effect->parameters() )
        p->effect->setParameterValue( param, p->savedParamValues.at( currentIndex++ ) );
}

void PlayerCore::acceptEffect()
{
    p->database->addHead( p->chosenEffect.name() );
    QList<Phonon::EffectParameter> parm_list = p->effect->parameters();

    for( int i=0 ; i<parm_list.count() ; i++ )
    {
        Phonon::EffectParameter parameter = parm_list.at( i );
        QVariant::Type v_type = p->effect->parameterValue( parameter ).type();

        switch( static_cast<int>( v_type ) )
        {
            case QVariant::Int :
                p->database->addChild( p->chosenEffect.name() , parameter.name() );
                p->database->set( p->chosenEffect.name() , parameter.name() , QString::number( p->effect->parameterValue( parameter ).toInt()) );
                break;

            case QVariant::Double :
                p->database->addChild( p->chosenEffect.name() , parameter.name() );
                p->database->set( p->chosenEffect.name() , parameter.name() , QString::number( p->effect->parameterValue( parameter ).toDouble()) );
                break;

            case QVariant::String :
                p->database->addChild( p->chosenEffect.name() , parameter.name() );
                p->database->set( p->chosenEffect.name() , parameter.name() , p->effect->parameterValue( parameter ).toString() );
                break;

            case QVariant::Bool :
                QString res( "false" );
                if( p->effect->parameterValue( parameter ).toBool() )
                    res = "true";

                p->database->addChild( p->chosenEffect.name() , parameter.name() );
                p->database->set( p->chosenEffect.name() , parameter.name() , res );
                break;
        }
    }

    p->database->save();
}

const Phonon::MediaObject *PlayerCore::mediaObject() const
{
    return p->mediaObject;
}

const Phonon::AudioOutput *PlayerCore::audioOutput() const
{
    return p->audioOutput;
}

const Phonon::Path *PlayerCore::path() const
{
    return &p->path;
}

void PlayerCore::setSource( const QString & file )
{
    p->mediaObject->setCurrentSource( file );
}

void PlayerCore::play()
{
    p->mediaObject->play();

    p->play_action->setIcon( p->pause_icon );
    p->play_action->setText( tr("Pause") );
}

void PlayerCore::playPause()
{
    switch( static_cast<int>(p->mediaObject->state()) )
    {
    case Phonon::PlayingState :
        p->mediaObject->pause();
        p->play_action->setIcon( p->play_icon );
        p->play_action->setText( tr("Play") );
        break;

    case Phonon::PausedState  :
    case Phonon::StoppedState :
    default :
        p->mediaObject->play();
        p->play_action->setIcon( p->pause_icon );
        p->play_action->setText( tr("Pause") );
        break;
    }
}

void PlayerCore::next()
{
    p->next_action->trigger();
}

void PlayerCore::previous()
{
    p->prev_action->trigger();
}

void PlayerCore::stop()
{
    p->play_action->setIcon( p->play_icon );
    p->play_action->setText( tr("Play") );
    p->mediaObject->stop();
}

void PlayerCore::finish()
{
    p->mediaObject->stop();
    p->play_action->setIcon( p->play_icon );
    p->play_action->setText( tr("Play") );

    finished_prev();
}

void PlayerCore::finished_prev()
{
    emit finished();
}

PlayerCore::~PlayerCore()
{
    delete p->database;
    delete p;
}
