/*
 * Cantata
 *
 * Copyright (c) 2011-2014 Craig Drummond <craig.p.drummond@gmail.com>
 *
 */
/*
 * Copyright (c) 2008 Sander Knopper (sander AT knopper DOT tk) and
 *                    Roeland Douma (roeland AT rullzer DOT com)
 *
 * This file is part of QtMPC.
 *
 * QtMPC 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.
 *
 * QtMPC 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 QtMPC.  If not, see <http://www.gnu.org/licenses/>.
 */

#include "musiclibraryitemalbum.h"
#include "musiclibraryitemartist.h"
#include "musiclibraryitemsong.h"
#include "musiclibraryitemroot.h"
#include "musiclibrarymodel.h"
#include "roles.h"
#include "support/localize.h"
#include "support/utils.h"
#include "widgets/icons.h"
#include "gui/settings.h"
#include "gui/plurals.h"
#include <QFile>
#include <QTimer>
#include <QStringRef>
#include <QDateTime>
#include <QDir>
#include <QMimeData>
#include <QStringList>
#if defined ENABLE_MODEL_TEST
#include "modeltest.h"
#endif

MusicLibraryModel::MusicLibraryModel(QObject *parent)
    : ActionModel(parent)
    , rootItem(new MusicLibraryItemRoot)
{
    rootItem->setModel(this);
    #if defined ENABLE_MODEL_TEST
    new ModelTest(this, this);
    #endif
}

MusicLibraryModel::~MusicLibraryModel()
{
    delete rootItem;
}

QModelIndex MusicLibraryModel::index(int row, int column, const QModelIndex &parent) const
{
    if (!hasIndex(row, column, parent)) {
        return QModelIndex();
    }

    const MusicLibraryItem * parentItem;

    if (!parent.isValid()) {
        parentItem = rootItem;
    } else {
        parentItem = static_cast<MusicLibraryItem *>(parent.internalPointer());
    }

    MusicLibraryItem * const childItem = parentItem->childItem(row);
    if (childItem) {
        return createIndex(row, column, childItem);
    }

    return QModelIndex();
}

QModelIndex MusicLibraryModel::parent(const QModelIndex &index) const
{
    if (!index.isValid()) {
        return QModelIndex();
    }

    const MusicLibraryItem * const childItem = static_cast<MusicLibraryItem *>(index.internalPointer());
    MusicLibraryItem * const parentItem = childItem->parentItem();

    if (parentItem == rootItem) {
        return QModelIndex();
    }

    return createIndex(parentItem->row(), 0, parentItem);
}

int MusicLibraryModel::rowCount(const QModelIndex &parent) const
{
    if (parent.column() > 0) {
        return 0;
    }

    const MusicLibraryItem *parentItem;

    if (!parent.isValid()) {
        parentItem = rootItem;
    } else {
        parentItem = static_cast<MusicLibraryItem *>(parent.internalPointer());
    }

    return parentItem->childCount();
}

const MusicLibraryItemRoot * MusicLibraryModel::root(const MusicLibraryItem *item) const
{
    if (MusicLibraryItem::Type_Root==item->itemType()) {
        return static_cast<const MusicLibraryItemRoot *>(item);
    }

    if (MusicLibraryItem::Type_Artist==item->itemType()) {
        return static_cast<const MusicLibraryItemRoot *>(item->parentItem());
    }

    if (MusicLibraryItem::Type_Album==item->itemType()) {
        return static_cast<const MusicLibraryItemRoot *>(item->parentItem()->parentItem());
    }

    if (MusicLibraryItem::Type_Song==item->itemType()) {
        return root(item->parentItem());
    }

    return 0;
}

static QString parentData(const MusicLibraryItem *i)
{
    QString data;
    const MusicLibraryItem *itm=i;

    while (itm->parentItem()) {
        if (!itm->parentItem()->data().isEmpty()) {
            if (MusicLibraryItem::Type_Root==itm->parentItem()->itemType()) {
                data="<b>"+itm->parentItem()->data()+"</b><br/>"+data;
            } else {
                data=itm->parentItem()->data()+"<br/>"+data;
            }
        }
        itm=itm->parentItem();
    }

    return data;
}

QVariant MusicLibraryModel::data(const QModelIndex &index, int role) const
{
    if (!index.isValid()) {
        return QVariant();
    }

    if (Qt::CheckStateRole==role) {
        return static_cast<MusicLibraryItem *>(index.internalPointer())->checkState();
    }

    MusicLibraryItem *item = static_cast<MusicLibraryItem *>(index.internalPointer());

    switch (role) {
    #ifndef ENABLE_UBUNTU
    case Qt::DecorationRole:
        switch (item->itemType()) {
        case MusicLibraryItem::Type_Root:
            return static_cast<MusicLibraryItemRoot *>(item)->icon();
        case MusicLibraryItem::Type_Artist:
            return Icons::self()->artistIcon;
        case MusicLibraryItem::Type_Album:
            return Icons::self()->albumIcon(32, true);
        case MusicLibraryItem::Type_Song:
            return Song::Playlist==static_cast<MusicLibraryItemSong *>(item)->song().type ? Icons::self()->playlistListIcon : Icons::self()->audioListIcon;
        default:
            return QVariant();
        }
    #endif
    case Cantata::Role_BriefMainText:
        if (MusicLibraryItem::Type_Album==item->itemType()) {
            return item->data();
        }
    case Cantata::Role_MainText:
    case Qt::DisplayRole:
        if (MusicLibraryItem::Type_Song==item->itemType()) {
            MusicLibraryItemSong *song = static_cast<MusicLibraryItemSong *>(item);
            if (Song::Playlist==song->song().type) {
                return song->song().isCueFile() ? i18n("Cue Sheet") : i18n("Playlist");
            }
            if (MusicLibraryItem::Type_Root==song->parentItem()->itemType()) {
                return song->song().trackAndTitleStr();
            }
            return song->song().trackAndTitleStr();
        }
        return item->displayData();
    case Qt::ToolTipRole:
        if (!Settings::self()->infoTooltips()) {
            return QVariant();
        }
        if (MusicLibraryItem::Type_Song==item->itemType()) {
            return static_cast<MusicLibraryItemSong *>(item)->song().toolTip();
        }

        return parentData(item)+
                (0==item->childCount()
                    ? item->displayData(true)
                    : (item->displayData(true)+"<br/>"+data(index, Cantata::Role_SubText).toString()));
    case Cantata::Role_SubText:
        switch (item->itemType()) {
        case MusicLibraryItem::Type_Root: {
            MusicLibraryItemRoot *collection=static_cast<MusicLibraryItemRoot *>(item);

            if (collection->flat()) {
                return Plurals::tracks(item->childCount());
            }
            return Plurals::artists(item->childCount());
        }
        case MusicLibraryItem::Type_Artist:
            return Plurals::albums(item->childCount());
        case MusicLibraryItem::Type_Song:
            return Utils::formatTime(static_cast<MusicLibraryItemSong *>(item)->time(), true);
        case MusicLibraryItem::Type_Album:
            return Plurals::tracksWithDuration(static_cast<MusicLibraryItemAlbum *>(item)->trackCount(),
                                               Utils::formatTime(static_cast<MusicLibraryItemAlbum *>(item)->totalTime()));
        default: return QVariant();
        }
    case Cantata::Role_ListImage:
        switch (item->itemType()) {
        case MusicLibraryItem::Type_Album:
            return true;
        default:
            return false;
        }
    #ifdef ENABLE_UBUNTU
    case Cantata::Role_Image:
        switch (item->itemType()) {
        case MusicLibraryItem::Type_Album:
            return static_cast<MusicLibraryItemAlbum *>(item)->cover();
        case MusicLibraryItem::Type_Artist:
            return static_cast<MusicLibraryItemArtist *>(item)->cover();
        default:
            return QString();
        }
    #endif
    case Cantata::Role_CoverSong: {
        QVariant v;
        switch (item->itemType()) {
        case MusicLibraryItem::Type_Album:
            v.setValue<Song>(static_cast<MusicLibraryItemAlbum *>(item)->coverSong());
            break;
        case MusicLibraryItem::Type_Artist:
            v.setValue<Song>(static_cast<MusicLibraryItemArtist *>(item)->coverSong());
            break;
        default:
            break;
        }
        return v;
    }
    case Cantata::Role_TitleText:
        if (MusicLibraryItem::Type_Album==item->itemType()) {
            return i18nc("Album by Artist", "%1 by %2", item->data(), item->parentItem()->data());
        }
        return item->data();
    default:
        break;
    }
    return ActionModel::data(index, role);
}

bool MusicLibraryModel::setData(const QModelIndex &idx, const QVariant &value, int role)
{
    if (Qt::CheckStateRole==role) {
        if (!idx.isValid()) {
            return false;
        }

        MusicLibraryItem *item = static_cast<MusicLibraryItem *>(idx.internalPointer());
        Qt::CheckState check=value.toBool() ? Qt::Checked : Qt::Unchecked;

        if (item->checkState()==check) {
            return false;
        }

        switch (item->itemType()) {
        case MusicLibraryItem::Type_Artist: {
            MusicLibraryItemArtist *artistItem=static_cast<MusicLibraryItemArtist *>(item);
            QModelIndex artistIndex=index(artistItem->row(), 0, QModelIndex());
            item->setCheckState(check);
            foreach (MusicLibraryItem *album, artistItem->childItems()) {
                if (check!=album->checkState()) {
                    MusicLibraryItemAlbum *albumItem=static_cast<MusicLibraryItemAlbum *>(album);
                    QModelIndex albumIndex=index(albumItem->row(), 0, artistIndex);
                    album->setCheckState(check);
                    foreach (MusicLibraryItem *song, albumItem->childItems()) {
                        song->setCheckState(check);
                    }
                    emit dataChanged(index(0, 0, albumIndex), index(0, albumItem->childCount(), albumIndex));
                }
                emit dataChanged(index(0, 0, artistIndex), index(0, artistItem->childCount(), artistIndex));
            }
            emit dataChanged(idx, idx);
            break;
        }
        case MusicLibraryItem::Type_Album: {
            MusicLibraryItemArtist *artistItem=static_cast<MusicLibraryItemArtist *>(item->parentItem());
            MusicLibraryItemAlbum *albumItem=static_cast<MusicLibraryItemAlbum *>(item);
            QModelIndex artistIndex=index(artistItem->row(), 0, QModelIndex());
            item->setCheckState(check);
            foreach (MusicLibraryItem *song, albumItem->childItems()) {
                song->setCheckState(check);
            }
            setParentState(artistIndex);
            emit dataChanged(idx, idx);
            break;
        }
        case MusicLibraryItem::Type_Song: {
            item->setCheckState(check);
            MusicLibraryItemAlbum *albumItem=static_cast<MusicLibraryItemAlbum *>(item->parentItem());
            MusicLibraryItemArtist *artistItem=static_cast<MusicLibraryItemArtist *>(albumItem->parentItem());
            QModelIndex artistIndex=index(artistItem->row(), 0, QModelIndex());
            QModelIndex albumIndex=index(albumItem->row(), 0, artistIndex);
            setParentState(albumIndex);
            setParentState(artistIndex);
            emit dataChanged(idx, idx);
            break;
        }
        case MusicLibraryItem::Type_Root:
            return false;
        }

        return true;
    }
    return ActionModel::setData(idx, value, role);
}

void MusicLibraryModel::setParentState(const QModelIndex &parent)
{
    MusicLibraryItemContainer *parentItem=static_cast<MusicLibraryItemContainer *>(parent.internalPointer());
    Qt::CheckState parentCheck=parentItem->checkState();
    bool haveCheckedChildren=false;
    bool haveUncheckedChildren=false;
    bool stop=false;

    foreach (MusicLibraryItem *child, parentItem->childItems()) {
        switch (child->checkState()) {
        case Qt::PartiallyChecked:
            parentCheck=Qt::PartiallyChecked;
            stop=true;
            break;
        case Qt::Unchecked:
            haveUncheckedChildren=true;
            parentCheck=haveCheckedChildren ? Qt::PartiallyChecked : Qt::Unchecked;
            stop=haveCheckedChildren && haveUncheckedChildren;
            break;
        case Qt::Checked:
            haveCheckedChildren=true;
            parentCheck=haveUncheckedChildren ? Qt::PartiallyChecked : Qt::Checked;
            stop=haveCheckedChildren && haveUncheckedChildren;
            break;
        }
        if (stop) {
            break;
        }
    }

    if (parentItem->checkState()!=parentCheck) {
        parentItem->setCheckState(parentCheck);
        emit dataChanged(parent, parent);
    }
}

void MusicLibraryModel::clear()
{
    beginResetModel();
    rootItem->clear();
    endResetModel();
}

void MusicLibraryModel::setSongs(const QSet<Song> &songs)
{
    rootItem->update(songs);
//    foreach (MusicLibraryItem *artist, rootItem->childItems()) {
//        MusicLibraryItemArtist *artistItem=static_cast<MusicLibraryItemArtist *>(artist);
//        artistItem->setCheckState(Qt::Checked);

//        foreach (MusicLibraryItem *album, artistItem->childItems()) {
//            MusicLibraryItemAlbum *albumItem=static_cast<MusicLibraryItemAlbum *>(album);
//            albumItem->setCheckState(Qt::Checked);
//            foreach (MusicLibraryItem *song, albumItem->childItems()) {
//                song->setCheckState(Qt::Checked);
//            }
//        }
//    }
}

Qt::ItemFlags MusicLibraryModel::flags(const QModelIndex &index) const
{
    if (index.isValid()) {
        return Qt::ItemIsSelectable | Qt::ItemIsEnabled | Qt::ItemIsUserCheckable;
    }
    return Qt::ItemIsDropEnabled;
}
