/*
 *  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.
 *
 *  This program 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 for more details.
 *
 *  You should have received a copy of the GNU General Public License
 *  along with this program; if not, write to the Free Software
 *  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
 */

/*
 * NOTES:
 * 
 * int QListBox::currentItem() returns -1 if no item is selected.
 * Always check the index before using it as either listbox or
 * list index. When removing listbox items, the highlighted(int)
 * signal gets emitted which may be -1, too.
 * 
 * The current playlist can be modified directly through the
 * pointer.
 * 
 * Quite a few functions are guarded by a null pointer check
 * against the playlist pointer just in case the playlist dialog
 * has not been set up correctly after creation.
 */

#include <qapplication.h>
#include <qdir.h>
#include <qfiledialog.h>
#include <qlabel.h>
#include <qlistbox.h>
#include <qmessagebox.h>
#include <qpopupmenu.h>
#include <qpushbutton.h>
#include <qspinbox.h>
#include <qstring.h>

#include "GlobalIcon.h"
#include "Playlist.h"
#include "PlaylistEditDialog.h"
#include "wrapper/SongLength.h"
#include "wrapper/SidTuneWrapper.h"

PlaylistEditDialog::PlaylistEditDialog( QWidget* parent,  const char* name, bool modal, WFlags fl )
: PlaylistEditDialogData( parent, name, modal, fl ), maxSongs( SidTuneWrapper::getMaxSongs() )
{
    setIcon(*mainIcon);

    playlistMenu = new QPopupMenu(this);
    Q_CHECK_PTR( playlistMenu );
    playlistMenu->insertItem( "Delete", this, SLOT( deleteEntry() ));
    playlistMenu->insertItem( "Play", this, SLOT( playEntry() ));
    playlistMenu->insertItem( "Fetch time", this, SLOT( updateEntryFetchTime() ));
    playlistMenu->insertSeparator();
    saveListMenuID = playlistMenu->insertItem( "Save list", this, SLOT( savePlaylist() ));
    playlistMenu->insertItem( "Clear list", this, SLOT( clearPlaylist() ));
    
    playlist = 0;  // start with no list (*DANGER*)
    
    clear();  // clear list and listbox
}

PlaylistEditDialog::~PlaylistEditDialog()
{
    // DO NOT...
    //
    // ... delete 'playlist' here. It is just a copy of a pointer
    // to a list maintained by some other component.
    //
    // ... delete child widgets, Qt does it all for us.
}

void PlaylistEditDialog::takeList(Playlist* inList)
{
    QApplication::setOverrideCursor( Qt::waitCursor );
    playlist = inList;
    playListBox->clear();
    // Copy each song title into listbox.
    playlist->lock();
    QListIterator<PlaylistItem> it(playlist->list);
    for ( it.toFirst(); it.current(); ++it ) 
    {
        playListBox->insertItem(it.current()->titleString);
    }
    playlist->unlock();
    if ( playListBox->count() )
    {
        playListBox->setCurrentItem(0);
    }
    QApplication::restoreOverrideCursor();
}

void PlaylistEditDialog::add(PlaylistItem* item)
{
    if ( playlist==0 )  // should never happen
        return;
    playListBox->insertItem(item->titleString);
}

void PlaylistEditDialog::clear()
{
    if ( playlist==0 )
        return;
    playListBox->clear();
    playlist->clear();
    
    listPosLabel->setText(QString("#%1").arg( (int)0 ));
    subtuneSpinBox->setMaxValue(1);
    subtuneSpinBox->setValue(1);
    fadeoutSpinBox->setValue(0);
    playtimeSpinBox->setValue(0);
    // Might disable spinboxes here until new item is available.
}

void PlaylistEditDialog::exchangeItems(uint item1, uint item2)
{
    playlist->lock();
    // Swap list box item texts.
    playListBox->changeItem(playlist->list.at(item1)->titleString,item2);
    playListBox->changeItem(playlist->list.at(item2)->titleString,item1);
    // Exchange list items by copying.
    PlaylistItem sourceItem = *(playlist->list.at(item1));
    *(playlist->list.at(item1)) = *(playlist->list.at(item2));
    *(playlist->list.at(item2)) = sourceItem;
    playlist->setModified();
    playlist->unlock();
    emit playlistEditUpdateRequest(item1);
    emit playlistEditUpdateRequest(item2);
}

// slot
void PlaylistEditDialog::clearPlaylist()
{
    if ( playlist==0 )
        return;
    QString tmp;
    if ( playlist->isModified() )
    {
        tmp = tr("Playlist has not been saved yet!\n"
                 "Do you really want to clear this playlist?");
    }
    else if ( !playlist->list.isEmpty() )
    {
        tmp = tr("Do you really want to clear this playlist?");
    }
    if ( !tmp.isEmpty() )  // need msgbox
    {
        QMessageBox mb( "SIDPLAY",
                        tmp,
                        QMessageBox::Information,
                        QMessageBox::Yes,
                        QMessageBox::No | QMessageBox::Default | QMessageBox::Escape,
                        0);
        mb.setButtonText( QMessageBox::Yes, "Yes" );
        mb.setButtonText( QMessageBox::No, "No" );
        switch( mb.exec() ) 
        {
         case QMessageBox::Yes:
            break;
         case QMessageBox::No:
            return;
        }
    }
    clear();
    emit playlistEditClearRequest();
}

// slot
void PlaylistEditDialog::deleteEntry()
{
    if ( playlist==0 )
        return;
    if ( playListBox->count() && playListBox->currentItem()>=0 )
    {
        int i = playListBox->currentItem();
        
        QMessageBox mb( "SIDPLAY",
                        "Do you really want to delete current entry?",
                        QMessageBox::Information,
                        QMessageBox::Yes,
                        QMessageBox::No | QMessageBox::Default | QMessageBox::Escape,
                        0);
        mb.setButtonText( QMessageBox::Yes, "Yes" );
        mb.setButtonText( QMessageBox::No, "No" );
        switch( mb.exec() ) 
        {
         case QMessageBox::Yes:
            break;
         case QMessageBox::No:
            return;
        }
        // We must first modify the list, so the listbox s
        // signals can trigger work on sane list contents.
        playlist->remove(i);
        playListBox->removeItem(i);  // emits itemhighlighted(int)
        emit playlistEditDelRequest(i);
    }
}

// slot
void PlaylistEditDialog::playlistItemSelected(int index)
{
    if ( playlist==0 )
        return;
    if ( index >= 0 )
    {
        playlist->setCurrentPlayPos(index);
        curPlaylistItem = *(playlist->list.at(index));
        emit playlistEditPlayRequest(curPlaylistItem);
        emit playlistEditPlayRequest(index);
    }
}

// slot
void PlaylistEditDialog::playlistItemHighlighted(int index)
{
    if ( playlist==0 )
        return;
    if ( index >= 0 )
    {
        PlaylistItem* pli = playlist->list.at(index);
        listPosLabel->setText(QString("#%1").arg(index+1));
        subtuneSpinBox->setMaxValue( maxSongs );
        subtuneSpinBox->setValue( pli->subtune );
        subtuneSpinBox->setMaxValue( pli->songs );
        fadeoutSpinBox->setValue( pli->fadeout );
        playtimeSpinBox->setValue( pli->time );
    }
}

// slot
void PlaylistEditDialog::subtuneValueChanged(int newVal)
{
    if ( playlist==0 )
        return;
    if ( playListBox->currentItem() >= 0 )
    {
        PlaylistItem* pli = playlist->list.at( playListBox->currentItem() );
        int oldVal = pli->subtune;
        // QSpinBox::setValue(int) emits valueChanged(int).
        // Hence, when we set up the spinbox we must recognize that.
        if ( oldVal != newVal )
            playlist->setModified();
        pli->subtune = newVal;
    }
}

// slot
void PlaylistEditDialog::playtimeValueChanged(int newVal)
{
    if ( playlist==0 )
        return;
    if ( playListBox->currentItem() >= 0 )
    {
        PlaylistItem* pli = playlist->list.at( playListBox->currentItem() );
        int oldVal = pli->time;
        // QSpinBox::setValue(int) emits valueChanged(int).
        // Hence, when we set up the spinbox we must recognize that.
        if ( oldVal != newVal )
            playlist->setModified();
        pli->time = newVal;
    }
}

// slot
void PlaylistEditDialog::updateEntryFetchTime()
{
    if ( playlist==0 || playListBox->currentItem()<0 )
        return;
    if ( !SongLength::getStatus() )
    {
        QMessageBox error;
        error.setIcon( QMessageBox::Warning );
        error.setText("No songlength database available!");
        error.adjustSize();
        error.exec();
        return;
    }
    PlaylistItem* pli = playlist->list.at( playListBox->currentItem() );
    SongLengthDBitem sli;
    // If anything goes wrong here or this sidtune is not in the DB,
    // don't update the current playtime.
    if ( !SongLength::getItem( pli->fileNameString, pli->subtune, sli )  )
        return;
    int oldVal = pli->time;
    // QSpinBox::setValue(int) emits valueChanged(int).
    // Hence, when we set up the spinbox we must recognize that.
    if ( oldVal == sli.playtime )
        return;
    pli->time = sli.playtime;
    playtimeSpinBox->setValue( pli->time );
    playlist->setModified();
}

// slot
void PlaylistEditDialog::fadeoutValueChanged(int newVal)
{
    if ( playlist==0 )
        return;
    if ( playListBox->currentItem() >= 0 )
    {
        PlaylistItem* pli = playlist->list.at( playListBox->currentItem() );
        int oldVal = pli->fadeout;
        // QSpinBox::setValue(int) emits valueChanged(int).
        // Hence, when we set up the spinbox we must recognize that.
        if ( oldVal != newVal )
            playlist->setModified();
        pli->fadeout = newVal;
    }
}

// slot
void PlaylistEditDialog::playEntry()
{
    if ( playlist==0 )
        return;
    uint index = playListBox->currentItem();
    if ( index>=0 && index<playListBox->count() )
    {
        curPlaylistItem = *(playlist->list.at(index));
        emit playlistEditPlayRequest(curPlaylistItem);
    }
}

// slot
void PlaylistEditDialog::prevButtonClicked()
{
    if ( playlist==0 )
        return;
    // We take the current index from the listbox, but also
    // work with it when modifying the playlist.
    int sourcePos = playListBox->currentItem();
    if ( playListBox->count()>1 && sourcePos>0 )
    {
        playListBox->setCurrentItem(sourcePos-1);
        playEntry();
    }
}

// slot
void PlaylistEditDialog::nextButtonClicked()
{
    if ( playlist==0 )
        return;
    // We take the current index from the listbox, but also
    // work with it when modifying the playlist.
    int items = playListBox->count(); 
    int sourcePos = playListBox->currentItem();
    if ( items>1 && sourcePos>=0 && (sourcePos<items-1) )
    {
        playListBox->setCurrentItem(sourcePos+1);
        playEntry();
    }
}

// slot
void PlaylistEditDialog::savePlaylist()
{
    if ( !playlist->save() )
    {
        QMessageBox error;
        error.setIcon(QMessageBox::Critical);
        error.setText("Could not save playlist!");
        error.adjustSize();
        error.exec();
    }
}

// slot
void PlaylistEditDialog::popUpListMenu(QListBoxItem*, const QPoint& pos)
{
    bool havePlaylistFileName = false;
    if ( playlist!=0 )
    {
        havePlaylistFileName = playlist->haveFileName();
    }
    playlistMenu->setItemEnabled(saveListMenuID,havePlaylistFileName);
    playlistMenu->exec(pos);
}

// slot
// Try to move current item to top pos (0).
void PlaylistEditDialog::moveUpButtonClicked()
{
    if ( playlist==0 )
        return;
    // We take the current index from the listbox, but also
    // work with it when modifying the playlist.
    int sourcePos = playListBox->currentItem();
    if ( playListBox->count()>1 && sourcePos>0 )
    {
        int destPos = sourcePos-1;
        exchangeItems(sourcePos,destPos);
        playListBox->setCurrentItem(destPos);
    }
}

// slot
// Try to move current item to bottom pos (count-1).
void PlaylistEditDialog::moveDownButtonClicked()
{
    if ( playlist==0 )
        return;
    // We take the current index from the listbox, but also
    // work with it when modifying the playlist.
    int items = playListBox->count(); 
    int sourcePos = playListBox->currentItem();
    if ( items>1 && sourcePos>=0 && (sourcePos<items-1) )
    {
        int destPos = sourcePos+1;
        exchangeItems(sourcePos,destPos);
        playListBox->setCurrentItem(destPos);
    }
}

