//
// fiv-io.h: image operations
//
// Copyright (c) 2021 - 2023, Přemysl Eric Janouch 
//
// Permission to use, copy, modify, and/or distribute this software for any
// purpose with or without fee is hereby granted.
//
// THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
// WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
// MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY
// SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
// WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION
// OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN
// CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
//
#pragma once
#include 
#include 
#include 
#include   // WebPConfig
// --- Colour management -------------------------------------------------------
// TODO(p): Make it also possible to use Skia's skcms.
typedef void *FivIoProfile;
FivIoProfile fiv_io_profile_new(const void *data, size_t len);
FivIoProfile fiv_io_profile_new_sRGB(void);
void fiv_io_profile_free(FivIoProfile self);
// From libwebp, verified to exactly match [x * a / 255].
#define PREMULTIPLY8(a, x) (((uint32_t) (x) * (uint32_t) (a) * 32897U) >> 23)
// --- Loading -----------------------------------------------------------------
extern const char *fiv_io_supported_media_types[];
gchar **fiv_io_all_supported_media_types(void);
typedef enum _FivIoOrientation FivIoOrientation;
typedef struct _FivIoRenderClosure FivIoRenderClosure;
typedef struct _FivIoImage FivIoImage;
// https://www.cipa.jp/std/documents/e/DC-008-2012_E.pdf Table 6
enum _FivIoOrientation {
	FivIoOrientationUnknown   = 0,
	FivIoOrientation0         = 1,
	FivIoOrientationMirror0   = 2,
	FivIoOrientation180       = 3,
	FivIoOrientationMirror180 = 4,
	FivIoOrientationMirror270 = 5,
	FivIoOrientation90        = 6,
	FivIoOrientationMirror90  = 7,
	FivIoOrientation270       = 8
};
// TODO(p): Maybe make FivIoProfile a referencable type,
// then loaders could store it in their closures.
struct _FivIoRenderClosure {
	/// The rendering is allowed to fail, returning NULL.
	FivIoImage *(*render)(FivIoRenderClosure *, FivIoProfile, double scale);
	void (*destroy)(FivIoRenderClosure *);
};
// Metadata are typically attached to all Cairo surfaces in an animation.
struct _FivIoImage {
	uint8_t *data;                      ///< Raw image data
	cairo_format_t format;              ///< Data format
	uint32_t width;                     ///< Width of the image in pixels
	uint32_t stride;                    ///< Row stride in bytes
	uint32_t height;                    ///< Height of the image in pixels
	FivIoOrientation orientation;       ///< Orientation to use for display
	GBytes *exif;                       ///< Raw Exif/TIFF segment
	GBytes *icc;                        ///< Raw ICC profile data
	GBytes *xmp;                        ///< Raw XMP data
	GBytes *thum;                       ///< WebP THUM chunk, for our thumbnails
	/// GHashTable with key-value pairs from PNG's tEXt, zTXt, iTXt chunks.
	/// Currently only read by fiv_io_open_png_thumbnail().
	GHashTable *text;
	/// A FivIoRenderClosure for parametrized re-rendering of vector formats.
	/// This is attached at the page level.
	FivIoRenderClosure *render;
	/// The first frame of the next page, in a chain.
	/// There is no wrap-around.
	FivIoImage *page_next;
	/// The first frame of the previous page, in a chain.
	/// There is no wrap-around. This is a weak pointer.
	FivIoImage *page_previous;
	/// The next frame in a sequence, in a chain, pre-composited.
	/// There is no wrap-around.
	FivIoImage *frame_next;
	/// The previous frame in a sequence, in a chain, pre-composited.
	/// This is a weak pointer that wraps around,
	/// and needn't be present for static images.
	FivIoImage *frame_previous;
	/// Frame duration in milliseconds.
	int64_t frame_duration;
	/// How many times to repeat the animation, or zero for +inf.
	uint64_t loops;
};
FivIoImage *fiv_io_image_ref(FivIoImage *image);
void fiv_io_image_unref(FivIoImage *image);
/// Analogous to cairo_image_surface_create(). May return NULL.
FivIoImage *fiv_io_image_new(
	cairo_format_t format, uint32_t width, uint32_t height);
/// Return a new Cairo image surface referencing the same data as the image,
/// eating the reference to it.
cairo_surface_t *fiv_io_image_to_surface(FivIoImage *image);
/// Return a new Cairo image surface referencing the same data as the image,
/// without eating the image's reference.
cairo_surface_t *fiv_io_image_to_surface_noref(const FivIoImage *image);
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
typedef struct {
	const char *uri;                    ///< Source URI
	FivIoProfile screen_profile;        ///< Target colour space or NULL
	int screen_dpi;                     ///< Target DPI
	gboolean enhance;                   ///< Enhance JPEG (currently)
	gboolean first_frame_only;          ///< Only interested in the 1st frame
	GPtrArray *warnings;                ///< String vector for non-fatal errors
} FivIoOpenContext;
FivIoImage *fiv_io_open(const FivIoOpenContext *ctx, GError **error);
FivIoImage *fiv_io_open_from_data(
	const char *data, size_t len, const FivIoOpenContext *ctx, GError **error);
FivIoImage *fiv_io_open_png_thumbnail(const char *path, GError **error);
// --- Metadata ----------------------------------------------------------------
/// Returns a rendering matrix for an image (user space to pattern space),
/// and its target dimensions.
cairo_matrix_t fiv_io_orientation_apply(const FivIoImage *image,
	FivIoOrientation orientation, double *width, double *height);
void fiv_io_orientation_dimensions(const FivIoImage *image,
	FivIoOrientation orientation, double *width, double *height);
/// Extracts the orientation field from Exif, if there's any.
FivIoOrientation fiv_io_exif_orientation(const guint8 *exif, gsize len);
/// Save metadata attached by this module in Exiv2 format.
gboolean fiv_io_save_metadata(
	const FivIoImage *page, const char *path, GError **error);
// --- Thumbnail passing utilities ---------------------------------------------
enum { FIV_IO_SERIALIZE_LOW_QUALITY = 1 << 0 };
void fiv_io_serialize_to_stdout(cairo_surface_t *surface, guint64 user_data);
cairo_surface_t *fiv_io_deserialize(GBytes *bytes, guint64 *user_data);
GBytes *fiv_io_serialize_for_search(cairo_surface_t *surface, GError **error);
// --- Export ------------------------------------------------------------------
/// Encodes an image as a WebP bitstream, following the configuration.
/// The result needs to be freed using WebPFree/WebPDataClear().
unsigned char *fiv_io_encode_webp(
	FivIoImage *image, const WebPConfig *config, size_t *len);
/// Saves the page as a lossless WebP still picture or animation.
/// If no exact frame is specified, this potentially creates an animation.
gboolean fiv_io_save(FivIoImage *page, FivIoImage *frame,
	FivIoProfile target, const char *path, GError **error);