/*  This file is part of the KDE project
    Copyright (C) 2007 Matthias Kretz <kretz@kde.org>

    This program is free software; you can redistribute it and/or
    modify it under the terms of the GNU Library General Public
    License as published by the Free Software Foundation; either
    version 2 of the License, or (at your option) version 3.

    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 "backend.h"
#include "mediaobject.h"
#include "effect.h"
#include "audiooutput.h"
//#include "audiodataoutput.h"
//#include "visualization.h"
#include "volumefadereffect.h"
//#include "videodataoutput.h"
#include "videowidget.h"
#include "comm.h"

#include <kdebug.h>
#include <klocale.h>
#include <kpluginfactory.h>
#include <kpluginloader.h>

#include <QtCore/QByteArray>
#include <QtCore/QProcess>
#include <QtCore/QCoreApplication>
#include <QtCore/QSet>
#include <QtCore/QVariant>
#include <QtGui/QApplication>

K_PLUGIN_FACTORY(ProxyBackendFactory, registerPlugin<Phonon::Proxy::Backend>();)
K_EXPORT_PLUGIN(ProxyBackendFactory("proxybackend"))

namespace Phonon
{
namespace Proxy
{

Backend *Backend::s_instance = 0;

Backend::Backend(QObject *parent, const QVariantList &)
    : QObject(parent),
    m_backendProcess(0),
    m_lastFunction(NoFunction)
{
    Q_ASSERT(0 == s_instance);
    s_instance = this;
    // find backend to load
    /*setProperty("identifier",     QLatin1String("phonon_xine"));
    setProperty("backendName",    QLatin1String("Proxy"));
    setProperty("backendComment", i18n("Phonon Proxy Backend"));
    setProperty("backendVersion", QLatin1String("0.1"));
    setProperty("backendIcon",    QLatin1String("phonon-xine"));
    setProperty("backendWebsite", QLatin1String("http://multimedia.kde.org/"));*/
}

bool Backend::event(QEvent *e)
{
    if (e->type() == QEvent::DynamicPropertyChange) {
        QDynamicPropertyChangeEvent *ev = static_cast<QDynamicPropertyChangeEvent *>(e);
        if (ev->propertyName() == "_k_realBackend") {
            if (m_backendProcess) {
                send(BackendShutdown);
                if (!m_backendProcess->waitForFinished()) {
                    m_backendProcess->kill();
                }
                delete m_backendProcess;
            }
            m_backendProcess = new QProcess(this);
            connect(m_backendProcess, SIGNAL(readyRead()), SLOT(readyRead()));
            m_backendProcess->start(QLatin1String("phononproxybackend"), QStringList() << property("_k_realBackend").toString());
            m_backendProcess->closeReadChannel(QProcess::StandardError);
            if (!m_backendProcess->waitForStarted()) {
                qFatal("phononproxybackend did not start");
            }
            m_stream.setDevice(m_backendProcess);

            // now get the real Backend object in the other process
            //createProxiedObject(BackendConstructor);
        }
    }
    return QObject::event(e);
}

Backend::~Backend()
{
    if (m_backendProcess) {
        send(BackendShutdown);
        if (!m_backendProcess->waitForFinished()) {
            m_backendProcess->kill();
        }
        delete m_backendProcess;
    }
    Q_ASSERT(s_instance);
    s_instance = 0;
}

QDataStream &Backend::_receive(quint32 func)
{
    while (s_instance->m_lastFunction == NoFunction) {
        if (!s_instance->m_backendProcess->waitForReadyRead()) {
            // waited 30s to get an answer and got nothing, looks like an error
            qFatal("Backend::receive timed out");
        }
        s_instance->readyRead();
    }
    Q_ASSERT(s_instance->m_lastFunction == func);
    s_instance->m_lastFunction = NoFunction;
    return s_instance->m_stream;
}

void Backend::readyRead()
{
    quint32 receivedFunc;
    ProxyObject *o = 0;
    while (m_backendProcess->bytesAvailable() > 0) {
        m_stream >> receivedFunc;
        if (receivedFunc >= _AfterFunctionId) {
            // we got a signal
            m_stream >> o;
            Q_ASSERT(o);
            Q_ASSERT(m_validObjects.contains(o));
            o->handleSignal(static_cast<SignalId>(receivedFunc), m_stream);
        } else {
            // we received an answer to a function
            m_lastFunction = static_cast<FunctionId>(receivedFunc);
            // don't continue reading from the stream since the following data might be needed for
            // the return value
            return;
        }
    }
}

void Backend::handleSignal(SignalId func, QDataStream &stream) const
{
    Q_ASSERT(func == BackendObjectDescriptionChanged);
    quint32 t;
    stream >> t;
    emit objectDescriptionChanged(static_cast<ObjectDescriptionType>(t));
}

QObject *Backend::createObject(BackendInterface::Class c, QObject *parent, const QList<QVariant> &args)
{
    ProxyObject *o = 0;
    switch (c) {
    case MediaObjectClass:
        o = new MediaObject(parent);
        break;
    case VolumeFaderEffectClass:
        //o = new VolumeFaderEffect(parent);
        break;
    case AudioOutputClass:
        o = new AudioOutput(parent);
        break;
    case AudioDataOutputClass:
        //o = new AudioDataOutput(parent);
        break;
    case VisualizationClass:
        //o = new Visualization(parent);
        break;
    case VideoDataOutputClass:
        //o = new VideoDataOutput(parent);
        break;
    case EffectClass:
        Q_ASSERT(args.size() == 1);
        o = new Effect(args[0].toInt(), parent);
        break;
    case VideoWidgetClass:
        //o = new VideoWidget(qobject_cast<QWidget *>(parent));
        break;
    }
    if (o && o->isValid()) {
        return o->qObject();
    }
    delete o;
    return 0;
}

QStringList Backend::availableMimeTypes() const
{
    send(BackendAvailableMimeTypes);
    QStringList ret;
    receive(BackendAvailableMimeTypes) >> ret;
    return ret;
}

QList<int> Backend::objectDescriptionIndexes(ObjectDescriptionType type) const
{
    send(BackendObjectDescriptionIndexes) << type;
    QList<int> list;
    receive(BackendObjectDescriptionIndexes) >> list;
    return list;
}

QHash<QByteArray, QVariant> Backend::objectDescriptionProperties(ObjectDescriptionType type, int index) const
{
    send(BackendObjectDescriptionProperties) << type << index;
    QHash<QByteArray, QVariant> ret;
    receive(BackendObjectDescriptionProperties) >> ret;
    return ret;
}

bool Backend::startConnectionChange(QSet<QObject *> nodes)
{
    send(BackendStartConnectionChange) << nodes;
    bool r;
    receive(BackendStartConnectionChange) >> r;
    return r;
}

bool Backend::connectNodes(QObject *_source, QObject *_sink)
{
    send(BackendConnectNodes) << _source << _sink;
    bool r;
    receive(BackendConnectNodes) >> r;
    return r;
}

bool Backend::disconnectNodes(QObject *_source, QObject *_sink)
{
    send(BackendDisconnectNodes) << _source << _sink;
    bool r;
    receive(BackendDisconnectNodes) >> r;
    return r;
}

bool Backend::endConnectionChange(QSet<QObject *> nodes)
{
    send(BackendEndConnectionChange) << nodes;
    bool r;
    receive(BackendEndConnectionChange) >> r;
    return r;
}

}}
