/********************************************************************
Copyright 2014  Martin Gräßlin <mgraesslin@kde.org>

This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public
License as published by the Free Software Foundation; either
version 2.1 of the License, or (at your option) version 3, or any
later version accepted by the membership of KDE e.V. (or its
successor approved by the membership of KDE e.V.), which shall
act as a proxy defined in Section 6 of version 3 of the license.

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
Lesser General Public License for more details.

You should have received a copy of the GNU Lesser General Public
License along with this library.  If not, see <http://www.gnu.org/licenses/>.
*********************************************************************/
#ifndef WAYLAND_SHM_POOL_H
#define WAYLAND_SHM_POOL_H

#include <QObject>

#include "buffer.h"
#include <KWayland/Client/kwaylandclient_export.h>

class QImage;
class QSize;

struct wl_shm;

namespace KWayland
{
namespace Client
{

class EventQueue;

/**
 * @short Wrapper class for wl_shm interface.
 *
 * This class holds a shared memory pool together with the Wayland server.
 *
 * To use this class one needs to interact with the Registry. There are two
 * possible ways to create a ShmPool instance:
 * @code
 * ShmPool *s = registry->createShmPool(name, version);
 * @endcode
 *
 * This creates the ShmPool and sets it up directly. As an alternative this
 * can also be done in a more low level way:
 * @code
 * ShmPool *s = new ShmPool;
 * s->setup(registry->bindShm(name, version));
 * @endcode
 *
 * The ShmPool holds a memory-mapped file from which it provides Buffers.
 * All Buffers are hold by the ShmPool and can be reused. Whenever a Buffer
 * is requested the ShmPool tries to reuse an existing Buffer. A Buffer can
 * be reused if the following conditions hold
 * @li it's no longer marked as used
 * @li the server released the buffer
 * @li the size matches
 * @li the stride matches
 * @li the format matches
 *
 * The ownership of a Buffer stays with ShmPool. The ShmPool might destroy the
 * Buffer at any given time. Because of that ShmPool only provides QWeakPointer
 * for Buffers. Users should always check whether the pointer is still valid and
 * only promote to a QSharedPointer for a short time, e.g. to set new data.
 *
 * The ShmPool can provide Buffers for different purposes. One can create a Buffer
 * from an existing QImage. This will use a Buffer with same size, stride and image
 * format as the QImage and <b>copy</b> the content of the QImage into the Buffer.
 * The memory is <b>not</b> shared:
 * @code
 * QImage image(24, 24, QImage::Format_ARG32);
 * image.fill(Qt::transparent);
 * Buffer::Ptr buffer = s->createBuffer(image);
 * @endcode
 *
 * It is also possible to create a Buffer and copy the content from a generic location.
 * Like above this doesn't share the content but copies it:
 * @code
 * QImage image(24, 24, QImage::Format_ARG32);
 * image.fill(Qt::transparent);
 * Buffer::Ptr buffer = s->createBuffer(image.size(), image.bytesPerLine(), image.constBits());
 * @endcode
 *
 * Last but not least it is possible to get a Buffer without copying content directly to it.
 * This means an empty area is just reserved and can be used to e.g. share the memory with a
 * QImage:
 * @code
 * const QSize size = QSize(24, 24);
 * const int stride = size.width() * 4;
 * Buffer::Ptr buffer = s->getBuffer(size, stride, Buffer::Format::RGB32);
 * if (!buffer) {
 *     qDebug() << "Didn't get a valid Buffer";
 *     return;
 * }
 * QImage image(buffer.toStrongRef()->address(), size.width(), size.height(), stride, QImage::Format_RGB32);
 * image.fill(Qt::black);
 * @endcode
 *
 * A Buffer can be attached to a Surface:
 * @code
 * Compositor *c = registry.createCompositor(name, version);
 * Surface *s = c->createSurface();
 * s->attachBuffer(buffer);
 * s->damage(QRect(QPoint(0, 0), size));
 * @endcode
 *
 * Once a Buffer is attached to a Surface and the Surface is committed, it might be released
 * by the Wayland server and thus is free to be reused again. If the client code wants to
 * continue using the Buffer it must call Buffer::setUsed on it. This is important if the memory
 * is shared for example with a QImage as the memory buffer for a QImage must remain valid
 * throughout the life time of the QImage:
 * @code
 * buffer.toStrongRef()->setUsed(true);
 * @endcode
 *
 * This is also important for the case that the shared memory pool needs to be resized.
 * The ShmPool will automatically resize if it cannot provide a new Buffer. During the resize
 * all existing Buffers are unmapped and any shared objects must be recreated. The ShmPool emits
 * the signal poolResized() after the pool got resized.
 *
 * @see Buffer
 **/
class KWAYLANDCLIENT_EXPORT ShmPool : public QObject
{
    Q_OBJECT
public:
    explicit ShmPool(QObject *parent = nullptr);
    virtual ~ShmPool();
    /**
     * @returns @c true if the ShmPool references a wl_shm interface and the shared memory pool
     * is setup.
     **/
    bool isValid() const;
    /**
     * Setup this ShmPool to manage the @p shm.
     * This also creates the shared memory pool.
     * When using Registry::createShmPool there is no need to call this
     * method.
     **/
    void setup(wl_shm *shm);
    /**
     * Releases the wl_shm interface.
     * After the interface has been released the ShmPool instance is no
     * longer valid and can be setup with another wl_shm interface.
     *
     * This also destroys the shared memory pool and all Buffers are destroyed.
     **/
    void release();
    /**
     * Destroys the data hold by this ShmPool.
     * This method is supposed to be used when the connection to the Wayland
     * server goes away. If the connection is not valid any more, it's not
     * possible to call release any more as that calls into the Wayland
     * connection and the call would fail. This method cleans up the data, so
     * that the instance can be deleted or setup to a new wl_shm interface
     * once there is a new connection available.
     *
     * All Buffers are destroyed!
     *
     * It is suggested to connect this method to ConnectionThread::connectionDied:
     * @code
     * connect(connection, &ConnectionThread::connectionDied, shmPool, &ShmPool::destroyed);
     * @endcode
     *
     * @see release
     **/
    void destroy();

    /**
     * Sets the @p queue to use for creating a Buffer.
     **/
    void setEventQueue(EventQueue *queue);
    /**
     * @returns The event queue to use for creating a Buffer.
     **/
    EventQueue *eventQueue();

    /**
     * Provides a Buffer with:
     * @li same size as @p image
     * @li same stride as @p image
     * @li same format as @p image
     *
     * If the ShmPool fails to provide such a Buffer a @c null Buffer::Ptr is returned.
     * The content of the @p image is <b>copied</b> into the buffer. The @p image and
     * returned Buffer do <b>not</b> share memory.
     *
     * @param image The image which should be copied into the Buffer
     * @return Buffer with copied content of @p image in success case, a @c null Buffer::Ptr otherwise
     * @see getBuffer
     **/
    Buffer::Ptr createBuffer(const QImage &image);
    /**
     * Provides a Buffer with @p size, @p stride and @p format.
     *
     * If the ShmPool fails to provide such a Buffer a @c null Buffer::Ptr is returned.
     * A memory copy is performed from @p src into the Buffer. The Buffer does <b>not</b> share
     * memory with @p src.
     *
     * @param size The requested size for the Buffer
     * @param stride The requested stride for the Buffer
     * @param src The source memory location to copy from
     * @param format The requested format for the Buffer
     * @return Buffer with copied content of @p src in success case, a @c null Buffer::Ptr otherwise
     * @see getBuffer
     **/
    Buffer::Ptr createBuffer(const QSize &size, int32_t stride, const void *src, Buffer::Format format = Buffer::Format::ARGB32);
    void *poolAddress() const;
    /**
     * Provides a Buffer with @p size, @p stride and @p format.
     *
     * If the ShmPool fails to provide such a Buffer a @c null Buffer::Ptr is returned.
     * Unlike with createBuffer there is no memory copy performed. This provides a bare Buffer
     * to be used by the user.
     *
     * @param size The requested size for the Buffer
     * @param stride The requested stride for the Buffer
     * @param format The requested format for the Buffer
     * @return Buffer as requested in success case, a @c null Buffer::Ptr otherwise.
     * @see createBuffer
     **/
    Buffer::Ptr getBuffer(const QSize &size, int32_t stride, Buffer::Format format = Buffer::Format::ARGB32);
    wl_shm *shm();
Q_SIGNALS:
    /**
     * This signal is emitted whenever the shared memory pool gets resized.
     * Any used Buffer must be remapped.
     **/
    void poolResized();
private:
    class Private;
    QScopedPointer<Private> d;
};

}
}

#endif
