// Maintainer: Max Howell <max.howell@methylblue.com>, (C) 2004
// Copyright:  See COPYING file that comes with this distribution

#include "config.h"             //HAVE_LIBVISUAL definition

#include "actionclasses.h"
#include "amarok.h"
#include "amarokconfig.h"
#include "app.h"
#include "debug.h"
#include "collectiondb.h"
#include "covermanager.h"
#include "enginecontroller.h"
#include "k3bexporter.h"
#include "mediumpluginmanager.h"
#include "playlistwindow.h"
#include "playlist.h"
#include "socketserver.h"       //Vis::Selector::showInstance()
#include "threadmanager.h"

#include <tqpixmap.h>
#include <tqtooltip.h>

#include <tdeaction.h>
#include <khelpmenu.h>
#include <kiconloader.h>
#include <tdelocale.h>
#include <kstandarddirs.h>
#include <tdetoolbar.h>
#include <tdetoolbarbutton.h>
#include <kurl.h>

extern TDEAboutData aboutData;

namespace Amarok
{
    bool repeatNone() { return AmarokConfig::repeat() == AmarokConfig::EnumRepeat::Off; }
    bool repeatTrack() { return AmarokConfig::repeat() == AmarokConfig::EnumRepeat::Track; }
    bool repeatAlbum() { return AmarokConfig::repeat() == AmarokConfig::EnumRepeat::Album; }
    bool repeatPlaylist() { return AmarokConfig::repeat() == AmarokConfig::EnumRepeat::Playlist; }
    bool randomOff() { return AmarokConfig::randomMode() == AmarokConfig::EnumRandomMode::Off; }
    bool randomTracks() { return AmarokConfig::randomMode() == AmarokConfig::EnumRandomMode::Tracks; }
    bool randomAlbums() { return AmarokConfig::randomMode() == AmarokConfig::EnumRandomMode::Albums; }
    bool favorNone() { return AmarokConfig::favorTracks() == AmarokConfig::EnumFavorTracks::Off; }
    bool favorScores() { return AmarokConfig::favorTracks() == AmarokConfig::EnumFavorTracks::HigherScores; }
    bool favorRatings() { return AmarokConfig::favorTracks() == AmarokConfig::EnumFavorTracks::HigherRatings; }
    bool favorLastPlay() { return AmarokConfig::favorTracks() == AmarokConfig::EnumFavorTracks::LessRecentlyPlayed; }
    bool entireAlbums() { return repeatAlbum() || randomAlbums(); }
}

using namespace Amarok;

KHelpMenu *Menu::s_helpMenu = 0;

static void
safePlug( TDEActionCollection *ac, const char *name, TQWidget *w )
{
    if( ac )
    {
        TDEAction *a = ac->action( name );
        if( a ) a->plug( w );
    }
}


//////////////////////////////////////////////////////////////////////////////////////////
// MenuAction && Menu
// TDEActionMenu doesn't work very well, so we derived our own
//////////////////////////////////////////////////////////////////////////////////////////

MenuAction::MenuAction( TDEActionCollection *ac )
  : TDEAction( i18n( "Amarok Menu" ), 0, ac, "amarok_menu" )
{
    setShortcutConfigurable ( false ); //FIXME disabled as it doesn't work, should use TQCursor::pos()
}

int
MenuAction::plug( TQWidget *w, int index )
{
    TDEToolBar *bar = dynamic_cast<TDEToolBar*>(w);

    if( bar && kapp->authorizeTDEAction( name() ) )
    {
        const int id = TDEAction::getToolButtonID();

        addContainer( bar, id );
        connect( bar, TQ_SIGNAL( destroyed() ), TQ_SLOT( slotDestroyed() ) );

        //TODO create menu on demand
        //TODO create menu above and aligned within window
        //TODO make the arrow point upwards!
        bar->insertButton( TQString(), id, true, i18n( "Menu" ), index );
        bar->alignItemRight( id );

        TDEToolBarButton* button = bar->getButton( id );
        button->setPopup( Amarok::Menu::instance() );
        button->setName( "toolbutton_amarok_menu" );
        button->setIcon( "amarok" );

        return containerCount() - 1;
    }
    else return -1;
}

Menu::Menu()
{
    TDEActionCollection *ac = Amarok::actionCollection();

    setCheckable( true );

    safePlug( ac, "repeat", this );
    safePlug( ac, "random_mode", this );

    insertSeparator();

    safePlug( ac, "playlist_playmedia", this );
    safePlug( ac, "play_audiocd", this );
    safePlug( ac, "lastfm_play", this );

    insertSeparator();

    insertItem( SmallIconSet( Amarok::icon( "covermanager" ) ), i18n( "C&over Manager" ), ID_SHOW_COVER_MANAGER );
    safePlug( ac, "queue_manager", this );
    insertItem( SmallIconSet( Amarok::icon( "visualizations" ) ), i18n( "&Visualizations" ), ID_SHOW_VIS_SELECTOR );
    insertItem( SmallIconSet( Amarok::icon( "equalizer" ) ), i18n( "E&qualizer" ), kapp, TQ_SLOT( slotConfigEqualizer() ), 0, ID_CONFIGURE_EQUALIZER );
    safePlug( ac, "script_manager", this );
    safePlug( ac, "statistics", this );

    insertSeparator();


    safePlug( ac, "update_collection", this );
    insertItem( SmallIconSet( Amarok::icon( "rescan" ) ), i18n("&Rescan Collection"), ID_RESCAN_COLLECTION );
    setItemEnabled( ID_RESCAN_COLLECTION, !ThreadManager::instance()->isJobPending( "CollectionScanner" ) );

#ifndef TQ_WS_MAC
    insertSeparator();

    safePlug( ac, KStdAction::name(KStdAction::ShowMenubar), this );
#endif

    insertSeparator();

    safePlug( ac, KStdAction::name(KStdAction::ConfigureToolbars), this );
    safePlug( ac, KStdAction::name(KStdAction::KeyBindings), this );
    safePlug( ac, "options_configure_globals", this ); //we created this one
    safePlug( ac, KStdAction::name(KStdAction::Preferences), this );

    insertSeparator();

    insertItem( SmallIconSet("help"), i18n( "&Help" ), helpMenu( this ) );

    insertSeparator();

    safePlug( ac, KStdAction::name(KStdAction::Quit), this );

    connect( this, TQ_SIGNAL( aboutToShow() ),  TQ_SLOT( slotAboutToShow() ) );
    connect( this, TQ_SIGNAL( activated(int) ), TQ_SLOT( slotActivated(int) ) );

    setItemEnabled( ID_SHOW_VIS_SELECTOR, false );
    #ifdef HAVE_LIBVISUAL
    setItemEnabled( ID_SHOW_VIS_SELECTOR, true );
    #endif
}

Menu*
Menu::instance()
{
    static Menu menu;
    return &menu;
}

TDEPopupMenu*
Menu::helpMenu( TQWidget *parent ) //STATIC
{
    if ( s_helpMenu == 0 )
        s_helpMenu = new KHelpMenu( parent, &aboutData, Amarok::actionCollection() );

    return s_helpMenu->menu();
}

void
Menu::slotAboutToShow()
{
    setItemEnabled( ID_CONFIGURE_EQUALIZER, EngineController::hasEngineProperty( "HasEqualizer" ) );
    setItemEnabled( ID_CONF_DECODER, EngineController::hasEngineProperty( "HasConfigure" ) );
}

void
Menu::slotActivated( int index )
{
    switch( index )
    {
    case ID_SHOW_COVER_MANAGER:
        CoverManager::showOnce();
        break;
    case ID_SHOW_VIS_SELECTOR:
        Vis::Selector::instance()->show(); //doing it here means we delay creation of the widget
        break;
    case ID_RESCAN_COLLECTION:
        CollectionDB::instance()->startScan();
        break;
    }
}

//////////////////////////////////////////////////////////////////////////////////////////
// PlayPauseAction
//////////////////////////////////////////////////////////////////////////////////////////

PlayPauseAction::PlayPauseAction( TDEActionCollection *ac )
        : TDEToggleAction( i18n( "Play/Pause" ), 0, ac, "play_pause" )
        , EngineObserver( EngineController::instance() )
{
    engineStateChanged( EngineController::engine()->state() );

    connect( this, TQ_SIGNAL(activated()), EngineController::instance(), TQ_SLOT(playPause()) );
}

void
PlayPauseAction::engineStateChanged( Engine::State state,  Engine::State /*oldState*/ )
{
    TQString text;

    switch( state ) {
    case Engine::Playing:
        setChecked( false );
        setIcon( Amarok::icon( "pause" ) );
        text = i18n( "Pause" );
        break;
    case Engine::Paused:
        setChecked( true );
        setIcon( Amarok::icon( "pause" ) );
        text = i18n( "Pause" );
        break;
    case Engine::Empty:
        setChecked( false );
        setIcon( Amarok::icon( "play" ) );
        text = i18n( "Play" );
        break;
    case Engine::Idle:
        return;
    }

    //update menu texts for this special action
    for( int x = 0; x < containerCount(); ++x ) {
        TQWidget *w = container( x );
        if( w->inherits( "TQPopupMenu" ) )
            static_cast<TQPopupMenu*>(w)->changeItem( itemId( x ), text );
        //TODO TDEToolBar sucks so much
//         else if( w->inherits( "TDEToolBar" ) )
//             static_cast<TDEToolBar*>(w)->getButton( itemId( x ) )->setText( text );
    }
}

//////////////////////////////////////////////////////////////////////////////////////////
// AnalyzerAction
//////////////////////////////////////////////////////////////////////////////////////////
#include "analyzerbase.h"

AnalyzerAction::AnalyzerAction( TDEActionCollection *ac )
        : TDEAction( i18n( "Analyzer" ), 0, ac, "toolbar_analyzer" )
{
    setShortcutConfigurable( false );
}

int
AnalyzerAction::plug( TQWidget *w, int index )
{
    //NOTE the analyzer will be deleted when the toolbar is deleted or cleared()
    //we are not designed for unplugging() yet so there would be a leak if that happens
    //but it's a rare event and unplugging is complicated.

    TDEToolBar *bar = dynamic_cast<TDEToolBar*>(w);

    if( bar && kapp->authorizeTDEAction( name() ) )
    {
        const int id = TDEAction::getToolButtonID();

        addContainer( w, id );
        connect( w, TQ_SIGNAL( destroyed() ), TQ_SLOT( slotDestroyed() ) );
        TQWidget *container = new AnalyzerContainer( w );
        bar->insertWidget( id, 0, container, index );
        bar->setItemAutoSized( id, true );

        return containerCount() - 1;
    }
    else return -1;
}


AnalyzerContainer::AnalyzerContainer( TQWidget *parent )
        : TQWidget( parent, "AnalyzerContainer" )
        , m_child( 0 )
{
    TQToolTip::add( this, i18n( "Click for more analyzers" ) );
    changeAnalyzer();
}

void
AnalyzerContainer::resizeEvent( TQResizeEvent *)
{
    m_child->resize( size() );
}

void AnalyzerContainer::changeAnalyzer()
{
    delete m_child;
    m_child = Analyzer::Factory::createPlaylistAnalyzer( this );
    m_child->setName( "ToolBarAnalyzer" );
    m_child->resize( size() );
    m_child->show();
}

void
AnalyzerContainer::mousePressEvent( TQMouseEvent *e)
{
    if( e->button() == TQt::LeftButton ) {
        AmarokConfig::setCurrentPlaylistAnalyzer( AmarokConfig::currentPlaylistAnalyzer() + 1 );
        changeAnalyzer();
    }
}

void
AnalyzerContainer::contextMenuEvent( TQContextMenuEvent *e)
{
#if defined HAVE_LIBVISUAL
    TDEPopupMenu menu;
    menu.insertItem( SmallIconSet( Amarok::icon( "visualizations" ) ), i18n("&Visualizations"), Menu::ID_SHOW_VIS_SELECTOR );

    if( menu.exec( mapToGlobal( e->pos() ) ) == Menu::ID_SHOW_VIS_SELECTOR )
        Menu::instance()->slotActivated( Menu::ID_SHOW_VIS_SELECTOR );
#else
    Q_UNUSED(e);
#endif
}

//////////////////////////////////////////////////////////////////////////////////////////
// ToggleAction
//////////////////////////////////////////////////////////////////////////////////////////

ToggleAction::ToggleAction( const TQString &text, void ( *f ) ( bool ), TDEActionCollection* const ac, const char *name )
        : TDEToggleAction( text, 0, ac, name )
        , m_function( f )
{}

void ToggleAction::setChecked( bool b )
{
    const bool announce = b != isChecked();

    m_function( b );
    TDEToggleAction::setChecked( b );
    AmarokConfig::writeConfig(); //So we don't lose the setting when crashing
    if( announce ) emit toggled( b ); //TDEToggleAction doesn't do this for us. How gay!
}

void ToggleAction::setEnabled( bool b )
{
    const bool announce = b != isEnabled();

    if( !b )
        setChecked( false );
    TDEToggleAction::setEnabled( b );
    AmarokConfig::writeConfig(); //So we don't lose the setting when crashing
    if( announce ) emit enabled( b );
}

//////////////////////////////////////////////////////////////////////////////////////////
// SelectAction
//////////////////////////////////////////////////////////////////////////////////////////

SelectAction::SelectAction( const TQString &text, void ( *f ) ( int ), TDEActionCollection* const ac, const char *name )
        : TDESelectAction( text, 0, ac, name )
        , m_function( f )
{ }

void SelectAction::setCurrentItem( int n )
{
    const bool announce = n != currentItem();

    m_function( n );
    TDESelectAction::setCurrentItem( n );
    AmarokConfig::writeConfig(); //So we don't lose the setting when crashing
    if( announce ) emit activated( n );
}

void SelectAction::setEnabled( bool b )
{
    const bool announce = b != isEnabled();

    if( !b )
        setCurrentItem( 0 );
    TDESelectAction::setEnabled( b );
    AmarokConfig::writeConfig(); //So we don't lose the setting when crashing
    if( announce ) emit enabled( b );
}

void SelectAction::setIcons( TQStringList icons )
{
    m_icons = icons;
    for( int i = 0, n = items().count(); i < n; ++i )
        popupMenu()->changeItem( i, kapp->iconLoader()->loadIconSet( *icons.at( i ), TDEIcon::Small ), popupMenu()->text( i ) );
}

TQStringList SelectAction::icons() const { return m_icons; }

TQString SelectAction::currentIcon() const
{
    if( m_icons.count() )
        return *m_icons.at( currentItem() );
    return TQString();
}

TQString SelectAction::currentText() const {
    return TDESelectAction::currentText() + "<br /><br />" + i18n("Click to change");
}

//////////////////////////////////////////////////////////////////////////////////////////
// VolumeAction
//////////////////////////////////////////////////////////////////////////////////////////

VolumeAction::VolumeAction( TDEActionCollection *ac )
        : TDEAction( i18n( "Volume" ), 0, ac, "toolbar_volume" )
        , EngineObserver( EngineController::instance() )
        , m_slider( 0 ) //is TQGuardedPtr
{}

int
VolumeAction::plug( TQWidget *w, int index )
{
    //NOTE we only support one plugging currently

    delete static_cast<Amarok::VolumeSlider*>( m_slider ); //just in case, remember, we only support one plugging!

    m_slider = new Amarok::VolumeSlider( w, Amarok::VOLUME_MAX );
    m_slider->setName( "ToolBarVolume" );
    m_slider->setValue( AmarokConfig::masterVolume() );
    m_slider->setSizePolicy( TQSizePolicy::Fixed, TQSizePolicy::Ignored );

    TQToolTip::add( m_slider, i18n( "Volume control" ) );

    EngineController* const ec = EngineController::instance();
    connect( m_slider, TQ_SIGNAL(sliderMoved( int )), ec, TQ_SLOT(setVolume( int )) );
    connect( m_slider, TQ_SIGNAL(sliderReleased( int )), ec, TQ_SLOT(setVolume( int )) );

    static_cast<TDEToolBar*>(w)->insertWidget( TDEAction::getToolButtonID(), 0, m_slider, index );

    return 0;
}

void
VolumeAction::engineVolumeChanged( int value )
{
    if( m_slider ) m_slider->setValue( value );
}



//////////////////////////////////////////////////////////////////////////////////////////
// RandomAction
//////////////////////////////////////////////////////////////////////////////////////////
RandomAction::RandomAction( TDEActionCollection *ac ) :
    SelectAction( i18n( "Ra&ndom" ), &AmarokConfig::setRandomMode, ac, "random_mode" )
{
    setItems( TQStringList() << i18n( "&Off" ) << i18n( "&Tracks" ) << i18n( "&Albums" ) );
    setCurrentItem( AmarokConfig::randomMode() );
    setIcons( TQStringList() << Amarok::icon( "random_no" ) << Amarok::icon( "random_track" ) << Amarok::icon( "random_album" ) );
}

void
RandomAction::setCurrentItem( int n )
{
    if( TDEAction *a = parentCollection()->action( "favor_tracks" ) )
        a->setEnabled( n );
    SelectAction::setCurrentItem( n );
}


//////////////////////////////////////////////////////////////////////////////////////////
// FavorAction
//////////////////////////////////////////////////////////////////////////////////////////
FavorAction::FavorAction( TDEActionCollection *ac ) :
    SelectAction( i18n( "&Favor" ), &AmarokConfig::setFavorTracks, ac, "favor_tracks" )
{
    setItems( TQStringList() << i18n( "Off" )
                            << i18n( "Higher &Scores" )
                            << i18n( "Higher &Ratings" )
                            << i18n( "Not Recently &Played" ) );

    setCurrentItem( AmarokConfig::favorTracks() );
    setEnabled( AmarokConfig::randomMode() );
}

//////////////////////////////////////////////////////////////////////////////////////////
// RepeatAction
//////////////////////////////////////////////////////////////////////////////////////////
RepeatAction::RepeatAction( TDEActionCollection *ac ) :
    SelectAction( i18n( "&Repeat" ), &AmarokConfig::setRepeat, ac, "repeat" )
{
    setItems( TQStringList() << i18n( "&Off" ) << i18n( "&Track" )
                            << i18n( "&Album" ) << i18n( "&Playlist" ) );
    setIcons( TQStringList() << Amarok::icon( "repeat_no" ) << Amarok::icon( "repeat_track" ) << Amarok::icon( "repeat_album" ) << Amarok::icon( "repeat_playlist" ) );
    setCurrentItem( AmarokConfig::repeat() );
}

//////////////////////////////////////////////////////////////////////////////////////////
// BurnMenuAction
//////////////////////////////////////////////////////////////////////////////////////////
BurnMenuAction::BurnMenuAction( TDEActionCollection *ac )
  : TDEAction( i18n( "Burn" ), 0, ac, "burn_menu" )
{}

int
BurnMenuAction::plug( TQWidget *w, int index )
{
    TDEToolBar *bar = dynamic_cast<TDEToolBar*>(w);

    if( bar && kapp->authorizeTDEAction( name() ) )
    {
        const int id = TDEAction::getToolButtonID();

        addContainer( bar, id );
        connect( bar, TQ_SIGNAL( destroyed() ), TQ_SLOT( slotDestroyed() ) );

        bar->insertButton( TQString(), id, true, i18n( "Burn" ), index );

        TDEToolBarButton* button = bar->getButton( id );
        button->setPopup( Amarok::BurnMenu::instance() );
        button->setName( "toolbutton_burn_menu" );
        button->setIcon( "k3b" );

        return containerCount() - 1;
    }
    else return -1;
}

BurnMenu::BurnMenu()
{
    insertItem( i18n("Current Playlist"), CURRENT_PLAYLIST );
    insertItem( i18n("Selected Tracks"), SELECTED_TRACKS );
    //TODO add "album" and "all tracks by artist"

    connect( this, TQ_SIGNAL( aboutToShow() ),  TQ_SLOT( slotAboutToShow() ) );
    connect( this, TQ_SIGNAL( activated(int) ), TQ_SLOT( slotActivated(int) ) );
}

TDEPopupMenu*
BurnMenu::instance()
{
    static BurnMenu menu;
    return &menu;
}

void
BurnMenu::slotAboutToShow()
{}

void
BurnMenu::slotActivated( int index )
{
    switch( index )
    {
    case CURRENT_PLAYLIST:
        K3bExporter::instance()->exportCurrentPlaylist();
        break;

    case SELECTED_TRACKS:
        K3bExporter::instance()->exportSelectedTracks();
        break;
    }
}

//////////////////////////////////////////////////////////////////////////////////////////
// StopMenuAction
//////////////////////////////////////////////////////////////////////////////////////////

StopAction::StopAction( TDEActionCollection *ac )
  : TDEAction( i18n( "Stop" ), Amarok::icon( "stop" ), 0, EngineController::instance(), TQ_SLOT( stop() ), ac, "stop" )
{}

int
StopAction::plug( TQWidget *w, int index )
{
    TDEToolBar *bar = dynamic_cast<TDEToolBar*>(w);

    if( bar && kapp->authorizeTDEAction( name() ) )
    {
        const int id = TDEAction::getToolButtonID();

        addContainer( bar, id );
        connect( bar, TQ_SIGNAL( destroyed() ), TQ_SLOT( slotDestroyed() ) );

        bar->insertButton( TQString(), id, TQ_SIGNAL( clicked() ), EngineController::instance(), TQ_SLOT( stop() ),
                           true, i18n( "Stop" ), index );

        TDEToolBarButton* button = bar->getButton( id );
        button->setDelayedPopup( Amarok::StopMenu::instance() );
        button->setName( "toolbutton_stop_menu" );
        button->setIcon( Amarok::icon( "stop" ) );
        button->setEnabled( EngineController::instance()->engine()->loaded() );  // Disable button at startup

        return containerCount() - 1;
    }
    else return TDEAction::plug( w, index );
}

StopMenu::StopMenu()
{
    insertTitle( i18n( "Stop" ) );
    insertItem( i18n("Now"), NOW );
    insertItem( i18n("After Current Track"), AFTER_TRACK );
    insertItem( i18n("After Queue"), AFTER_QUEUE );

    connect( this, TQ_SIGNAL( aboutToShow() ),  TQ_SLOT( slotAboutToShow() ) );
    connect( this, TQ_SIGNAL( activated(int) ), TQ_SLOT( slotActivated(int) ) );
}

TDEPopupMenu*
StopMenu::instance()
{
    static StopMenu menu;
    return &menu;
}

void
StopMenu::slotAboutToShow()
{
    Playlist *pl = Playlist::instance();

    setItemEnabled( NOW,         Amarok::actionCollection()->action( "stop" )->isEnabled() );

    setItemEnabled( AFTER_TRACK, EngineController::engine()->loaded() );
    setItemChecked( AFTER_TRACK, pl->stopAfterMode() == Playlist::StopAfterCurrent );

    setItemEnabled( AFTER_QUEUE, pl->nextTracks().count() );
    setItemChecked( AFTER_QUEUE, pl->stopAfterMode() == Playlist::StopAfterQueue );
}

void
StopMenu::slotActivated( int index )
{
    Playlist* pl = Playlist::instance();
    const int mode = pl->stopAfterMode();

    switch( index )
    {
        case NOW:
            Amarok::actionCollection()->action( "stop" )->activate();
            if( mode == Playlist::StopAfterCurrent || mode == Playlist::StopAfterQueue )
                pl->setStopAfterMode( Playlist::DoNotStop );
            break;
        case AFTER_TRACK:
            pl->setStopAfterMode( mode == Playlist::StopAfterCurrent
                                ? Playlist::DoNotStop
                                : Playlist::StopAfterCurrent );
            break;
        case AFTER_QUEUE:
            pl->setStopAfterMode( mode == Playlist::StopAfterQueue
                                ? Playlist::DoNotStop
                                : Playlist::StopAfterQueue );
            break;
    }
}


#include "actionclasses.moc"
