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

    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.

*/


// Sample code:
{
    PcmIODevice *pcm = new PcmIODevice(parent);
    pcm->setFormat(48000, PcmIODevice::Signed16Bit, 2);
    pcm->setCategory(Phonon::CommunicationCategory);
    pcm->open(QIODevice::ReadOnly, PcmIODevice::FixedFormat | PcmIODevice::LowLatency);
    pcm->emitReadyReadAt(8, 10); // 8-10 ms
    connect(pcm, SIGNAL(readyRead()), consumer, SLOT(packetReady()));
}

void Consumer::packetReady()
{
    Packet p = m_pool.packet();
    p.setSize(pcm->read(p.data(), p.size()));
}

{
    PcmIODevice *pcm = new PcmIODevice(parent);
    pcm->open(Phonon::CommunicationCategory, Format(48000, 16, 2), QIODevice::WriteOnly | PcmIODevice::FixedFormat);
    pcm->setHint(PcmIODevice::LowLatency);
}

void Producer::dataProducingFunction
{
    char *data = m_packet.data();
    data += m_position;
    // write some data
    if (m_position == m_packet.size()) {
        qint64 written = pcm->write(m_packet.data(), m_packet.size());
        if (written == m_packet.size()) {
            m_packet = m_pool.packet();
        }
    }
}



    PcmInput *input = new PcmInput(Phonon::CommunicationCategory);
    input->setHint(Phonon::LowLatency);
    input->requestFormat(QList<Format>() << Format(48000, 16, 1) << Format(44100, 16, 1) << ...);
    input->setPacketSize(8, 10); // 8-10 ms
    connect(input, SIGNAL(packetReady(Packet)), consumer, SLOT(packetReady(Packet)));

    PcmOutput *output = new PcmOutput(Phonon::CommunicationCategory);
    output->setFormat(...);
    //output->setPacketSize(8, 10); // 8-10ms
    connect(output, SIGNAL(packetRequest(Packet)), producer, SLOT(packetRequest(Packet)));
}

class PacketPool
{
    public:
        PacketPool(int packetSize, int numberOfPackets);
        ~PacketPool();

        Packet getPacket();

    private:
        void returnPacket(const Packet &);

        int m_size;
        QStack<Packet> m_packets;
        char *m_data;
#ifndef NDEBUG
        int m_numberOfPackets;
#endif
};

PacketPool::PacketPool(int packetSize, int numberOfPackets)
    : m_size(packetSize)
#ifndef NDEBUG
    , m_numberOfPackets(numberOfPackets)
#endif
{
    m_data = qMalloc(packetSize * numberOfPackets);
    for (int i = 0; i < numberOfPackets; ++i) {
        PacketData *d = new PacketData(packetSize, m_data + (i * packetSize), this);
        m_packets.push(Packet(d));
    }
#ifndef NDEBUG
    Q_ASSERT(m_numberOfPackets == m_packets.size());
#endif
}

PacketPool::~PacketPool()
{
#ifndef NDEBUG
    Q_ASSERT(m_numberOfPackets == m_packets.size());
#endif
    // TODO: make all Packet objects not call returnPacket...
    qFree(m_data);
}

void PacketPool::returnPacket(const Packet &packet)
{
    m_packets.push(packet);
}

struct PacketData : public QSharedData
{
    PacketData(int size, char *data, PacketPool *pool) : m_size(size), m_data(data), m_pool(pool) {}

    const int m_size;
    char *const m_data;
    PacketPool *const m_pool;
};

// need some packet class in a pool instead of QByteArray
class Packet
{
    static PacketData shared_null = { Q_BASIC_ATOMIC_INITIALIZER(1), 0, 0, 0 };
    public:
        Packet() : d(&shared_null) {}
        Packet(const Packet &rhs) : d(rhs.d) { d->ref.ref(); }
        Packet &operator=(const Packet &rhs) { if (!d->ref.deref()) d->m_pool->returnPacket(Packet(this)); d = rhs.d; d->ref.ref(); }
        bool operator==(const Packet &rhs) { return d == rhs.d; }
        bool operator!=(const Packet &rhs) { return d != rhs.d; }
        char *data() const { Q_ASSERT(isValid()); return d->m_data; }
        int size() const { Q_ASSERT(isValid()); return d->m_size; }
        bool isNull() const { return d == &shared_null; }

    protected:
        Packet(PacketData *dd) : d(dd) {}
        PacketData *d;
};

void Producer::packetRequest(Packet &data)
{
    data = mydata;
}

// implementation of PcmOutput calls returnPacket(...)
