/*
 * Cantata
 *
 * Copyright (c) 2011-2014 Craig Drummond <craig.p.drummond@gmail.com>
 *
 * ----
 *
 * 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; see the file COPYING.  If not, write to
 * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
 * Boston, MA 02110-1301, USA.
 */

#include "musiclibraryitemalbum.h"
#include "musiclibraryitemartist.h"
#include "musiclibraryitemsong.h"
#include "musiclibraryitemroot.h"
#include "musiclibraryitempodcast.h"
#include "multimusicmodel.h"
#include <QStringList>

MultiMusicModel::MultiMusicModel(QObject *parent)
    : MusicModel(parent)
{
}

MultiMusicModel::~MultiMusicModel()
{
    qDeleteAll(collections);
}

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

    if (parent.isValid()) {
        MusicLibraryItem *p=static_cast<MusicLibraryItem *>(parent.internalPointer());

        if (p) {
            return row<p->childCount() ? createIndex(row, column, p->childItem(row)) : QModelIndex();
        }
    } else {
        return row<collections.count() ? createIndex(row, column, collections.at(row)) : QModelIndex();
    }

    return QModelIndex();
}

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

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

    if (parentItem) {
        return createIndex(parentItem->parentItem() ? parentItem->row() : row(parentItem), 0, parentItem);
    } else {
        return QModelIndex();
    }
}

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

    return parent.isValid() ? static_cast<MusicLibraryItem *>(parent.internalPointer())->childCount() : collections.count();
}

void MultiMusicModel::updateGenres()
{
    QSet<QString> newGenres;
    foreach (MusicLibraryItemRoot *col, collections) {
        newGenres+=col->genres();
    }
    if (newGenres!=colGenres) {
        colGenres=newGenres;
        emit updateGenres(colGenres);
    }
}

void MultiMusicModel::toggleGrouping()
{
    beginResetModel();
    foreach (MusicLibraryItemRoot *col, collections) {
        col->toggleGrouping();
    }
    endResetModel();
}

void MultiMusicModel::getDetails(QSet<QString> &artists, QSet<QString> &albumArtists, QSet<QString> &composers, QSet<QString> &albums, QSet<QString> &genres)
{
    foreach (MusicLibraryItemRoot *col, collections) {
        col->getDetails(artists, albumArtists, composers, albums, genres);
    }
}

int MultiMusicModel::indexOf(const QString &id)
{
    int i=0;
    foreach (MusicLibraryItemRoot *col, collections) {
        if (col->id()==id) {
            return i;
        }
        i++;
    }
    return -1;
}

QList<Song> MultiMusicModel::songs(const QModelIndexList &indexes, bool playableOnly, bool fullPath) const
{
    QMap<MusicLibraryItem *, QList<Song> > colSongs;

    foreach(QModelIndex index, indexes) {
        MusicLibraryItem *item = static_cast<MusicLibraryItem *>(index.internalPointer());
        MusicLibraryItem *p=item;

        while (p->parentItem()) {
            p=p->parentItem();
        }

        if (!p) {
            continue;
        }

        MusicLibraryItemRoot *parent=static_cast<MusicLibraryItemRoot *>(p);

        if (playableOnly && !parent->canPlaySongs()) {
            continue;
        }

        switch (item->itemType()) {
        case MusicLibraryItem::Type_Root: {
            if (static_cast<MusicLibraryItemRoot *>(parent)->flat()) {
                foreach (const MusicLibraryItem *song, static_cast<const MusicLibraryItemContainer *>(item)->childItems()) {
                    if (MusicLibraryItem::Type_Song==song->itemType() && !colSongs[parent].contains(static_cast<const MusicLibraryItemSong*>(song)->song())) {
                        colSongs[parent] << parent->fixPath(static_cast<const MusicLibraryItemSong*>(song)->song(), fullPath);
                    }
                }
            } else {
                // First, sort all artists as they would appear in UI...
                QList<MusicLibraryItem *> artists=static_cast<const MusicLibraryItemContainer *>(item)->childItems();
                if (artists.isEmpty()) {
                    break;
                }

                bool isPodcasts=MusicLibraryItem::Type_Podcast==(*artists.begin())->itemType();
                if (isPodcasts) {
                    qSort(artists.begin(), artists.end(), MusicLibraryItemPodcast::lessThan);
                } else {
                    qSort(artists.begin(), artists.end(), MusicLibraryItemArtist::lessThan);
                }
                foreach (MusicLibraryItem *a, artists) {
                    if (isPodcasts) {
                        const MusicLibraryItemContainer *podcast=static_cast<const MusicLibraryItemContainer *>(a);
                        foreach (const MusicLibraryItem *song, static_cast<const MusicLibraryItemContainer *>(podcast)->childItems()) {
                            if (MusicLibraryItem::Type_Song==song->itemType() && !colSongs[parent].contains(static_cast<const MusicLibraryItemSong*>(song)->song())) {
                                colSongs[parent] << parent->fixPath(static_cast<const MusicLibraryItemSong*>(song)->song(), fullPath);
                            }
                        }
                    } else {
                        const MusicLibraryItemContainer *artist=static_cast<const MusicLibraryItemContainer *>(a);
                        // Now sort all albums as they would appear in UI...
                        QList<MusicLibraryItem *> artistAlbums=artist->childItems();
                        qSort(artistAlbums.begin(), artistAlbums.end(), MusicLibraryItemAlbum::lessThan);
                        foreach (MusicLibraryItem *i, artistAlbums) {
                            const MusicLibraryItemContainer *album=static_cast<const MusicLibraryItemContainer *>(i);
                            foreach (const MusicLibraryItem *song, album->childItems()) {
                                if (MusicLibraryItem::Type_Song==song->itemType() && !colSongs[parent].contains(static_cast<const MusicLibraryItemSong*>(song)->song())) {
                                    colSongs[parent] << parent->fixPath(static_cast<const MusicLibraryItemSong*>(song)->song(), fullPath);
                                }
                            }
                        }
                    }
                }
            }
            break;
        }
        case MusicLibraryItem::Type_Artist: {
            // First, sort all albums as they would appear in UI...
            QList<MusicLibraryItem *> artistAlbums=static_cast<const MusicLibraryItemContainer *>(item)->childItems();
            qSort(artistAlbums.begin(), artistAlbums.end(), MusicLibraryItemAlbum::lessThan);

            foreach (MusicLibraryItem *i, artistAlbums) {
                const MusicLibraryItemContainer *album=static_cast<const MusicLibraryItemContainer *>(i);
                foreach (const MusicLibraryItem *song, album->childItems()) {
                    if (MusicLibraryItem::Type_Song==song->itemType() && !colSongs[parent].contains(static_cast<const MusicLibraryItemSong*>(song)->song())) {
                        colSongs[parent] << parent->fixPath(static_cast<const MusicLibraryItemSong*>(song)->song(), fullPath);
                    }
                }
            }
            break;
        }
        case MusicLibraryItem::Type_Album:
            foreach (const MusicLibraryItem *song, static_cast<const MusicLibraryItemContainer *>(item)->childItems()) {
                if (MusicLibraryItem::Type_Song==song->itemType() && !colSongs[parent].contains(static_cast<const MusicLibraryItemSong*>(song)->song())) {
                    colSongs[parent] << parent->fixPath(static_cast<const MusicLibraryItemSong*>(song)->song(), fullPath);
                }
            }
            break;
        case MusicLibraryItem::Type_Song:
            if (!colSongs[parent].contains(static_cast<const MusicLibraryItemSong*>(item)->song())) {
                colSongs[parent] << parent->fixPath(static_cast<const MusicLibraryItemSong*>(item)->song(), fullPath);
            }
            break;
        case MusicLibraryItem::Type_Podcast:
            foreach (const MusicLibraryItem *song, static_cast<const MusicLibraryItemContainer *>(item)->childItems()) {
                if (MusicLibraryItem::Type_Song==song->itemType() && !colSongs[parent].contains(static_cast<const MusicLibraryItemSong*>(song)->song())) {
                    colSongs[parent] << parent->fixPath(static_cast<const MusicLibraryItemSong*>(song)->song(), fullPath);
                }
            }
            break;
        default:
            break;
        }
    }

    QList<Song> songs;
    QMap<MusicLibraryItem *, QList<Song> >::Iterator it(colSongs.begin());
    QMap<MusicLibraryItem *, QList<Song> >::Iterator end(colSongs.end());

    for (; it!=end; ++it) {
        songs.append(it.value());
    }

    return songs;
}

QStringList MultiMusicModel::filenames(const QModelIndexList &indexes, bool playableOnly, bool fullPath) const
{
    QList<Song> songList=songs(indexes, playableOnly, fullPath);
    QStringList fnames;
    foreach (const Song &s, songList) {
        fnames.append(s.file);
    }
    return fnames;
}

void MultiMusicModel::clearImages()
{
    beginResetModel();
    foreach (MusicLibraryItemRoot *c, collections) {
        c->clearImages();
    }
    endResetModel();
}
