/*  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 "audiopath.h"
#include "audioeffect.h"
#include "abstractaudiooutput.h"
#include "audiooutput.h"

#include "qbtgstreamer/qbtgstreamerelementfactory.h"

#include <QVector>
#include <QString>

#include <kdebug.h>

namespace Phonon
{
namespace GStreamer
{

AudioPath::AudioPath(QObject *parent)
    : QObject(parent),
      d(new AudioPathPrivate)
{
    d->pipeline = Pipeline::instance();
    d->setupBin();
}

AudioPath::~AudioPath()
{
    delete d;
}

bool AudioPath::addOutput(QObject *audioOutput)
{
    kDebug(611) << k_funcinfo << endl;

    AudioOutput *out = qobject_cast<AudioOutput*>(audioOutput);

    if (!out) {
        kWarning(611) << "Adding an audiopath output, but no output given!" << endl;
        return false;
    }

    AbstractAudioOutput *ao = qobject_cast<AbstractAudioOutput *>(audioOutput);
    if (!ao) {
        kWarning(611) << "Could not cast the audio output to an abstract audio output!" << endl;
        return false;
    }

    foreach (OutputLink *l, d->links) {
        if (l->output == ao) {
            kWarning(611) << "Output already added!" << endl;
            return false;
        }
    }

    OutputLink *link = new OutputLink;
    link->output = ao;
    link->srcPad = d->requestSrcPad();
    link->sinkPad = out->requestSinkPad(); 

    kDebug(611) << "Linking path to output." << endl;
    link->srcPad->link(link->sinkPad);

    d->links << link;

    return true;
}

bool AudioPath::removeOutput(QObject *audioOutput)
{
    kDebug(611) << k_funcinfo << endl;

    AudioOutput *out = qobject_cast<AudioOutput*>(audioOutput);

    if (!out) {
        kWarning(611) << "Adding an audiopath output, but no output given!" << endl;
        return false;
    }

    AbstractAudioOutput *ao = qobject_cast<AbstractAudioOutput *>(audioOutput);
    if (!ao) {
        kWarning(611) << "Could not cast the audio output to an abstract audio output!" << endl;
        return false;
    }

    OutputLink *link;

    kDebug(611) << "Finding the correct link" << endl;
    foreach (OutputLink *l, d->links) {
        if (l->output == ao) {
            link = l;
            break;
        }
    }

    if (!link) {
        kWarning(611) << "Link was not found!" << endl;
        return false;
    }

    kDebug(611) << "Unlinking src and sink" << endl;
    link->srcPad->unlink(link->sinkPad);

    kDebug(611) << "Releasing sink pad" << endl;
    out->releaseSinkPad(link->sinkPad);
    d->releaseSrcPad(link->srcPad);

    kDebug(611) << "Removing link" << endl;
    d->links.removeAll(link);

    return true;
}

bool AudioPath::insertEffect(QObject *newEffect, QObject *insertBefore)
{
    Q_ASSERT(newEffect);
    AudioEffect *ae = qobject_cast<AudioEffect *>(newEffect);
    Q_ASSERT(ae);

    AudioEffect *before = 0;

    if (insertBefore) {
        before = qobject_cast<AudioEffect *>(insertBefore);
        Q_ASSERT(before);

        if (!m_effects.contains(before))
            return false;

        m_effects.insert(m_effects.indexOf(before), ae);
     } else {
         m_effects.append(ae);
     }

     return true;
}

bool AudioPath::removeEffect(QObject *effect)
{
    Q_ASSERT(effect);
    AudioEffect *ae = qobject_cast<AudioEffect *>(effect);
    Q_ASSERT(ae);

    if (m_effects.removeAll(ae) > 0)
        return true;

    return false;
}

/*
 *  +----------------------------------------+
 *  | bin                                    |
 *  |    +-------+                +-----+    |
 *  |    | adder |    effects     | tee |    |
 *  |    |       src- ........ -sink    |    |
 * -sink--sink   |                |  src---src-
 * -sink--sink   |                |  src---src-
 *  ...  +-------+                +-----+  ... 
 *  ...                                    ...
 *  |                                        |
 *  +----------------------------------------+
 */
bool AudioPathPrivate::setupBin()
{
    kDebug(611) << k_funcinfo << endl;

    bin = new QbtGStreamerBin("audiopathBin");

    // Create the adder element
    adder = QbtGStreamerElementFactory::makeElement("adder", "audiopathAdder");
    if (!adder) {
        kWarning(611) << "Element 'adder' was not found!" << endl;
        return false;
    }

    // Create the tee element
    tee = QbtGStreamerElementFactory::makeElement("tee", "audiopathTee");
    if (!tee) {
        kWarning(611) << "Element 'tee' was not found!" << endl;
        return false;
    }

    // Add the adder and tee element to the bin
    if (!bin->addElement(adder) || !bin->addElement(tee)) {
        kWarning(611) << "Error creating the audiopath pipeline. Could not add the element 'adder' or 'tee' to the bin!" << endl;
        return false;
    } 

    // Get the adder src and tee sink
    adderSrc = adder->pad("src");
    teeSink = tee->pad("sink");

    // Link the adder to the tee without any initial effects
    QbtGStreamerPadLinkReturn r = adderSrc->link(teeSink);
    if (r != QbtGStreamerPadLinkOk) {
        kWarning(611) << "Problem linking adder src and tee sink!" << endl;
        return false;
    }

    bin->setState(QbtGStreamerStatePaused);

    // Add the bin to the pipeline
    if (!pipeline->add(bin)) {
        kWarning(611) << "Could not add the audiopath bin to the audiopath pipeline!" << endl;
        return false;
    }

    return true;
}

QbtGStreamerGhostPad *AudioPathPrivate::requestSrcPad()
{
    kDebug(611) << k_funcinfo << endl;

    QbtGStreamerPad *pad;
    QbtGStreamerGhostPad *result = 0;
    QString name;

    pad = tee->requestPad("src%d");
    if (!pad) {
        kWarning(611) << "Could not get a src pad from the tee element!" << endl;
        return 0;
    }

    name = pad->name();
    kDebug(611) << "Creating a ghost pad with the name " << name << endl;
    result = new QbtGStreamerGhostPad(name, pad);

    /* activate and add to element */
    result->setActive(true);
    bin->addPad(result);

    return result; 
}

void AudioPathPrivate::releaseSrcPad(QbtGStreamerGhostPad *pad)
{
    kDebug(611) << k_funcinfo << endl;

    QbtGStreamerPad *target;

    target = pad->target();

    tee->releaseRequestPad(target);

    bin->setState(QbtGStreamerStateNull);

    pad->setActive(false);
    bin->removePad(pad);
}

}}

#include "audiopath.moc"
// vim: sw=4 ts=4 noet
