/* Copyright (C) 2023 The gtkmm Development Team
 *
 * 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) any later version.
 *
 * 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/>.
 */

#include <glibmm/object.h>
#include <gdkmm/enums.h>
#include <cairomm/region.h>

_DEFS(gdkmm,gdk)
_PINCLUDE(glibmm/private/object_p.h)

namespace Gdk
{
class GDKMM_API ColorState;
class GDKMM_API Display;
class GDKMM_API Texture;

/** %Gdk::DmabufTextureBuilder is a builder used to construct Gdk::Texture
 * objects from DMA buffers.
 *
 * DMA buffers are commonly called @em dma-bufs.
 *
 * DMA buffers is a feature of the Linux kernel to enable efficient buffer and
 * memory sharing between hardware such as codecs, GPUs, displays, cameras and the
 * kernel drivers controlling them. For example, a decoder may want its output to
 * be directly shared with the display server for rendering without a copy.
 *
 * Any device driver which participates in DMA buffer sharing, can do so as either
 * the exporter or importer of buffers (or both).
 *
 * The memory that is shared via DMA buffers is usually stored in non-system memory
 * (maybe in device's local memory or something else not directly accessible by the
 * CPU), and accessing this memory from the CPU may have higher than usual overhead.
 *
 * In particular for graphics data, it is not uncommon that data consists of multiple
 * separate blocks of memory, for example one block for each of the red, green and
 * blue channels. These blocks are called @em planes. DMA buffers can have up to
 * four planes. Even if the memory is a single block, the data can be organized in
 * multiple planes, by specifying offsets from the beginning of the data.
 *
 * DMA buffers are exposed to user-space as file descriptors allowing to pass them
 * between processes. If a DMA buffer has multiple planes, there is one file
 * descriptor per plane.
 *
 * The format of the data (for graphics data, essentially its colorspace) is described
 * by a 32-bit integer. These format identifiers are defined in the header file
 * [drm/drm_fourcc.h](https://github.com/torvalds/linux/blob/master/include/uapi/drm/drm_fourcc.h)
 * and commonly referred to as @em fourcc values, since they are identified by 4 ASCII
 * characters. Additionally, each DMA buffer has a @em modifier, which is a 64-bit integer
 * that describes driver-specific details of the memory layout, such as tiling or compression.
 *
 * The operation of %Gdk::DmabufTextureBuilder is quite simple: Create a texture builder,
 * set all the necessary properties, and then call build() to create the new texture.
 *
 * The required properties for a dma-buf texture are
 *  - The width and height in pixels
 *  - The `fourcc` code and `modifier` which identify the format and memory layout of the dma-buf
 *  - The file descriptor, offset and stride for each of the planes
 *
 * %Gdk::DmabufTextureBuilder can be used for quick one-shot construction of
 * textures as well as kept around and reused to construct multiple textures.
 *
 * @newin{4,14}
 */
class GDKMM_API DmabufTextureBuilder : public Glib::Object
{
  _CLASS_GOBJECT(DmabufTextureBuilder, GdkDmabufTextureBuilder, GDK_DMABUF_TEXTURE_BUILDER, Glib::Object, GObject, , , GDKMM_API)
  _DO_NOT_DERIVE_GTYPE dnl// GdkDmabufTextureBuilder is an internal type

protected:
  _CTOR_DEFAULT()

public:
  _WRAP_CREATE()

  _WRAP_METHOD(Glib::RefPtr<Display> get_display(), gdk_dmabuf_texture_builder_get_display, refreturn)
  _WRAP_METHOD(Glib::RefPtr<const Display> get_display() const, gdk_dmabuf_texture_builder_get_display, refreturn, constversion)
  _WRAP_METHOD(void set_display(const Glib::RefPtr<Display>& display), gdk_dmabuf_texture_builder_set_display)

  _WRAP_METHOD(unsigned int get_width() const, gdk_dmabuf_texture_builder_get_width)
  _WRAP_METHOD(void set_width(unsigned int width), gdk_dmabuf_texture_builder_set_width)

  _WRAP_METHOD(unsigned int get_height() const, gdk_dmabuf_texture_builder_get_height)
  _WRAP_METHOD(void set_height(unsigned int height), gdk_dmabuf_texture_builder_set_height)

  _WRAP_METHOD(guint32 get_fourcc() const, gdk_dmabuf_texture_builder_get_fourcc)
  _WRAP_METHOD(void set_fourcc(guint32 fourcc), gdk_dmabuf_texture_builder_set_fourcc)

  _WRAP_METHOD(guint64 get_modifier() const, gdk_dmabuf_texture_builder_get_modifier)
  _WRAP_METHOD(void set_modifier(guint64 modifier), gdk_dmabuf_texture_builder_set_modifier)

  _WRAP_METHOD(bool get_premultiplied() const, gdk_dmabuf_texture_builder_get_premultiplied)
  _WRAP_METHOD(void set_premultiplied(bool premultiplied = true), gdk_dmabuf_texture_builder_set_premultiplied)

  _WRAP_METHOD(unsigned int get_n_planes() const, gdk_dmabuf_texture_builder_get_n_planes)
  _WRAP_METHOD(void set_n_planes(unsigned int n_planes), gdk_dmabuf_texture_builder_set_n_planes)

  _WRAP_METHOD(int get_fd(unsigned int plane) const, gdk_dmabuf_texture_builder_get_fd)
  _WRAP_METHOD(void set_fd(unsigned int plane, int fd), gdk_dmabuf_texture_builder_set_fd)

  _WRAP_METHOD(unsigned int get_stride(unsigned int plane) const, gdk_dmabuf_texture_builder_get_stride)
  _WRAP_METHOD(void set_stride(unsigned int plane, unsigned int stride), gdk_dmabuf_texture_builder_set_stride)

  _WRAP_METHOD(unsigned int get_offset(unsigned int plane) const, gdk_dmabuf_texture_builder_get_offset)
  _WRAP_METHOD(void set_offset(unsigned int plane, unsigned int offset), gdk_dmabuf_texture_builder_set_offset)

  // ColorState is immutable. No need for a non-const get-method.
  _WRAP_METHOD(Glib::RefPtr<ColorState> get_color_state() const, gdk_dmabuf_texture_builder_get_color_state, refreturn)
  _WRAP_METHOD(void set_color_state(const Glib::RefPtr<const ColorState>& color_state), gdk_dmabuf_texture_builder_set_color_state)

  _WRAP_METHOD(Glib::RefPtr<Texture> get_update_texture(), gdk_dmabuf_texture_builder_get_update_texture, refreturn)
  _WRAP_METHOD(Glib::RefPtr<const Texture> get_update_texture() const, gdk_dmabuf_texture_builder_get_update_texture, refreturn, constversion)
  _WRAP_METHOD(void set_update_texture(const Glib::RefPtr<Texture>& texture), gdk_dmabuf_texture_builder_set_update_texture)

  _WRAP_METHOD(::Cairo::RefPtr< ::Cairo::Region> get_update_region(), gdk_dmabuf_texture_builder_get_update_region, refreturn)
  _WRAP_METHOD(::Cairo::RefPtr<const ::Cairo::Region> get_update_region() const, gdk_dmabuf_texture_builder_get_update_region, refreturn, constversion)
  _WRAP_METHOD(void set_update_region(const ::Cairo::RefPtr< ::Cairo::Region>& region), gdk_dmabuf_texture_builder_set_update_region)

  /** For instance,
   * void on_dmabuf_texture_released();
   */
  using SlotDmabufTextureReleased = sigc::slot<void()>;

  /** Builds a new `Gdk::Texture` with the values set up in the builder.
   *
   * It is a programming error to call this function if any mandatory
   * property has not been set.
   *
   * If the dmabuf is not supported by GTK, an exception will be thrown.
   *
   * The @a slot gets called when the returned texture gets released.
   *
   * It is possible to call this function multiple times to create multiple textures,
   * possibly with changing properties in between.
   *
   * It is the responsibility of the caller to keep the file descriptors for the planes
   * open until the created texture is no longer used, and close them afterwards (possibly
   * using the @a slot notify).
   *
   * Not all formats defined in the `drm_fourcc.h` header are supported. You can use
   * Gdk::Display::get_dmabuf_formats() to get a list of supported formats.
   *
   * @newin{4,14}
   *
   * @param slot Destroy function to be called when the texture is released.
   * @return A newly built `Gdk::Texture` or an empty RefPtr if the format
   *         is not supported, or any mandatory property has not been set.
   *
   * @throws Glib::Error
   */
  Glib::RefPtr<Texture> build(const SlotDmabufTextureReleased& slot = {});
  _IGNORE(gdk_dmabuf_texture_builder_build)

  _WRAP_PROPERTY("display", Glib::RefPtr<Display>)
  _WRAP_PROPERTY("width", unsigned int)
  _WRAP_PROPERTY("height", unsigned int)
  _WRAP_PROPERTY("fourcc", unsigned int)
  _WRAP_PROPERTY("modifier", guint64)
  _WRAP_PROPERTY("premultiplied", bool)
  _WRAP_PROPERTY("n-planes", unsigned int)
  _WRAP_PROPERTY("color-state", Glib::RefPtr<ColorState>)
  _WRAP_PROPERTY("update-region", ::Cairo::RefPtr< ::Cairo::Region>)
  _WRAP_PROPERTY("update-texture", Glib::RefPtr<Texture>)

  // There are no signals or public vfuncs.
};

} // namespace Gdk
