#include "musiclibrarytreeprivate.h"
#include "musiclibrary.h"

#define TYPE_INVALID    0
#define TYPE_ARTIST     1
#define TYPE_RECORD     2
#define TYPE_SONG       3

MusicLibraryTreePrivate::MusicLibraryTreePrivate(MusicLibrary *l) : library(l)
{
    // In this case there's direct mapping from music library objects to view,
    // so the constructor doesn't need to do anything
    // Compare with implementation in musiclibrarystdtreeprivate.cpp, which
    // builds a separate tree of QStandardItem objects

    connect(library, SIGNAL(objectChanged(MusicObject*)), SLOT(objectChanged(MusicObject*)));
}

QModelIndex MusicLibraryTreePrivate::index(int row, int column, const QModelIndex &parent) const
{
    // Standard item model has invisible root item,
    // generic item model has invalid parent to represent that
    if (!parent.isValid()) {
        // Library has artists, so get one by index
        return createIndex(row, column, library->artists()[row]);
    } else {
        MusicObject *obj = static_cast<MusicObject *>(parent.internalPointer());
        switch (objType(obj)) {
        case TYPE_ARTIST:
            // Artist has records
            return createIndex(row, column, static_cast<Artist *>(obj)->records()[row]);
        case TYPE_RECORD:
            // Record has songs
            return createIndex(row, column, static_cast<Record *>(obj)->songs()[row]);
        case TYPE_SONG:
            // Song doesn't have children
            break;
        }
    }
    return QModelIndex();
}

QModelIndex MusicLibraryTreePrivate::parent(const QModelIndex &child) const
{
    if (child.isValid()) {
        MusicObject *obj = static_cast<MusicObject *>(child.internalPointer());
        switch (objType(obj)) {
        case TYPE_ARTIST:
            // Artist doesn't have a parent
            return QModelIndex();
        case TYPE_RECORD:
            // Record has an artist parent and the index of that artist is needed
            return artistToIndex(static_cast<Record *>(child.internalPointer())->artist());
        case TYPE_SONG:
            // Song has record parent and it's index under the artist is needed
            return recordToIndex(static_cast<Song *>(child.internalPointer())->record());
        }
    }
    return QModelIndex();
}

int MusicLibraryTreePrivate::rowCount(const QModelIndex &parent) const
{
    if (!parent.isValid()) {
        // Library has artists
        return library->artists().size();
    } else {
        MusicObject *obj = static_cast<MusicObject *>(parent.internalPointer());
        switch (objType(obj)) {
        case TYPE_ARTIST:
            // Artist has records
            return static_cast<Artist *>(obj)->records().size();
        case TYPE_RECORD:
            // Record has songs
            return static_cast<Record *>(obj)->songs().size();
        case TYPE_SONG:
            // Song doesn't have children
            break;
        }
    }
    return 0;
}

int MusicLibraryTreePrivate::columnCount(const QModelIndex &) const
{
    // Single-column tree
    return 1;
}

QVariant MusicLibraryTreePrivate::data(const QModelIndex &index, int role) const
{
    // Display and user roles are supported
    if (role != Qt::DisplayRole && role != Qt::UserRole) {
        return QVariant();
    }

    // Data can be accessed via user role
    MusicObject *obj = static_cast<MusicObject *>(index.internalPointer());
    if (role == Qt::UserRole) {
        return qVariantFromValue<QObject *>(obj);
    }

    // Display role is shown in the view
    switch (objType(obj)) {
    case TYPE_ARTIST:
        return obj->name() + " (" + static_cast<Artist *>(obj)->homePage().toString() + ")";
    case TYPE_RECORD:
        return obj->name() + " (" + static_cast<Record *>(obj)->releaseDate().toString("ddd MMM d yyyy") + ")";
    case TYPE_SONG:
        return QString::number(static_cast<Song *>(obj)->number()) + " " + obj->name();
    }
    return QVariant();
}

void MusicLibraryTreePrivate::objectChanged(MusicObject *obj)
{
    // When model element changes, it is mapped to QModelIndex
    // and a notification about data change within that index
    // is made. This causes all views that are using this model
    // to update themselves
    QModelIndex modelIndex;
    switch (objType(obj)) {
    case TYPE_ARTIST:
        modelIndex = artistToIndex(static_cast<Artist *>(obj));
        emit dataChanged(modelIndex, modelIndex);
        break;
    case TYPE_RECORD:
        modelIndex = recordToIndex(static_cast<Record *>(obj));
        emit dataChanged(modelIndex, modelIndex);
        break;
    case TYPE_SONG:
        modelIndex = songToIndex(static_cast<Song *>(obj));
        emit dataChanged(modelIndex, modelIndex);
        break;
    }
}

QModelIndex MusicLibraryTreePrivate::artistToIndex(Artist *artist) const
{
    int listIndex = library->artists().indexOf(artist);
    if (listIndex >= 0) {
        return createIndex(listIndex, 0, artist);
    }
    return QModelIndex();
}

QModelIndex MusicLibraryTreePrivate::recordToIndex(Record *record) const
{
    int listIndex = record->artist()->records().indexOf(record);
    if (listIndex >= 0) {
        return createIndex(listIndex, 0, record);
    }
    return QModelIndex();
}

QModelIndex MusicLibraryTreePrivate::songToIndex(Song *song) const
{
    int listIndex = song->record()->songs().indexOf(song);
    if (listIndex >= 0) {
        return createIndex(listIndex, 0, song);
    }
    return QModelIndex();
}

int MusicLibraryTreePrivate::objType(MusicObject *obj) const
{
    Artist *a = qobject_cast<Artist *>(obj);
    if (a) {
        return TYPE_ARTIST;
    } else {
        Record *r = qobject_cast<Record *>(obj);
        if (r) {
            return TYPE_RECORD;
        } else {
            Song *s = qobject_cast<Song *>(obj);
            if (s) {
                return TYPE_SONG;
            }
        }
    }
    return TYPE_INVALID;
}
