/****************************************************************************
**
** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies).
** Contact: http://www.qt-project.org/legal
**
** This file is part of the Qt Mobility Components.
**
** $QT_BEGIN_LICENSE:LGPL$
** Commercial License Usage
** Licensees holding valid commercial Qt licenses may use this file in
** accordance with the commercial license agreement provided with the
** Software or, alternatively, in accordance with the terms contained in
** a written agreement between you and Digia.  For licensing terms and
** conditions see http://qt.digia.com/licensing.  For further information
** use the contact form at http://qt.digia.com/contact-us.
**
** GNU Lesser General Public License Usage
** Alternatively, this file may be used under the terms of the GNU Lesser
** General Public License version 2.1 as published by the Free Software
** Foundation and appearing in the file LICENSE.LGPL included in the
** packaging of this file.  Please review the following information to
** ensure the GNU Lesser General Public License version 2.1 requirements
** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
**
** In addition, as a special exception, Digia gives you certain additional
** rights.  These rights are described in the Digia Qt LGPL Exception
** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
**
** GNU General Public License Usage
** Alternatively, this file may be used under the terms of the GNU
** General Public License version 3.0 as published by the Free Software
** Foundation and appearing in the file LICENSE.GPL included in the
** packaging of this file.  Please review the following information to
** ensure the GNU General Public License version 3.0 requirements will be
** met: http://www.gnu.org/copyleft/gpl.html.
**
**
** $QT_END_LICENSE$
**
****************************************************************************/

#include "qwmpmetadata.h"

#include "qwmpevents.h"
#include "qwmpglobal.h"

#include <qmediacontent.h>

#include <QtCore/qdatetime.h>
#include <QtCore/qdir.h>
#include <QtCore/qfileinfo.h>
#include <QtCore/qsize.h>
#include <QtCore/qstringlist.h>
#include <QtCore/qurl.h>
#include <QtCore/qvariant.h>


struct QWmpMetaDataKeyLookup
{
    QtMultimediaKit::MetaData key;
    const wchar_t *token;
};

static const QWmpMetaDataKeyLookup qt_wmpMetaDataKeys[] =
{
    { QtMultimediaKit::Title, L"Title" },
    { QtMultimediaKit::SubTitle, L"WM/SubTitle" },
    { QtMultimediaKit::Author, L"Author" },
    { QtMultimediaKit::Comment, L"Comment" },
    { QtMultimediaKit::Description, L"Description" },
    { QtMultimediaKit::Category, L"WM/Category" },
    { QtMultimediaKit::Genre, L"WM/Genre" },
    //{ QtMultimediaKit::Date, 0 },
    { QtMultimediaKit::Year, L"WM/Year" },
    { QtMultimediaKit::UserRating, L"UserRating" },
    //{ QtMultimediaKit::MetaDatawords, 0 },
    { QtMultimediaKit::Language, L"Language" },
    { QtMultimediaKit::Publisher, L"WM/Publisher" },
    { QtMultimediaKit::Copyright, L"Copyright" },
    { QtMultimediaKit::ParentalRating, L"ParentalRating" },
    { QtMultimediaKit::RatingOrganisation, L"RatingOrganisation" },

    // Media
    { QtMultimediaKit::Size, L"FileSize" },
    { QtMultimediaKit::MediaType, L"MediaType" },
    { QtMultimediaKit::Duration, L"Duration" },

    // Audio
    { QtMultimediaKit::AudioBitRate, L"AudioBitrate" },
    { QtMultimediaKit::AudioCodec, L"AudioCodec" },
    { QtMultimediaKit::ChannelCount, L"Channels" },
    { QtMultimediaKit::SampleRate, L"Frequency" },

    // Music
    { QtMultimediaKit::AlbumTitle, L"WM/AlbumTitle" },
    { QtMultimediaKit::AlbumArtist, L"WM/AlbumArtist" },
    { QtMultimediaKit::ContributingArtist, L"Author" },
    { QtMultimediaKit::Composer, L"WM/Composer" },
    { QtMultimediaKit::Conductor, L"WM/Conductor" },
    { QtMultimediaKit::Lyrics, L"WM/Lyrics" },
    { QtMultimediaKit::Mood, L"WM/Mood" },
    { QtMultimediaKit::TrackNumber, L"WM/TrackNumber" },
    //{ QtMultimediaKit::TrackCount, 0 },
    //{ QtMultimediaKit::CoverArtUrlSmall, 0 },
    //{ QtMultimediaKit::CoverArtUrlLarge, 0 },

    // Image/Video
    //{ QtMultimediaKit::Resolution, 0 },
    //{ QtMultimediaKit::PixelAspectRatio, 0 },

    // Video
    //{ QtMultimediaKit::FrameRate, 0 },
    { QtMultimediaKit::VideoBitRate, L"VideoBitRate" },
    { QtMultimediaKit::VideoCodec, L"VideoCodec" },

    //{ QtMultimediaKit::PosterUrl, 0 },

    // Movie
    { QtMultimediaKit::ChapterNumber, L"ChapterNumber" },
    { QtMultimediaKit::Director, L"WM/Director" },
    { QtMultimediaKit::LeadPerformer, L"LeadPerformer" },
    { QtMultimediaKit::Writer, L"WM/Writer" },

    // Photos
    { QtMultimediaKit::CameraManufacturer, L"CameraManufacturer" },
    { QtMultimediaKit::CameraModel, L"CameraModel" },
    { QtMultimediaKit::Event, L"Event" },
    { QtMultimediaKit::Subject, L"Subject" }
};

QWmpMetaData::QWmpMetaData(IWMPCore3 *player, QWmpEvents *events, QObject *parent)
    : QMetaDataReaderControl(parent)
    , m_media(0)
{
    player->get_currentMedia(&m_media);

    connect(events, SIGNAL(CurrentItemChange(IDispatch*)),
            this, SLOT(currentItemChangeEvent(IDispatch*)));
    connect(events, SIGNAL(MediaChange(IDispatch*)), this, SLOT(mediaChangeEvent(IDispatch*)));
}

QWmpMetaData::~QWmpMetaData()
{
    if (m_media)
        m_media->Release();
}

bool QWmpMetaData::isMetaDataAvailable() const
{
    return m_media != 0;
}

bool QWmpMetaData::isWritable() const
{
    return m_media != 0;
}

QVariant QWmpMetaData::metaData(QtMultimediaKit::MetaData key) const
{
    static const int  count = sizeof(qt_wmpMetaDataKeys) / sizeof(QWmpMetaDataKeyLookup);

    switch (key) {
    case QtMultimediaKit::Date:
        {
            QVariant day = value(m_media, QAutoBStr(L"ReleaseDateDay"));
            QVariant month = value(m_media, QAutoBStr(L"ReleaseDateMonth"));
            QVariant year = value(m_media, QAutoBStr(L"ReleaseDateYear"));

            if (!day.isNull() && !month.isNull() && !year.isNull())
                return QDate(year.toInt(), month.toInt(), day.toInt());
        }
        break;
    case QtMultimediaKit::CoverArtUrlSmall:
        return albumArtUrl(m_media, "_Small.jpg");
    case QtMultimediaKit::CoverArtUrlLarge:
        return albumArtUrl(m_media, "_Large.jpg");
    case QtMultimediaKit::Resolution:
        {
            QVariant width = value(m_media, QAutoBStr(L"WM/VideoWidth"));
            QVariant height = value(m_media, QAutoBStr(L"WM/VideoHeight"));

            if (!width.isNull() && !height.isNull())
                return QSize(width.toInt(), height.toInt());
        }
        break;
    case QtMultimediaKit::PixelAspectRatio:
        {
            QVariant x = value(m_media, QAutoBStr(L"PixelAspectRatioX"));
            QVariant y = value(m_media, QAutoBStr(L"PixelAspectRatioY"));

            if (!x.isNull() && !y.isNull())
                return QSize(x.toInt(), y.toInt());
        }
        break;
    case QtMultimediaKit::VideoFrameRate:
        break;
    default:
        for (int i = 0; i < count; ++i) {
            if (qt_wmpMetaDataKeys[i].key == key)
                return value(m_media, QAutoBStr(qt_wmpMetaDataKeys[i].token));
        }
        break;
    }
    return QVariant();
}

QList<QtMultimediaKit::MetaData> QWmpMetaData::availableMetaData() const
{
    QList<QtMultimediaKit::MetaData> keys;

    if (m_media) {
        // WMP will return a list of all possible keys so there's no point in filtering the keys
        // in the lookup table.
        static const int  count = sizeof(qt_wmpMetaDataKeys) / sizeof(QWmpMetaDataKeyLookup);
        for (int i = 0; i < count; ++i)
            keys.append(qt_wmpMetaDataKeys[i].key);

        BSTR string = 0;
        if (m_media->get_sourceURL(&string) == S_OK) {
            QString url = QString::fromWCharArray(static_cast<const wchar_t *>(string));
            ::SysFreeString(string);

            if (m_media->getItemInfo(QAutoBStr(L"WM/WMCollectionGroupID"), &string) == S_OK) {
                QString uuid = QString::fromWCharArray(static_cast<const wchar_t *>(string));
                ::SysFreeString(string);

                QString albumArtLarge = QLatin1String("AlbumArt_") + uuid + QLatin1String("_Large.jpg");
                QString albumArtSmall = QLatin1String("AlbumArt_") + uuid + QLatin1String("_Small.jpg");

                QDir dir = QFileInfo(url).absoluteDir();

                if (dir.exists(albumArtLarge))
                    keys.append(QtMultimediaKit::CoverArtUrlLarge);
                if (dir.exists(albumArtSmall))
                    keys.append(QtMultimediaKit::CoverArtUrlSmall);
            }
        }
    }
    return keys;
}

QVariant QWmpMetaData::extendedMetaData(const QString &key) const
{
    return value(m_media, QAutoBStr(key));
}

QStringList QWmpMetaData::availableExtendedMetaData() const
{
    return keys(m_media);
}

void QWmpMetaData::currentItemChangeEvent(IDispatch *dispatch)
{
    IWMPMedia *media = m_media;

    m_media = 0;
    if (dispatch)
        dispatch->QueryInterface(__uuidof(IWMPMedia), reinterpret_cast<void **>(&m_media));

    if (media) {
        if (m_media)
            emit metaDataChanged();
        else
            emit metaDataAvailableChanged(false);

        media->Release();
    } else {
        if (m_media)
            emit metaDataAvailableChanged(false);
    }
}

void QWmpMetaData::mediaChangeEvent(IDispatch *dispatch)
{
    IWMPMedia *media = 0;
    if (dispatch &&  dispatch->QueryInterface(
            __uuidof(IWMPMedia), reinterpret_cast<void **>(&media)) == S_OK) {
        VARIANT_BOOL isEqual = VARIANT_FALSE;
        if (media->get_isIdentical(m_media, &isEqual) == S_OK && isEqual)
            emit metaDataChanged();
        media->Release();
    }
}


QStringList QWmpMetaData::keys(IWMPMedia *media)
{
    QStringList keys;

    long count = 0;
    if (media && media->get_attributeCount(&count) == S_OK) {
        for (long i = 0; i < count; ++i) {
            BSTR string;
            if (media->getAttributeName(i, &string) == S_OK) {
                keys.append(QString::fromWCharArray(string, ::SysStringLen(string)));

                ::SysFreeString(string);
            }
        }
    }
    return keys;
}

QVariant QWmpMetaData::value(IWMPMedia *media, BSTR key)
{
    QVariantList values;
    IWMPMedia3 *media3 = 0;
    if (media && media->QueryInterface(
            __uuidof(IWMPMedia3), reinterpret_cast<void **>(&media3)) == S_OK) {
        long count = 0;
        media3->getAttributeCountByType(key, 0, &count);

        // The count appears to only be valid for static properties, dynamic properties like
        // PlaylistIndex will have a count of zero but return a value for index 0.
        if (count == 0)
            count = 1;

        for (long i = 0; i < count; ++i) {
            VARIANT var;
            VariantInit(&var);

            if (media3->getItemInfoByType(key, 0, i, &var) == S_OK) {
                QVariant value = convertVariant(var);

                if (!value.isNull())
                    values.append(value);

                VariantClear(&var);
            }
        }
        media3->Release();
    }

    switch (values.count()) {
    case 0:
        return QVariant();
    case 1:
        return values.first();
    default:
        return values;
    }
}

QMediaContent QWmpMetaData::resources(IWMPMedia *media)
{
    QMediaContent content;

    BSTR string = 0;
    if (media->get_sourceURL(&string) == S_OK) {
        QString url = QString::fromWCharArray(static_cast<const wchar_t *>(string));
        ::SysFreeString(string);

        content = QMediaContent(QUrl(url));
    }

    return content;
}

QVariant QWmpMetaData::convertVariant(const VARIANT &variant)
{
    switch (variant.vt) {
    case VT_I2:
        return variant.iVal;
    case VT_I4:
        return variant.lVal;
    case VT_I8:
        return variant.llVal;
    case VT_UI2:
        return variant.uiVal;
    case VT_UI4:
        return quint32(variant.ulVal);
    case VT_UI8:
        return variant.ullVal;
    case VT_INT:
        return variant.intVal;
    case VT_UINT:
        return variant.uintVal;
    case VT_BSTR:
        return QString::fromWCharArray(variant.bstrVal, ::SysStringLen(variant.bstrVal));
    case VT_DISPATCH:
        {
            IWMPMetadataPicture *picture = 0;
            IWMPMetadataText *text = 0;

            if (variant.pdispVal->QueryInterface(
                    __uuidof(IWMPMetadataPicture), reinterpret_cast<void **>(&picture)) == S_OK) {
                QUrl url;
                BSTR string;
                if (picture->get_URL(&string) == S_OK) {
                    url = QUrl(QString::fromWCharArray(string, ::SysStringLen(string)));

                    ::SysFreeString(string);
                }
                picture->Release();
                return qVariantFromValue(url);
            } else if (variant.pdispVal->QueryInterface(
                    __uuidof(IWMPMetadataText), reinterpret_cast<void **>(&text)) == S_OK) {
                QString description;
                BSTR string;
                if (text->get_description(&string) == S_OK) {
                    description = QString::fromWCharArray(string, SysStringLen(string));

                    ::SysFreeString(string);
                }
                text->Release();
                return description;
            } else {
                qWarning("Unknown dispatch type");
            }
        }
        break;
    default:
        qWarning("Unsupported Type %d %x", variant.vt, variant.vt);
        break;
    }

    return QVariant();
}

QVariant QWmpMetaData::albumArtUrl(IWMPMedia *media, const char *suffix)
{
    BSTR string = 0;
    if (media && media->get_sourceURL(&string) == S_OK) {
        QString url = QString::fromWCharArray(static_cast<const wchar_t *>(string));
        ::SysFreeString(string);

        if (media->getItemInfo(QAutoBStr(L"WM/WMCollectionGroupID"), &string) == S_OK) {
            QString uuid = QString::fromWCharArray(static_cast<const wchar_t *>(string));
            ::SysFreeString(string);

            QString fileName = QLatin1String("AlbumArt_") + uuid + QLatin1String(suffix);

            QDir dir = QFileInfo(url).absoluteDir();

            if (dir.exists(fileName)) {
                return qVariantFromValue(
                        QUrl(QLatin1String("file:///") + dir.absoluteFilePath(fileName)));
            }
        }
    }
    return QVariant();
}
