#include "musiclibrarystdtreeprivate.h"
#include "musiclibrary.h"

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

MusicLibraryStdTreePrivate::MusicLibraryStdTreePrivate(MusicLibrary *l) : library(l)
{
    // Get the model root item
    QStandardItem *root = invisibleRootItem();

    // Add each artist to the model root
    foreach (Artist *artist, library->artists()) {

        // A standard item is created for each artist and the
        // displayed text is built with objToString function
        QStandardItem *aitem = new QStandardItem(objToString(artist));

        // The standard item is linked with the artist object,
        // so it can be quickly found when the artist data changes.
        // Since the artist class doesn't provide an API for linking
        // with model data, this uses the generic QObject property API.
        // A QStandardItem doesn't inherit from QObject, so a void
        // pointer needs to be used to store it.
        artist->setProperty("modelitem", qVariantFromValue<void *>(aitem));

        // Reverse linking is also done, since the UI expects to find
        // the Artist object from the model by using UserRole property.
        aitem->setData(qVariantFromValue<QObject *>(artist), Qt::UserRole);

        // Add each record of an artist
        foreach (Record *record, artist->records()) {

            // Similar as with artists, create standard item and link with record
            QStandardItem *ritem = new QStandardItem(objToString(record));
            record->setProperty("modelitem", qVariantFromValue<void *>(ritem));
            ritem->setData(qVariantFromValue<QObject *>(record), Qt::UserRole);

            // Add each song from a record
            foreach (Song *song, record->songs()) {

                // Similar as with songs and records
                QStandardItem *sitem = new QStandardItem(objToString(song));
                song->setProperty("modelitem", qVariantFromValue<void *>(sitem));
                sitem->setData(qVariantFromValue<QObject *>(song), Qt::UserRole);

                // Add to item model
                ritem->appendRow(sitem);
            }

            // Add to item model
            aitem->appendRow(ritem);
        }

        // Add to item model
        root->appendRow(aitem);
    }

    // Observe for changes in the model
    connect(library, SIGNAL(objectChanged(MusicObject*)), SLOT(objectChanged(MusicObject*)));
}

void MusicLibraryStdTreePrivate::objectChanged(MusicObject *obj)
{
    // When a model is changed, fetch the standard item
    // from the properties of the model object and
    // update it's DisplayRole property. This will
    // update all views the model is used in
    QStandardItem *item = static_cast<QStandardItem *>(
            qVariantValue<void *>(obj->property("modelitem")));
    item->setData(objToString(obj), Qt::DisplayRole);
}

QString MusicLibraryStdTreePrivate::objToString(MusicObject *obj) const
{
    // 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 "";
}

int MusicLibraryStdTreePrivate::objType(MusicObject *obj) const
{
    // Using qobject_cast to map an object to sub-class
    // A better option would be to add a type() function
    // into the MusicObject base class and return correct
    // one from there
    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;
}
