/*
*  qm_playlistview.cpp
*  QUIMUP playlist listview
*  © 2008-2024 Johan Spee
*  SPDX-License-Identifier: GPL-3.0-or-later
*/

#include "qm_playlistview.h"
#include "qm_commandlist.h"
#include "qm_modes_ids.h"


qm_playlistView::qm_playlistView(QWidget *parent, qm_Config *cfg)
{
    setParent(parent);
    config = cfg;

    if (objectName().isEmpty())
        setObjectName("quimup playlist");

    set_themed_icons(config->set_dark_theme);

    // init_vars
    mpd_cmd = nullptr;
    dropBeforeIndex = -1;
    line = new QWidget(viewport());
    line->hide();
    line->setAutoFillBackground(true);
    QPalette pal = line->palette();
    pal.setColor(line->backgroundRole(), QColor("#5f8ba9"));
    line->setPalette(pal);
    setDragDropMode(QAbstractItemView::DragDrop);
    setDropIndicatorShown(false);
    setDragEnabled(true);

    string_time_width = "";
    string_pos_width = "";
    current_song_pos = -1;
    current_song_id = -1;
    current_status = 0;
    col_pos_width = 20;
    col_time_width = 20;
    resize_cols_count = 10;
    b_is_on_start = true;
    b_streamplaying = false;
    b_waspurged = true;
    b_mpd_connected = false;
    col_default_fg = this->palette().color(QPalette::WindowText);
    col_played_fg  = QColor("#70707A");
    // col_playing_fg : in set_themed_icons()

    setup_widgets();
}


void qm_playlistView::setup_widgets()
{
    setRootIsDecorated(false);
    setItemsExpandable (false);
    setUniformRowHeights(true);
    setAllColumnsShowFocus(true);
    setSelectionMode(ExtendedSelection);
    sortByColumn (-1, Qt::AscendingOrder); // no sorting
    setColumnCount(6);
    QStringList labels;
    labels << "#" << " " << " " + tr("artist") + " " << " " + tr("title") + " " << " " + tr("m:s") + " " << " " + tr("album : nr") + " ";
    setHeaderLabels(labels);
    // icon set in set_themed_icons
    connect( this, SIGNAL(itemDoubleClicked (QTreeWidgetItem *, int)), this, SLOT(on_item_dclicked(QTreeWidgetItem *)) );

    context_menu = new QMenu(this);
    this->setContextMenuPolicy(Qt::DefaultContextMenu);

    a_clear_list = new QAction(context_menu);
    a_clear_list->setText(tr("Clear list"));
    a_clear_list->setIcon(ic_a_clear_list);
    connect( a_clear_list, SIGNAL(triggered()), this, SLOT(clear_it()) );
    context_menu->addAction(a_clear_list);

    a_delete_selection = new QAction(context_menu);
    a_delete_selection->setText(tr("Remove selected"));
    a_delete_selection->setIcon(ic_a_delete_selection);
    connect( a_delete_selection, SIGNAL(triggered()), this, SLOT(delete_it()) );
    context_menu->addAction(a_delete_selection);

    a_purge_played = new QAction(context_menu);
    a_purge_played->setText(tr("Purge played"));
    a_purge_played->setIcon(ic_a_purge_played);
    connect( a_purge_played, SIGNAL(triggered()), this, SLOT(purge_it()) );
    context_menu->addAction(a_purge_played);

    a_reset_played = new QAction(context_menu);
    a_reset_played->setText(tr("Reset status"));
    a_reset_played->setIcon(ic_a_reset_played);
    connect( a_reset_played, SIGNAL(triggered()), this, SLOT(reset_played()) );
    context_menu->addAction(a_reset_played);

    a_shuffle_list = new QAction(context_menu);
    a_shuffle_list->setText(tr("Shuffle list"));
    a_shuffle_list->setIcon(ic_a_shuffle_list);
    connect( a_shuffle_list, SIGNAL(triggered()), this, SLOT(shuffle_it()) );
    context_menu->addAction(a_shuffle_list);

    a_save_list = new QAction(context_menu);
    a_save_list->setText(tr("Save list"));
    a_save_list->setIcon(ic_a_save_list);
    connect( a_save_list, SIGNAL(triggered()), this, SLOT(save_list()) );
    context_menu->addAction(a_save_list);

    a_save_selection = new QAction(context_menu);
    a_save_selection->setText(tr("Save selection"));
    a_save_selection->setIcon(ic_a_save_selection);
    connect( a_save_selection, SIGNAL(triggered()), this, SLOT(save_selected()) );
    context_menu->addAction(a_save_selection);

    hvw_playlist = new QHeaderView(Qt::Horizontal, this);
    hvw_playlist->setHighlightSections(false);
    hvw_playlist->setSectionsClickable(false);
    hvw_playlist->setDefaultAlignment(Qt::AlignLeft);
    hvw_playlist->setStretchLastSection(true);
    hvw_playlist->setMinimumSectionSize(20);
    setHeader(hvw_playlist);
    hvw_playlist->show();

    headerItem()->setIcon(1, ic_track_played);

    set_auto_columns();
}


void qm_playlistView::reset_played()
{
    QTreeWidgetItemIterator itr(this);
    while (*itr)
    {
        if ( (*itr)->get_state == STATE_PLAYED && (*itr)->get_id != current_song_id)
        {
            // reset icon
            if ((*itr)->get_type == TP_SONG)
                (*itr)->setIcon(1, ic_track);
            else
            {
                if ((*itr)->get_type == TP_SONGX)
                    (*itr)->setIcon(1, ic_trackx);
                else
                    (*itr)->setIcon(1, ic_stream);
            }

            // reset colors
            for (int i = 0; i < 6; i++)
                (*itr)->setForeground( i, QBrush(col_default_fg));

            // reset state
            (*itr)->set_state(STATE_NEW);
        }
        ++itr;
    }
}


void qm_playlistView::keyPressEvent(QKeyEvent *event)
{
    switch (event->key())
    {
        case Qt::Key_Return:
        {
            on_item_dclicked(currentItem());
            break;
        }

        case Qt::Key_Delete:
        {
            delete_it();
            break;
        }

        case Qt::Key_Space:
        {
            if (current_song_pos != -1 && current_song_pos < topLevelItemCount())
                scrollToItem(topLevelItem(current_song_pos));
            break;
        }

        case Qt::Key_Home:
        {
            if (topLevelItemCount() > 0)
                scrollToItem(topLevelItem(0));
            break;
        }

        case Qt::Key_End:
        {
            if (topLevelItemCount() > 0)
                scrollToItem(topLevelItem(topLevelItemCount() - 1));
            break;
        }

        case Qt::Key_Down:
        {
            if (currentItem()->get_pos < topLevelItemCount())
                setCurrentItem(itemBelow ( currentItem() ));
            break;
        }

        case Qt::Key_Up:
        {
            if (currentItem()->get_pos > 1)
                setCurrentItem(itemAbove ( currentItem() ));
            break;
        }

        default:
            QTreeWidget::keyPressEvent(event);
    }
}


void qm_playlistView::set_playlist_stats(int numTracks, int TotalTime)
{
    QString plist_stats = "<i>" + tr("Tracks") + "</i> " + QString::number(numTracks) + " &middot; <i>" + tr("Total Time") + "</i> " + into_time(TotalTime);
    emit signal_playlist_statistics(plist_stats);
}


void qm_playlistView::on_item_dclicked(QTreeWidgetItem *item)
{
    int itemID = item->get_id;
    if (itemID != -1 && b_mpd_connected)
    {
        // a stream may trigger on_new_playlist before set_newsong
        // and reset the list. So set ID and b_streamplaying now.
        current_song_id = itemID;
        if (item->get_type == TP_STREAM)
            b_streamplaying = true;
        else
            b_streamplaying = false;

        mpd_cmd->play_this(static_cast<uint>(itemID));
        // will check for error "No such file or directory"
    }
}


void qm_playlistView::set_status(int newStatus)
{
    if (current_song_pos == -1 || current_song_pos >= topLevelItemCount() )
        return;

    switch (newStatus)
    {
        case 1: // MPD_STATE_STOP
        {
            topLevelItem(current_song_pos)->setIcon(1, ic_stop);
            if ( topLevelItem(current_song_pos)->get_state != STATE_PLAYING )
                for (int i = 0; i < 6; i++)
                    topLevelItem(current_song_pos)->setForeground( i, QBrush(col_playing_fg));
            break;
        }

        case 2: // MPD_STATE_PLAY
        {
            topLevelItem(current_song_pos)->setIcon(1, ic_play);
            for (int i = 0; i < 6; i++)
                topLevelItem(current_song_pos)->setForeground( i, QBrush(col_playing_fg));
            topLevelItem(current_song_pos)->set_state(STATE_PLAYING);
            break;
        }

        case 3: // MPD_STATE_PAUSE
        {
            topLevelItem(current_song_pos)->setIcon(1, ic_pause);
            if ( topLevelItem(current_song_pos)->get_state != STATE_PLAYING )
                for (int i = 0; i < 6; i++)
                    topLevelItem(current_song_pos)->setForeground( i, QBrush(col_playing_fg));
            break;
        }

        default: // 0 (MPD_STATE_UNKNOWN) or -1 (Disconnected)
        {
            topLevelItem(current_song_pos)->setIcon(1, ic_wait);
            break;
        }
    }
    current_status = newStatus;
}

// called by set_markplayed() from Player upon signal from Settings
void qm_playlistView::set_markplayed()
{
    if ( topLevelItemCount() == 0 )
        return;

    if (config->mark_whenplayed) // show visual 'played' state
    {
        QTreeWidgetItemIterator itr(this);
        while (*itr)
        {
            if ( (*itr)->get_state == STATE_PLAYED && (*itr)->get_id != current_song_id)
            {
                if ((*itr)->get_type == TP_SONG)
                    (*itr)->setIcon(1, ic_track_played);
                else
                {
                    if ((*itr)->get_type == TP_SONGX)
                        (*itr)->setIcon(1, ic_trackx_played);
                    else
                        (*itr)->setIcon(1, ic_stream_played);
                }
                for (int i = 0; i < 6; i++)
                    (*itr)->setForeground( i, QBrush(col_played_fg));
            }
            ++itr;
        }
    }
    else // remove visual 'played' state
    {
        QTreeWidgetItemIterator itr(this);
        while (*itr)
        {
            if ( (*itr)->get_state == STATE_PLAYED && (*itr)->get_id != current_song_id)
            {
                if ((*itr)->get_type == TP_SONG)
                    (*itr)->setIcon(1, ic_track);
                else
                {
                    if ((*itr)->get_type == TP_SONGX)
                        (*itr)->setIcon(1, ic_trackx);
                    else
                        (*itr)->setIcon(1, ic_stream);
                }
                for (int i = 0; i < 6; i++)
                    (*itr)->setForeground( i, QBrush(col_default_fg));
            }
            ++itr;
        }
    }
}


void qm_playlistView::set_newsong(int newPos)
{
    if ( topLevelItemCount() == 0 || !(newPos < topLevelItemCount()) )
        return;

    // reset current
    if (current_song_pos != -1)
    {
        switch (topLevelItem(current_song_pos)->get_state)
        {
            case STATE_PLAYING:
            {
                topLevelItem(current_song_pos)->set_state(STATE_PLAYED);
                if (config->mark_whenplayed)
                {
                    if (topLevelItem(current_song_pos)->get_type == TP_SONG)
                        topLevelItem(current_song_pos)->setIcon(1, ic_track_played);
                    else
                    {
                        if (topLevelItem(current_song_pos)->get_type == TP_SONGX)
                            topLevelItem(current_song_pos)->setIcon(1, ic_trackx_played);
                        else
                            topLevelItem(current_song_pos)->setIcon(1, ic_stream_played);
                    }
                    for(int i = 0; i < 6; i++)
                        topLevelItem(current_song_pos)->setForeground( i, QBrush(col_played_fg));

                    break;
                }
                [[fallthrough]];
                // else no break -> default (fallthrough intentional)
            }

            case STATE_PLAYED:
            {
                if (config->mark_whenplayed)
                {
                    if (topLevelItem(current_song_pos)->get_type == TP_SONG)
                        topLevelItem(current_song_pos)->setIcon(1, ic_track_played);
                    else
                    {
                        if (topLevelItem(current_song_pos)->get_type == TP_SONGX)
                            topLevelItem(current_song_pos)->setIcon(1, ic_trackx_played);
                        else
                            topLevelItem(current_song_pos)->setIcon(1, ic_stream_played);
                    }
                    for (int i = 0; i < 6; i++)
                        topLevelItem(current_song_pos)->setForeground( i, QBrush(col_played_fg));
                    break;
                }
                [[fallthrough]];
                // else no break -> default (fallthrough intentional)
            }

            default: // STATE_NEW or !config->mark_whenplayed
            {
                if (topLevelItem(current_song_pos)->get_type == TP_SONG)
                    topLevelItem(current_song_pos)->setIcon(1, ic_track);
                else
                {
                    if (topLevelItem(current_song_pos)->get_type == TP_SONGX)
                        topLevelItem(current_song_pos)->setIcon(1, ic_trackx);
                    else
                        topLevelItem(current_song_pos)->setIcon(1, ic_stream);
                }
                for (int i = 0; i < 6; i++)
                    topLevelItem(current_song_pos)->setForeground( i, QBrush(col_default_fg));
                break;
            }
        } // end switch
    }

    if (newPos < 0 ) // 'no song'
    {
        current_song_id = -1;
        if (topLevelItemCount() > 0)
        {
            current_song_pos = 0;
            set_status(-1);
        }
        else
            current_song_pos = -1;
        return;
    }

    current_song_id = topLevelItem(newPos)->get_id;

    if (topLevelItem(newPos)->get_type == TP_STREAM)
        b_streamplaying = true;
    else
        b_streamplaying = false;

    current_song_pos = newPos;

    set_status(current_status);
}


qm_IDlist qm_playlistView::get_played_IDs()
{
    qm_IDlist the_list;
    QTreeWidgetItemIterator itr(this);
    while (*itr)
    {
        if ( (*itr)->get_state == STATE_PLAYED)
        {
            the_list.push_back((*itr)->get_id);
        }
        ++itr;
    }
    return the_list;
}


void qm_playlistView::on_new_playlist(qm_songinfo newPlaylist, int changerID)
{
    // prevent resetting by a stream
    if (b_streamplaying)
    {
        // the current song did indeed reset the list
        if (current_song_id == changerID)
        {
            b_waspurged = false;
            return;
        }
    }

    // reset current song
    current_song_id = -10;
    // keep current_status: it is needed until the status actualyy changes

    qm_IDlist shadow_list;
    if (!b_waspurged)
        shadow_list= get_played_IDs();// ID-list of the 'played' items

    clear();

    QTreeWidgetItem *new_item;
    int pos = 0;
    int plist_totaltime = 0;
    int plist_maxtime = 0;

    QList<qm_songInfo>::iterator iter;
    for (iter = newPlaylist.begin(); iter != newPlaylist.end(); ++iter)
    {
        bool b_wasplayed = false;
        if (!b_waspurged)
        {
            QListIterator<int> itr(shadow_list);
            QList<int>::iterator IDitr;
            for (IDitr = shadow_list.begin(); IDitr != shadow_list.end(); ++IDitr)
            {
                if (iter->song_id == *IDitr)
                {
                    b_wasplayed = true;
                    break;
                }
            }
        }

        if (iter->type != TP_NOSONG)
        {
            pos++;
            if (iter->type == TP_STREAM)
            {
                if (iter->name.isEmpty())
                    iter->name = tr("Stream");
                if (iter->title.isEmpty())
                    iter->title = tr("info when playing");
                QStringList labels;
                labels << " " + QString::number(pos) + " " << "" << " " + iter->name + " " << iter->title << "  . . .  " << " "+  iter->uri + " ";
                new_item =  new QTreeWidgetItem(labels);
                new_item->set_pos(pos);
                if (b_wasplayed)
                {
                    new_item->set_state(STATE_PLAYED);
                    if (config->mark_whenplayed)
                    {
                        new_item->setIcon(1, ic_stream_played);
                        for (int i = 0; i < 6; i++)
                            new_item->setForeground( i, QBrush(col_played_fg));
                    }
                    else
                        new_item->setIcon(1, ic_stream);
                }
                else
                {
                    new_item->setIcon(1, ic_stream);
                    new_item->set_state(STATE_NEW);
                }
                new_item->set_id(iter->song_id);
                new_item->set_type(TP_STREAM);
                new_item->set_time(0);
                new_item->set_uri(iter->uri);
                addTopLevelItem(new_item);
            }
            else
                if (iter->type == TP_SONG || iter->type == TP_SONGX) // should always be true
                {
                    plist_totaltime += iter->time;
                    if (iter->time > plist_maxtime)
                        plist_maxtime = iter->time;
                    if (iter->title.isEmpty())
                    {
                        // use file name as title
                        int i = (iter->uri).lastIndexOf( '/' ) + 1;
                        iter->title = (iter->uri).right((iter->uri).length() - i);
                    }
                    if (iter->album.isEmpty())
                        iter->album = "?";
                    if (iter->artist.isEmpty())
                        iter->artist = "?";
                    QStringList labels;
                    QString track = "?";
                    if (!iter->track.isEmpty())
                        track = iter->track;
                    if (!iter->disc.isEmpty())
                        track = track + " / " + iter->disc;
                    labels  << " " + QString::number(pos) + " "
                    << "" // icon
                    << " " + iter->artist + " "
                    << " " + iter->title + " "
                    << " " + into_time(iter->time) + " "
                    << " " + iter->album + " : " + track + " ";
                    new_item =  new QTreeWidgetItem(labels);
                    new_item->set_pos(pos);
                    if (iter->type == TP_SONG)
                    {
                        if (b_wasplayed)
                        {
                            new_item->set_state(STATE_PLAYED);
                            if (config->mark_whenplayed)
                            {
                                new_item->setIcon(1, ic_track_played);
                                for (int i = 0; i < 6; i++)
                                    new_item->setForeground( i, QBrush(col_played_fg));
                            }
                            else
                                new_item->setIcon(1, ic_track);
                        }
                        else
                        {
                            new_item->setIcon(1, ic_track);
                            new_item->set_state(STATE_NEW);
                        }
                    }
                    else
                    {
                        if (b_wasplayed)
                        {
                            new_item->set_state(STATE_PLAYED);
                            if (config->mark_whenplayed)
                            {
                                new_item->setIcon(1, ic_trackx_played);
                                for (int i = 0; i < 6; i++)
                                    new_item->setForeground( i, QBrush(col_played_fg));
                            }
                            else
                                new_item->setIcon(1, ic_trackx);
                        }
                        else
                        {
                            new_item->setIcon(1, ic_trackx);
                            new_item->set_state(STATE_NEW);
                        }
                    }
                    new_item->set_id(iter->song_id);
                    new_item->set_type(iter->type);

                    new_item->set_time(iter->time);
                    new_item->set_uri(iter->uri);
                    addTopLevelItem(new_item);
                }
        }
    }
    newPlaylist.clear();

    set_playlist_stats(topLevelItemCount(), plist_totaltime);

    // Columns were too small with large numbers. This hack fixes it
    // by inserting an extra '0' and forcing the width.
    string_pos_width = " " + QString::number(pos) + "0 ";
    col_pos_width = get_string_width(string_pos_width);
    string_time_width = " " + into_time(plist_maxtime) + " 0";
    col_time_width = get_string_width(string_time_width);
    resize_columns();

    if (topLevelItemCount() > 0)
    {
        current_song_pos = 0;
        topLevelItem(0)->setIcon(1, ic_wait);
    }
    else
        current_song_pos = -1;

    b_waspurged = false;
}

void qm_playlistView::set_pos_time_column_width()
{
    col_pos_width = get_string_width(string_pos_width);
    col_time_width = get_string_width(string_time_width);
}

QString qm_playlistView::into_time(int time)
{
    QString formattedTime = "";
    int hrs, mins, secs;

    hrs = time / 3600;
    if (hrs > 0)
    {
        formattedTime += QString::number(hrs) + ":";
        mins = (time % 3600)/60;
        if(mins < 10)
            formattedTime += "0";
    }
    else
        mins = time / 60;

    formattedTime += QString::number(mins) + ":";

    secs = time % 60;
    if(secs < 10)
        formattedTime += "0";
    formattedTime += QString::number(secs);
    return formattedTime;
}


void qm_playlistView::select_it(int mode)
{

    QString findThis = config->select_string;

    if (findThis.isEmpty() || topLevelItemCount() == 0)
        return;

    // remove current selections
    QListIterator<QTreeWidgetItem *> itr(selectedItems());
    while (itr.hasNext())
    {
        itr.next()->setSelected(false);
    }

    int column;

    switch (mode)
    {
        case SR_ID_ARTIST:
        {
            column = 2;
            break;
        }
        case SR_ID_TITLE:
        {
            column = 3;
            break;
        }
        case SR_ID_ALBUM:
        {
            column = 5;
            break;
        }
        default: // SR_ID_ALL
            column = 0;
    }

    if (column == 0)
        for(int colm = 2; colm < 6; colm++)
        {
            QList<QTreeWidgetItem *> result = findItems( findThis, Qt::MatchContains|Qt::MatchFixedString, colm );
            QListIterator<QTreeWidgetItem *> it(result);
            while (it.hasNext())
            {
                it.next()->setSelected(true);
            }
            result.clear();
        }
    else
    {
        QList<QTreeWidgetItem *> result = findItems( findThis, Qt::MatchContains|Qt::MatchFixedString, column );
        QListIterator<QTreeWidgetItem *> it(result);
        while (it.hasNext())
        {
            it.next()->setSelected(true);
        }
        result.clear();
    }
}


void qm_playlistView::purge_it()
{
    if (topLevelItemCount() == 0 || !b_mpd_connected )
        return;

    qm_commandList newCommandList;

    QTreeWidgetItemIterator itr(this);
    while (*itr)
    {
        if ( (*itr)->get_state == STATE_PLAYED)
        {
            qm_mpd_command newCommand;
            newCommand.cmd = CMD_DEL;
            int ID = (*itr)->get_id;
            newCommand.song_id = ID;
            // higher pos first (push_front)
            newCommandList.push_front(newCommand);
        }
        ++itr;
    }

    if (newCommandList.size() > 0)
        mpd_cmd->execute_commands( newCommandList, true );

    newCommandList.clear();
}


void qm_playlistView::delete_it()
{
    if (topLevelItemCount() == 0 || !b_mpd_connected)
        return;

    qm_commandList newCommandList;

    QTreeWidgetItemIterator itr(this);
    while (*itr)
    {
        if ( (*itr)->isSelected())
        {
            qm_mpd_command newCommand;
            newCommand.cmd = CMD_DEL;
            int ID = (*itr)->get_id;
            newCommand.song_id = ID;
            // higher pos first (push_front)
            newCommandList.push_front(newCommand);
        }
        ++itr;
    }

    if (newCommandList.size() > 0)
        mpd_cmd->execute_commands( newCommandList, true );

    newCommandList.clear();
}


void qm_playlistView::clear_it()
{
    if (topLevelItemCount() == 0 || !b_mpd_connected )
        return;

    b_streamplaying = false;
    clear();
    mpd_cmd->clear_list();
    current_song_id = -1;
    current_song_pos = -1;
}


void qm_playlistView::shuffle_it()
{
    if (topLevelItemCount() < 2 || !b_mpd_connected )
        return;
    mpd_cmd->shuffle_list();
}


void qm_playlistView::save_list()
{
    if (topLevelItemCount() == 0 )
        return;

    QString suggested_name = (topLevelItem(0)->text(2)).simplified();
    bool ok;
    QString listname = QInputDialog::getText(this, tr("Save"),
                       tr("Enter a name for the playlist"), QLineEdit::Normal,
                       suggested_name, &ok);
    if ( !ok || listname.isEmpty() )
        return;

    QString result = mpd_cmd->save_playlist(listname);

    if (result.startsWith("Error:"))
    {
        result = result.section("} ",1);
        QMessageBox::warning( this, tr("Error"), result);
    }
    else
        QMessageBox::information( this, tr("Confirmation"), result);
}


void qm_playlistView::reload_playlist()
{
    if (!b_mpd_connected )
        return;
    mpd_cmd->reset_playlist();
}


void qm_playlistView::save_selected()
{
    if (topLevelItemCount() == 0 )
        return;

    QStringList tobeSaved;

    QTreeWidgetItemIterator itr(this);
    while (*itr)
    {
        if ( (*itr)->isSelected() )
        {
            tobeSaved.push_back( (*itr)->get_file );
        }
        ++itr;
    }

    if (tobeSaved.empty())
        return;

    if (tobeSaved.size() == topLevelItemCount())
    {
        save_list();
        return;
    }

    bool ok;
    QString listname = QInputDialog::getText(this, tr("Save"),
                       tr("Enter a name for the playlist"), QLineEdit::Normal,
                       "selection", &ok);
    if ( !ok || listname.isEmpty() )
        return;

    QString result = mpd_cmd->save_selection(tobeSaved, listname);

    if (result.startsWith("Error:"))
    {
        result = result.section("} ",1);
        QMessageBox::warning( this, tr("Error"), result);
    }
    else
        QMessageBox::information( this, tr("Confirmation"), result);
}


void qm_playlistView::set_connected(qm_mpdCommand *cmd, bool isconnected)
{
    mpd_cmd = cmd;
    if (mpd_cmd == nullptr)
        b_mpd_connected = false;
    else
        b_mpd_connected = isconnected;

    if (b_mpd_connected)
    {
        connect( mpd_cmd, SIGNAL(signal_playlist_update (qm_songinfo, int)), this, SLOT(on_new_playlist(qm_songinfo, int)) );

        a_reset_played->setEnabled(true);
        a_purge_played->setEnabled(true);

        if (config->mpd_save_allowed)
        {
            a_save_list->setEnabled(true);
            a_save_selection->setEnabled(true);
        }
        else
        {
            a_save_list->setEnabled(false);
            a_save_selection->setEnabled(false);
        }

        if (config->mpd_shuffle_allowed)
            a_shuffle_list->setEnabled(true);
        else
            a_shuffle_list->setEnabled(false);

        if (config->mpd_delete_allowed)
            a_delete_selection->setEnabled(true);
        else
            a_delete_selection->setEnabled(false);

        if (config->mpd_clear_allowed)
            a_clear_list->setEnabled(true);
        else
            a_clear_list->setEnabled(false);

        b_is_on_start = false;

        if (!qeued_args_list.isEmpty())
        {
            on_open_with_request(qeued_args_list);
            qeued_args_list.clear();
        }
    }
    else
    {
        clear();
        current_song_id = -1;
        current_song_pos = -1;
        a_purge_played->setEnabled(false);
        a_reset_played->setEnabled(false);
        a_clear_list->setEnabled(false);
        a_delete_selection->setEnabled(false);
        a_shuffle_list->setEnabled(false);
        a_save_list->setEnabled(false);
        a_save_selection->setEnabled(false);
    }
}


void qm_playlistView::contextMenuEvent(QContextMenuEvent *event)
{
    if (event->reason() == QContextMenuEvent::Mouse)
        context_menu->exec(QCursor::pos(), nullptr);
}


void qm_playlistView::startDrag(Qt::DropActions da)
{
    QMimeData *data;
    QModelIndexList indexes = selectedIndexes();
    if (indexes.count() > 0)
    {
        data = model()->mimeData(indexes);
        if (data == nullptr)
            return;
    }
    else
        return;
    QDrag *drag = new QDrag(this);
    drag->setPixmap(pxm_dragdrop);
    drag->setMimeData(data);
    drag->exec(da, Qt::IgnoreAction);
}


void qm_playlistView::dragMoveEvent(QDragMoveEvent *event)
{
    if (event->source() == nullptr) // drag from outside the app
    {
        if (!event->mimeData()->hasUrls() || !config->mpd_socket_conn)
        {
            event->ignore();
            return;
        }
    }


    QTreeWidgetItem *item = itemAt(event->position().toPoint());
    if (item)
    {
        dropBeforeIndex = indexOfTopLevelItem(item);
        showLine_at(visualItemRect(item).top());
    }
    else if (event->position().y() >= 0)
    {
        dropBeforeIndex = topLevelItemCount();
        showLine_at(visualItemRect(topLevelItem(topLevelItemCount() - 1)).bottom());
    }
    else
    {
        dropBeforeIndex = -1;
        line->hide();
    }
    event->accept();

}


void qm_playlistView::dragEnterEvent(QDragEnterEvent *event)
{
    if ( event->source() != nullptr ) // if drag from within application
    {
        line->show();
        event->accept();
    }
    else
    {
        if (event->mimeData()->hasUrls() && config->mpd_socket_conn)
        {
            line->show();
            event->accept();
        }
        else
            event->ignore();
    }
}


void qm_playlistView::dragLeaveEvent(QDragLeaveEvent *event)
{
    line->hide();
    dropBeforeIndex = -1;

    event->accept();
}

// External files are dropped on player
void qm_playlistView::on_open_drop_request(QDropEvent *event)
{
    // Files dragged from inside this application?
    // Should not happen: these go to dropEvent(QDropEvent *event)
    if (event->source() != nullptr)
    {
        event->ignore();
        return;
    }

    if (config->cout_extensive)
        printf("External items dropped on player\n");

    if (b_mpd_connected && event->mimeData()->hasUrls() && config->mpd_socket_conn)
    {
        int plistsize = topLevelItemCount();
        QList<QUrl> urlList = event->mimeData()->urls();
        QList<QUrl> checkedlist;
        int count = 0;

        if (urlList.size() > 0)
        {
            for (int i = 0; i < urlList.size(); ++i)
            {
                QString url = urlList.at(i).path();
                if ( file_check(url) )
                    checkedlist.push_back(urlList.at(i));
            }

            int last = checkedlist.size();
            if (last > 0)
            {
                for (int i = 0; i < checkedlist.size(); ++i)
                {
                    QString url = checkedlist.at(i).path();
                    if ( !url.startsWith("file://", Qt::CaseInsensitive) )
                    {
                        url = "file://" + url;
                    }
                    qm_mpd_command newCommand;
                    newCommand.cmd = CMD_ADD;
                    newCommand.uri = url;

                    count++;
                    if (count == last) // last one triggers a playlist update
                        mpd_cmd->execute_single_command(newCommand, true);
                    else
                        mpd_cmd->execute_single_command(newCommand, false);
                }

                scrollToItem(topLevelItem(plistsize));
            }
        }
    }

    this->setFocus();
    dropBeforeIndex = -1;
    line->hide();
    event->ignore();
}

 // called by the core
void qm_playlistView::on_open_with_request(QStringList &inlist)
{
    if (!b_mpd_connected || mpd_cmd == nullptr)
    {
        if(!b_is_on_start)
        {
            printf("Adding external files failed: not connected\n");
            return;
        }

        if (config->auto_connect)
        {
            if (config->cout_extensive)
                printf("External files are qeued until connected\n");
            qeued_args_list = inlist;
        }
        else
            printf("Adding external files failed: not connected\n");

        return;
    }

    if (!config->mpd_socket_conn)
    {
        qeued_args_list.clear();
        printf("Adding external files failed: not a socket connection\n");
        return;
    }

    int last = inlist.size();
    if (last == 0)
        return;

     bool b_newlist = false;

    QStringList checked_uri;

    QStringList::iterator iter;
    for (iter = inlist.begin(); iter != inlist.end(); ++iter)
    {
         QString uri = *iter;

        if  (uri == "-play")
        {

            b_newlist = true;
        }
        else
        {
            if  (uri.startsWith("file:///") && file_check(uri))
                checked_uri.push_back(uri);
        }
    }

    last = checked_uri.size();
    if (last == 0)
        return;

    // else shown in file_check()
    if (!config->cout_extensive)
        printf ("External files were added\n");


    if (b_newlist)
        mpd_cmd->clear_list();

    int count = 0;

    for (iter = checked_uri.begin(); iter != checked_uri.end(); ++iter)
    {
       QString url = *iter;
       qm_mpd_command newCommand;
       newCommand.cmd = CMD_ADD;
       newCommand.uri = url;
       count ++;
       if (count == last) // last one triggers a playlist update
           mpd_cmd->execute_single_command(newCommand, true);
       else
           mpd_cmd->execute_single_command(newCommand, false);
    }

    this->setFocus();

    if (b_newlist)
       mpd_cmd->play(true);
}


void qm_playlistView::dropEvent(QDropEvent *event)
{
    QList<QTreeWidgetItem*> tobeMoved;

    if (event->source() == this) // drag from inside playlist
    {
        if (config->cout_extensive)
            printf("Items dragged & dropped within playlist\n");

        QTreeWidgetItemIterator itr(this);
        while (*itr)
        {
            if ( (*itr)->isSelected() )
            {
                tobeMoved.push_back( *itr );
            }
            ++itr;
        }

         if (topLevelItemCount() == 0 || !b_mpd_connected || tobeMoved.empty() || tobeMoved.size() == topLevelItemCount())
        {
            this->setFocus();
            line->hide();
            dropBeforeIndex = -1;
            event->ignore();
            return;
        }

        qm_commandList move_out_List;
        qm_commandList move_in_List;
        int endpos = topLevelItemCount();

        QListIterator<QTreeWidgetItem *> witr(tobeMoved);
        int put_it_here = dropBeforeIndex;
        witr.toBack();
        while (witr.hasPrevious())
        {
            QTreeWidgetItem *current = witr.previous();
            if (current->get_pos <= dropBeforeIndex)
                put_it_here--;
            qm_mpd_command move_out_Command;
            move_out_Command.cmd = CMD_MOV;
            move_out_Command.song_id = current->get_id;
            move_out_Command.moveto = endpos-1;
            move_out_List.push_front(move_out_Command);
        }
        witr.toBack();
        while (witr.hasPrevious())
        {
            QTreeWidgetItem *current = witr.previous();
            qm_mpd_command move_in_Command;
            move_in_Command.cmd = CMD_MOV;
            move_in_Command.song_id = current->get_id;
            move_in_Command.moveto = put_it_here;
            move_in_List.push_back(move_in_Command);
        }
        // first move selection to the end of the list (move-out)
        // next move the items into position (move_in) and update the list
        mpd_cmd->execute_commands( move_out_List, false );
        mpd_cmd->execute_commands( move_in_List, true );
        move_out_List.clear();
        move_in_List.clear();

        scrollToItem(topLevelItem(put_it_here));
    }
    else
    if (event->source() != this && event->source() != nullptr) // drag from library
    {
        if (config->cout_extensive)
            printf("Items from library dropped on playlist\n");

        if (topLevelItemCount() == 0)
        {
            emit signal_playlist_dropreceived(-1);
        }
        else
        if (dropBeforeIndex != -1)
        {
            emit signal_playlist_dropreceived(dropBeforeIndex);
            scrollToItem(topLevelItem(dropBeforeIndex));
        }
    }
    else
    if (event->source() == nullptr) // drag from outside the app
    {
        if (config->cout_extensive)
            printf("External items dropped on playlist\n");

        if (b_mpd_connected && event->mimeData()->hasUrls() && config->mpd_socket_conn)
        {
            QList<QUrl> urlList = event->mimeData()->urls();
            QList<QUrl> checkedlist;
            int count = 0;
            int put_it_here = dropBeforeIndex;

            if (urlList.size() > 0)
            {
                // start with the last one
                for (int i = urlList.size()-1; i >=0; --i)
                {
                    QString url = urlList.at(i).path();
                    if ( file_check(url) )
                        checkedlist.push_back(urlList.at(i));
                }

                int last = checkedlist.size();
                if (last > 0)
                {
                    for (int i = 0; i < checkedlist.size(); ++i)
                    {
                        QString url = checkedlist.at(i).path();
                        if ( !url.startsWith("file://", Qt::CaseInsensitive) )
                        {
                            url = "file://" + url;
                        }
                        qm_mpd_command newCommand;
                        newCommand.cmd = CMD_INS;
                        newCommand.uri = url;
                        newCommand.moveto = put_it_here;

                        count++;
                        if (count == last) // last one triggers a playlist update
                            mpd_cmd->execute_single_command(newCommand, true);
                        else
                            mpd_cmd->execute_single_command(newCommand, false);
                    }

                    scrollToItem(topLevelItem(dropBeforeIndex));
                }
            }
        }
    }

    this->setFocus();
    dropBeforeIndex = -1;
    line->hide();
    event->ignore();
}

// Check Dragged-&-Dropped file extension
bool qm_playlistView::file_check(const QString &url)
{
    int i = url.lastIndexOf( '/' ) + 1;
    QString fname = url.right(url.length() - i);

    // check if file exists
    QFile file;
    QString urlfix;
    // remove "file://"
    if (url.startsWith("file://"))
    {
        urlfix = url.right(url.length() - 7);
    }
    else
    {
        urlfix = url;
    }

    file.setFileName(urlfix);
    if ( !file.exists() )
    {
        if (config->cout_extensive)
            printf ("External file was not found: %s\n", urlfix.toUtf8().constData());

        return false;
    }

    if  (   url.endsWith(".mp3",  Qt::CaseInsensitive) ||
            url.endsWith(".ogg",  Qt::CaseInsensitive) ||
            url.endsWith(".mpc",  Qt::CaseInsensitive) ||
            url.endsWith(".flac", Qt::CaseInsensitive) ||
            url.endsWith(".ape",  Qt::CaseInsensitive) ||
            url.endsWith(".mp4",  Qt::CaseInsensitive) ||
            url.endsWith(".m4a",  Qt::CaseInsensitive) ||
            url.endsWith(".oga",  Qt::CaseInsensitive) ||
            url.endsWith(".mp2",  Qt::CaseInsensitive) ||
            url.endsWith(".aif",  Qt::CaseInsensitive) ||
            url.endsWith(".aiff", Qt::CaseInsensitive) ||
            url.endsWith(".au",   Qt::CaseInsensitive) ||
            url.endsWith(".wav",  Qt::CaseInsensitive) ||
            url.endsWith(".wv",   Qt::CaseInsensitive) ||
            url.endsWith(".aac",  Qt::CaseInsensitive) ||
            url.endsWith(".mod",  Qt::CaseInsensitive) ||
            url.endsWith(".mov",  Qt::CaseInsensitive) ||
            url.endsWith(".mkv",  Qt::CaseInsensitive) ||
            url.endsWith(".mpg",  Qt::CaseInsensitive) ||
            url.endsWith(".mpeg", Qt::CaseInsensitive) ||
            url.endsWith(".ac3",  Qt::CaseInsensitive) ||
            url.endsWith(".asf",  Qt::CaseInsensitive) ||
            url.endsWith(".avi",  Qt::CaseInsensitive) )
        {
        if (config->cout_extensive)
            printf ("External file added: %s\n", fname.toUtf8().constData());

        return true;
    }
    else
    {
        if (config->cout_extensive)
            printf ("External file skipped: %s\n", fname.toUtf8().constData());

        return false;
    }
}


void qm_playlistView::showLine_at(int y)
{
    line->setGeometry(2, y, viewport()->width()-4, 2);
}


void qm_playlistView::resize_columns()
{
    if (config->playlist_autocols)
    {
        setColumnWidth(0, col_pos_width);   // position (using sizeHint does not fix wrong size)
        setColumnWidth(1,24);               // icon
        setColumnWidth(4, col_time_width);  // playtime (using sizeHint does not fix wrong size)
        int hint2 = sizeHintForColumn(2);   // artist
        int hint3 = sizeHintForColumn(3);   // title
        int hint5 = sizeHintForColumn(5);   // album

        int w_available = viewport()->width() - (columnWidth(0) + columnWidth(1) + columnWidth(4));

        int maxsize;

        if ( (hint5 <  w_available/3) && (hint3 < w_available/3))
            maxsize = w_available - (hint5 + hint3);
        else
        if (hint5 <  w_available/3)
            maxsize = (w_available - hint5)/2;
        else
            if (hint3 <  w_available/3)
            maxsize = (w_available - hint3)/2;
        else
            maxsize = w_available/3;

        if (hint2 > maxsize)
        {
            setColumnWidth(2, maxsize);
            w_available -= maxsize;
        }
        else
        {
            setColumnWidth(2, hint2);
            w_available -= hint2;
        }

        if (hint5 <  w_available/2)
            maxsize = (w_available - hint5);
        else
            maxsize = w_available/2;

        if (hint3 > maxsize)
        {
            setColumnWidth(3, maxsize);
            w_available -= maxsize;

        }
        else
        {
            setColumnWidth(3, hint3);
            w_available -= hint3;

        }

        setColumnWidth(5, w_available);
    }
}


void qm_playlistView::set_auto_columns()
{
    // we use setStretchLastSection(true) for col 5

    if (config->playlist_autocols)
    {
        hvw_playlist->setSectionResizeMode(0, QHeaderView::Fixed);
        hvw_playlist->setSectionResizeMode(1, QHeaderView::Fixed);
        hvw_playlist->setSectionResizeMode(2, QHeaderView::Fixed);
        hvw_playlist->setSectionResizeMode(3, QHeaderView::Fixed);
        hvw_playlist->setSectionResizeMode(4, QHeaderView::Fixed);
        resize_columns();
    }
    else
    {
        hvw_playlist->setSectionResizeMode(0, QHeaderView::Fixed);
        hvw_playlist->setSectionResizeMode(1, QHeaderView::Fixed);
        hvw_playlist->setSectionResizeMode(2, QHeaderView::Interactive);
        hvw_playlist->setSectionResizeMode(3, QHeaderView::Interactive);
        hvw_playlist->setSectionResizeMode(4, QHeaderView::Fixed);
    }
}


void qm_playlistView::set_themed_icons(bool dark_theme)
{
    if (!dark_theme)
    {
        ic_a_delete_selection = QIcon(":/br_delete");
        ic_a_purge_played = QIcon(":/br_purge");
        ic_a_reset_played = QIcon(":/br_played_reset");
        ic_a_shuffle_list = QIcon(":/br_shuffle");
        ic_a_save_list = QIcon(":/br_saveplist");
        ic_a_save_selection = QIcon(":/br_saveplistselect");
        ic_track= QIcon(":/br_track");
        ic_trackx = QIcon(":/br_trackx");
        ic_stream = QIcon(":/br_stream");
        ic_track_played= QIcon(":/br_track_played");
        ic_trackx_played = QIcon(":/br_trackx_played");
        ic_stream_played = QIcon(":/br_stream_played");
        ic_wait = QIcon(":/br_wait");
        ic_stop = QIcon(":/br_stop");
        ic_play = QIcon(":/br_play");
        ic_pause = QIcon(":/br_pause");
        pxm_dragdrop = QPixmap(":/br_dragdrop");
        col_playing_fg = QColor("#003070");
     }
    else
    {
        ic_a_delete_selection = QIcon(":/br_delete.alt");
        ic_a_purge_played = QIcon(":/br_purge.alt");
        ic_a_reset_played = QIcon(":/br_played_reset.alt");
        ic_a_shuffle_list = QIcon(":/br_shuffle.alt");
        ic_a_save_list = QIcon(":/br_saveplist.alt");
        ic_a_save_selection = QIcon(":/br_saveplistselect.alt");
        ic_track= QIcon(":/br_track.alt");
        ic_trackx = QIcon(":/br_trackx.alt");
        ic_stream = QIcon(":/br_stream.alt");
        ic_track_played= QIcon(":/br_track_played.alt");
        ic_trackx_played = QIcon(":/br_trackx_played.alt");
        ic_stream_played = QIcon(":/br_stream_played.alt");
        pxm_dragdrop = QPixmap(":/br_dragdrop.alt");
        col_playing_fg = QColor("#80a1cf");
    }
    // not themed
    ic_a_clear_list = QIcon(":/br_reddelete");
    ic_wait = QIcon(":/br_wait");
    ic_stop = QIcon(":/br_stop");
    ic_play = QIcon(":/br_play");
    ic_pause = QIcon(":/br_pause");
}


int qm_playlistView::get_string_width(QString str)
{
    QLabel lb_render;
    lb_render.setFont(hvw_playlist->font());
    lb_render.setText(str);
    int width = (lb_render.minimumSizeHint()).width();
    return width;
}


void qm_playlistView::resizeEvent(QResizeEvent *event)
{
    // act every 6 pixels
    resize_cols_count++;
    if (resize_cols_count >= 6)
    {
        resize_cols_count = 0;
        resize_columns();
    }

    event->accept();
}


void qm_playlistView::update_stream_title(int pos, QString title)
{
    if (title.isEmpty())
            title = tr("no info");
    QTreeWidgetItem *item = topLevelItem(pos);
    item->setText(3, title);
}


qm_playlistView::~qm_playlistView()
{}
