/*  This file is part of the KDE project
    Copyright (C) 2006 Matthias Kretz <kretz@kde.org>
    Copyright (C) 2006-2007 Tim Beaulen <tbscope@gmail.com>

    This library is free software; you can redistribute it and/or
    modify it under the terms of the GNU Library General Public
    License version 2 as published by the Free Software Foundation.

    This library 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
    Library General Public License for more details.

    You should have received a copy of the GNU Library General Public License
    along with this library; see the file COPYING.LIB.  If not, write to
    the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
    Boston, MA 02110-1301, USA.
*/

#include "playbin.h"

#include "qbtgstreamer/qbtgstreamerelementfactory.h"
#include "qbtgstreamer/qbtgstreamertypefindfactory.h"
#include "qbtgstreamer/qbtgstreamerclock.h"

#include <QCoreApplication>
#include <QtGlobal>

#include <phonon/audiodeviceenumerator.h>
#include <phonon/audiodevice.h>

#include <solid/devicemanager.h>
#include <solid/device.h>
#include <solid/audiohw.h>

#include <kconfiggroup.h>
#include <kurl.h>
#include <kdebug.h>
#include <klocale.h>

namespace Phonon
{
namespace GStreamer
{
#if 0
findFeature::findFeature(const QString &feature)
{
    makeDefault();

    m_feature = feature;

    kDebug(611) << "Feature finder: looking for " << m_feature << endl;
    kDebug(611) << "---------------" << endl;
}

findFeature::~findFeature()
{
}

bool findFeature::featureFilter(QbtGStreamerPluginFeature *feature, QbtGStreamerDataPointer *data)
{
    Q_UNUSED(data)

    QbtGStreamerElementFactory *f = new QbtGStreamerElementFactory;
    if (f->fromPluginFeature(feature)) { 
        if (f->klass().contains(m_feature, Qt::CaseInsensitive)) {
            kDebug(611) << "Feature: " << feature->name() << " --- description: " << f->description() << endl;
            return true;
        }
    }
    return false;
}
#endif

mimeTypeRegistry::mimeTypeRegistry()
{
    makeDefault();       
}

mimeTypeRegistry::~mimeTypeRegistry()
{
}

bool mimeTypeRegistry::featureFilter(QbtGStreamerPluginFeature *feature, QbtGStreamerDataPointer *data)
{
    Q_UNUSED(data)

    uint rank;
    if (feature->isTypeFindFactory()) {
        //kDebug(611) << "###### " << feature->name() << endl;
        return true;
    }

    rank = feature->rank();
    if (rank < GST_RANK_MARGINAL) {
        //kDebug(611) << "Rank is lower than marginal" << endl;
        return false;
    }
    return false;
}

PlayBin::PlayBin()
{
    QbtGStreamerElementFactory *factory = new QbtGStreamerElementFactory;
    m_play = factory->makeElement("playbin", "play");

    m_bus = m_play->bus();
    m_bus->messageWatch(true);

    connect(m_bus, SIGNAL(message(QbtGStreamerMessage *, QbtGStreamerDataPointer *)),
            this, SLOT(slotMessage(QbtGStreamerMessage *, QbtGStreamerDataPointer *)));
}

PlayBin::~PlayBin()
{
}

void PlayBin::setUrl(const KUrl &url)
{
    m_play->setProperty("uri", url.url().toLocal8Bit().data());
}

void PlayBin::play()
{
    m_play->setState(QbtGStreamerStatePlaying);

    if (m_play->indexable())
        kDebug(611) << "playbin is indexable" << endl;
    else
        kDebug(611) << "playbin is not indexable" << endl;

    kDebug(611) << "duration = " << m_play->duration() << endl;
}

void PlayBin::pause()
{
    m_play->setState(QbtGStreamerStatePaused);
    //kDebug(611) << "Base time = " << m_play->baseTime() << endl;
}

void PlayBin::stop()
{
    m_play->setState(QbtGStreamerStateNull);
    kDebug(611) << "duration = " << m_play->duration() << endl;
}

void PlayBin::setVolume(float volume)
{
    Q_UNUSED(volume)
}

float PlayBin::volume()
{
    return m_volume;
}

qint64 PlayBin::duration()
{
    return m_play->duration();
}

void PlayBin::seek(qint64 pos)
{
    m_play->seekSimple(pos);
}

QStringList PlayBin::supportedMimeTypes()
{
    // TODO: How to solve the problem that GStreamer doesn't give a list of supported mimetypes?

    /* Quoting an irc discussion to be able to remember it:

[13:12] <tbscope> What would be the best way to get a list of all supported mimetypes? Currently I'm using the default registry, get all typefind factorys and use the name of the plugin feature for the mimetype. But I have a feeling that this is not the best way, or even correct.
[13:13] <bilboed> tbscope, get all pad templates of all plugins
[13:13] <MikeS> tbscope: that's completely wrong. The form of the name in that case is merely a convention, and anyway, gstreamer doesn't use mime types at all
[13:13] <tbscope> bilboed: thanks, I'll look into that.
[13:14] <MikeS> tbscope: the name part of caps (which you can see in the pad template) looks a bit like a mime type, but it isn't, and you mustn't treat it as a mime-type.
[13:14] <bilboed> tbscope, yeah, take the full caps of the padtemplate, not just the name
[13:14] <bilboed> damn, I had a script to do that some time back
[13:14] <tbscope> MikeS: So there's no way to get a list of supported mimetypes from gstreamer?
[13:15] <MikeS> tbscope: correct.
[13:15] <MikeS> GStreamer supports many things that don't even have mime types.
[13:16] <bilboed> beverage/x-coffee,coffeetype=arabica
[13:16] <tbscope> No problem, then I'll have to manually construct a list by checking which codecs are installed
[13:19] <MikeS> that probably makes even less sense. Very few of the codecs have mime-types defined.
[13:20] <MikeS> Why do you think that a list of mime-types is what you want, by the way? I've heard other people ask for this before, and it's usually turned out that they wanted to solve some other problem entirely.

    */
    /*mimeTypeRegistry *reg = new mimeTypeRegistry;
    QList<QbtGStreamerPluginFeature *> myList = reg->featureList();*/

    QStringList mimeTypes;
    /*foreach (QbtGStreamerPluginFeature *f, myList) {
        kDebug(611) << "Found mimetype:" << f->name() << endl;
        mimeTypes << f->name();
    }*/

    return mimeTypes;
}

QStringList PlayBin::supportedFileExtensions()
{
    QStringList extensions;

    QList<QbtGStreamerTypeFindFactory *> myList = QbtGStreamerTypeFindFactory::list();

    foreach (QbtGStreamerTypeFindFactory *f, myList) {
        if (!f->extensions().isEmpty()) {
            extensions << f->extensions();
        }
    }

    kDebug(611) << "The backend supports the following file extensions:" << endl << extensions.join(", ") << endl;
    return extensions;
}

#if 0
QStringList PlayBin::supportedAudioOutputDevices()
    {
        QbtGStreamerElementFactory *factory = new QbtGStreamerElementFactory;
        QbtGStreamerElement *alsamixer = factory->makeElement("alsamixer","alsa");
        alsamixer->setProperty("device", "hw:0");
        alsamixer->setState(GST_STATE_PAUSED);

        QbtGStreamerMixer *mixer = new QbtGStreamerMixer;
        mixer->setGstMixer(GST_MIXER(alsamixer->gstElement()));

        QStringList outputDevices;
        foreach(QbtGStreamerMixerTrack *t, mixer->tracks()) {
            qDebug() << "Found a track. label =" << t->label() << " (flags =" << t->flags() << ")";
            if(t->flags() && GST_MIXER_TRACK_OUTPUT)
                outputDevices << t->label();
        }

        
        return outputDevices;
    }
#endif
#if 0
void PlayBin::slotMessage(QbtGStreamerMessage *msg, QbtGStreamerDataPointer *data)
{
    Q_UNUSED(data)

    if (msg->type() == QbtGStreamerMessageError) {
        QString debugstr;
        QbtGStreamerError *err = msg->error(debugstr);

        switch (err->domain()) {
        case QbtGStreamerErrorCore:
            kWarning(611) << "Core error:" << endl;
            break;
        case QbtGStreamerErrorLibrary:
            kWarning(611) << "Library error:" << endl;
            break;
        case QbtGStreamerErrorResource:
            switch (err->code()) {
            case QbtGStreamerErrorResourceFailed:
                kWarning(611) << "Resource error: General error" << endl;
                break;
            case QbtGStreamerErrorResourceTooLazy:
                kWarning(611) << "Resource error: Lazy developer error" << endl;
                break;
            case QbtGStreamerErrorResourceNotFound:
                kWarning(611) << "Resource error: Resource could not be found" << endl;
                break;
            case QbtGStreamerErrorResourceBusy:
                kWarning(611) << "Resource error: Resource is busy" << endl;
                break;
            case QbtGStreamerErrorResourceOpenRead:
                kWarning(611) << "Resource error: Can not open resource for reading" << endl;
                break;
            case QbtGStreamerErrorResourceOpenWrite:
                kWarning(611) << "Resource error: Can not open resource for writing" << endl;
                break;
            case QbtGStreamerErrorResourceOpenReadWrite:
                kWarning(611) << "Resource error: Can not open resource for reading or writing" << endl;
                break;
            case QbtGStreamerErrorResourceClose:
                kWarning(611) << "Resource error: Can not close the resource" << endl;
                break;
            case QbtGStreamerErrorResourceRead:
                kWarning(611) << "Resource error: Can not read from the resource" << endl;
                break;
            case QbtGStreamerErrorResourceWrite:
                kWarning(611) << "Resource error: Can not write to the resource" << endl;
                break;
            case QbtGStreamerErrorResourceSeek:
                kWarning(611) << "Resource error: Can not seek the resource" << endl;
                break;
            case QbtGStreamerErrorResourceSync:
                kWarning(611) << "Resource error: Can not sync the resource" << endl;
                break;
            case QbtGStreamerErrorResourceSettings:
                kWarning(611) << "Resource error: Can not manipulate the settings" << endl;
                break;
            case QbtGStreamerErrorResourceNoSpaceLeft:
                kWarning(611) << "Resource error: No space left on the resource" << endl;
                break;
            /*case QbtGStreamerErrorResourceNumErrors:
                kWarning(611) << "Resource error:" << endl;
                break;*/
            }
        case QbtGStreamerErrorStream:
            kWarning(611) << "Stream error:" << endl;
            break;
        case QbtGStreamerErrorSystem:
            kWarning(611) << "System error:" << endl;
            break;
        }
    } else if (msg->type() == QbtGStreamerMessageStateChanged) {
        GstState oldState; 
        GstState newState;
        GstState pendingState;
        msg->stateChanged(&oldState, &newState, &pendingState);

        QString strOld;
        QString strNew;
        QString strPen;

        if (oldState == GST_STATE_VOID_PENDING)
            strOld = "VOID PENDING";
        else if (oldState == GST_STATE_NULL)
            strOld = "NULL"; 
        else if (oldState == GST_STATE_READY)
            strOld = "READY";
        else if (oldState == GST_STATE_PAUSED)
            strOld = "PAUSED";
        else if (oldState == GST_STATE_PLAYING)
            strOld = "PLAYING";
    
        if (newState == GST_STATE_VOID_PENDING)
            strNew = "VOID PENDING";
        else if (newState == GST_STATE_NULL)
            strNew = "NULL"; 
        else if (newState == GST_STATE_READY)
            strNew = "READY";
        else if (newState == GST_STATE_PAUSED)
            strNew = "PAUSED";
        else if (newState == GST_STATE_PLAYING)
            strNew = "PLAYING";

        if (pendingState == GST_STATE_VOID_PENDING)
            strPen = "VOID PENDING";
        else if (pendingState == GST_STATE_NULL)
            strPen = "NULL"; 
        else if (pendingState == GST_STATE_READY)
            strPen = "READY";
        else if (pendingState == GST_STATE_PAUSED)
            strPen = "PAUSED";
        else if (pendingState == GST_STATE_PLAYING)
            strPen = "PLAYING";

        kDebug(611) << "State changed from " << strOld << " to " << strNew << ". Pending state: " << strPen << endl;
    } else {
        kDebug(611) << "Message of type: " << msg->typeName(msg->type()) << endl;
    }
}
#endif
/*void PlayBin::setConfig(const KSharedConfigPtr &cfg)
{
    m_config = cfg; 
    kDebug(611) << "config set!" << endl; 
}
*/
/*QSet<int> PlayBin::audioOutputIndexes()
{
    checkAudioOutputs();
    QSet<int> set;

    for (int i = 0; i < m_audioOutputInfos.size(); ++i) {
        //if (that->m_audioOutputInfos[i].available) {
        set << m_audioOutputInfos[i].index;
        //}
    }

    return set;
}
*/
#if 0
void PlayBin::addAudioOutput(AudioDevice dev, QString driver)
{
    kDebug(611) << k_funcinfo << endl;

    QString postfix;

    if (dev.driver() == Solid::AudioInterface::Alsa) {
        postfix = QLatin1String(" (ALSA)");
    } else if (dev.driver() == Solid::AudioInterface::OpenSoundSystem) {
        postfix = QLatin1String(" (OSS)");
    }

    AudioOutputInfo info(dev.index(), dev.cardName() + postfix,
            QString(), dev.iconName(), driver, dev.deviceIds());

    info.available = dev.isAvailable();
    m_audioOutputInfos << info;
}

void PlayBin::addAudioOutput(int index, const QString &name, const QString &description, const QString &icon, const QString &driver, const QStringList &deviceIds)
{
    AudioOutputInfo info(index, name, description, icon, driver, deviceIds);
    const int listIndex = m_audioOutputInfos.indexOf(info);

    if (listIndex == -1) {
        info.available = true;
        m_audioOutputInfos << info;
        KConfigGroup config(m_config, QLatin1String("AudioOutputDevice_") + QString::number(index));
        config.writeEntry("name", name);
        config.writeEntry("description", description);
        config.writeEntry("driver", driver);
        config.writeEntry("icon", icon);
    } else {
        m_audioOutputInfos[listIndex].devices = deviceIds;
        m_audioOutputInfos[listIndex].available = true;
    }
}

#endif
#if 0
QString PlayBin::audioOutputName(int audioDevice)
{
    checkAudioOutputs();

    for (int i = 0; i < m_audioOutputInfos.size(); ++i) {
        if (m_audioOutputInfos[i].index == audioDevice) {
            return m_audioOutputInfos[i].name;
        }
    }

    return QString();
}

QString PlayBin::audioOutputDescription(int audioDevice)
{
    checkAudioOutputs();

    for (int i = 0; i < m_audioOutputInfos.size(); ++i) {
        if (m_audioOutputInfos[i].index == audioDevice) {
            return m_audioOutputInfos[i].description;
        }
    }

    return QString();
}

QString PlayBin::audioOutputIcon(int audioDevice)
{
    checkAudioOutputs();

    for (int i = 0; i < m_audioOutputInfos.size(); ++i) {
        if (m_audioOutputInfos[i].index == audioDevice) {
            return m_audioOutputInfos[i].icon;
        }
    }

    return QString();
}

bool PlayBin::audioOutputAvailable(int audioDevice)
{
    checkAudioOutputs();

    for (int i = 0; i < m_audioOutputInfos.size(); ++i) {
        if (m_audioOutputInfos[i].index == audioDevice) {
            return m_audioOutputInfos[i].available;
        }
    }

    return false;

}
#endif

#if 0
void PlayBin::checkAudioOutputs()
{
    kDebug(611) << k_funcinfo << endl;
    
    if (m_audioOutputInfos.isEmpty()) {
        kDebug(611) << "isEmpty" << endl;

        QStringList groups = m_config->groupList();

        int nextIndex = 10000;

        foreach (QString group, groups) {
            if (group.startsWith("AudioOutputDevice")) {
                const int index = group.right(group.size() - 18).toInt();
                if (index >= nextIndex) {
                    nextIndex = index + 1;
                }
                KConfigGroup config(m_config, group);
                m_audioOutputInfos << AudioOutputInfo(index,
                    config.readEntry("name", QString()),
                    config.readEntry("description", QString()),
                    config.readEntry("icon", QString()),
                    config.readEntry("driver", QString()),
                    QStringList()); // the device list can change and needs to be queried
                                    // from the actual hardware configuration
            }
        }

        findFeature *reg = new findFeature("sink/audio");
        QList<QbtGStreamerPluginFeature *> myList = reg->featureList();

        foreach (QbtGStreamerPluginFeature *f, myList) {
            if (f->name() == "alsasink") {
                kDebug(611) << "Found alsa output element: " << f->name() << endl;

                QList<AudioDevice> alsaDevices = AudioDeviceEnumerator::availablePlaybackDevices();
                foreach (AudioDevice dev, alsaDevices) {
                    kDebug(611) << "Found audio device!" << endl;
                    if (dev.driver() == Solid::AudioInterface::Alsa) {
                        addAudioOutput(dev, QLatin1String("alsa"));
                    }
                }
            }
            if (f->name() == "osssink") {
                kDebug(611) << "Found oss output element: " << f->name() << endl;
            }
            if (f->name() == "pulsesink") {
                kDebug(611) << "Found PulseAudio sink element" << endl;
                addAudioOutput(nextIndex++, i18n("PulseAudio"),
                            i18n("<p>TODO: add pulseaudio info!</p>"),
                            f->name(), f->name(), QStringList());
            }
        }

        // now m_audioOutputInfos holds all devices this computer has ever seen
        foreach (AudioOutputInfo info, m_audioOutputInfos) {
            kDebug(611) << "--- " << info.index << info.name << info.driver << info.devices << endl;
        }
    }
}
#endif

}}

#include "playbin.moc"
