package vendor_openexr

import "core:c"

// Enum declaring allowed values for \c u8 value stored in built-in compression type.
compression_t :: enum c.int {
	NONE  = 0,
	RLE   = 1,
	ZIPS  = 2,
	ZIP   = 3,
	PIZ   = 4,
	PXR24 = 5,
	B44   = 6,
	B44A  = 7,
	DWAA  = 8,
	DWAB  = 9,
}

// Enum declaring allowed values for \c u8 value stored in built-in env map type.
envmap_t :: enum c.int {
	LATLONG = 0,
	CUBE    = 1,
}

// Enum declaring allowed values for \c u8 value stored in \c lineOrder type.
lineorder_t :: enum c.int {
	INCREASING_Y = 0,
	DECREASING_Y = 1,
	RANDOM_Y     = 2,
}

// Enum declaring allowed values for part type.
storage_t :: enum c.int {
	SCANLINE = 0,  // Corresponds to type of \c scanlineimage.
	TILED,         // Corresponds to type of \c tiledimage.
	DEEP_SCANLINE, // Corresponds to type of \c deepscanline.
	DEEP_TILED,    // Corresponds to type of \c deeptile.
}

// @brief Enum representing what type of tile information is contained.
tile_level_mode_t :: enum c.int {
	ONE_LEVEL     = 0, // Single level of image data.
	MIPMAP_LEVELS = 1, // Mipmapped image data.
	RIPMAP_LEVELS = 2, // Ripmapped image data.
}

/** @brief Enum representing how to scale positions between levels. */
tile_round_mode_t :: enum c.int {
	DOWN = 0,
	UP   = 1,
}

/** @brief Enum capturing the underlying data type on a channel. */
pixel_type_t :: enum c.int {
	UINT  = 0,
	HALF  = 1,
	FLOAT = 2,
}

/* /////////////////////////////////////// */
/* First set of structs are data where we can read directly with no allocation needed... */

/** @brief Struct to hold color chromaticities to interpret the tristimulus color values in the image data. */
attr_chromaticities_t :: struct #packed {
	red_x:   f32,
	red_y:   f32,
	green_x: f32,
	green_y: f32,
	blue_x:  f32,
	blue_y:  f32,
	white_x: f32,
	white_y: f32,
}

/** @brief Struct to hold keycode information. */
attr_keycode_t :: struct #packed {
	film_mfc_code:   i32,
	film_type:       i32,
	prefix:          i32,
	count:           i32,
	perf_offset:     i32,
	perfs_per_frame: i32,
	perfs_per_count: i32,
}

/** @brief struct to hold a 32-bit floating-point 3x3 matrix. */
attr_m33f_t :: struct #packed {
	m: [9]f32,
}

/** @brief struct to hold a 64-bit floating-point 3x3 matrix. */
attr_m33d_t :: struct #packed {
	m: [9]f64,
}

/** @brief Struct to hold a 32-bit floating-point 4x4 matrix. */
attr_m44f_t :: struct #packed {
	m: [16]f32,
}

/** @brief Struct to hold a 64-bit floating-point 4x4 matrix. */
attr_m44d_t :: struct #packed {
	m: [16]f64,
}

/** @brief Struct to hold an integer ratio value. */
attr_rational_t :: struct #packed {
	num: i32,
	denom: u32,
}

/** @brief Struct to hold timecode information. */
attr_timecode_t :: struct #packed {
	time_and_flags: u32,
	user_data:      u32,
}

/** @brief Struct to hold a 2-element integer vector. */
attr_v2i_t :: distinct [2]i32

/** @brief Struct to hold a 2-element 32-bit float vector. */
attr_v2f_t :: distinct [2]f32

/** @brief Struct to hold a 2-element 64-bit float vector. */
attr_v2d_t :: distinct [2]f64

/** @brief Struct to hold a 3-element integer vector. */
attr_v3i_t :: distinct [3]i32

/** @brief Struct to hold a 3-element 32-bit float vector. */
attr_v3f_t :: distinct [3]f32

/** @brief Struct to hold a 3-element 64-bit float vector. */
attr_v3d_t :: distinct [3]f64

/** @brief Struct to hold an integer box/region definition. */
attr_box2i_t :: struct #packed {
	min: attr_v2i_t,
	max: attr_v2i_t,
}

/** @brief Struct to hold a floating-point box/region definition. */
attr_box2f_t:: struct #packed {
	min: attr_v2f_t,
	max: attr_v2f_t,
}

/** @brief Struct holding base tiledesc attribute type defined in spec
 *
 * NB: This is in a tightly packed area so it can be read directly, be
 * careful it doesn't become padded to the next \c uint32_t boundary.
 */
attr_tiledesc_t :: struct #packed {
	x_size: u32,
	y_size: u32,
	level_and_round: u8,
}

/** @brief Macro to access type of tiling from packed structure. */
GET_TILE_LEVEL_MODE :: #force_inline proc "c" (tiledesc: attr_tiledesc_t) -> tile_level_mode_t {
	return tile_level_mode_t(tiledesc.level_and_round & 0xf)
}
/** @brief Macro to access the rounding mode of tiling from packed structure. */
GET_TILE_ROUND_MODE :: #force_inline proc "c" (tiledesc: attr_tiledesc_t) -> tile_round_mode_t {
	return tile_round_mode_t((tiledesc.level_and_round >> 4) & 0xf)
}
/** @brief Macro to pack the tiling type and rounding mode into packed structure. */
PACK_TILE_LEVEL_ROUND :: #force_inline proc "c" (lvl: tile_level_mode_t, mode: tile_round_mode_t) -> u8 {
	return ((u8(mode) & 0xf) << 4) | (u8(lvl) & 0xf)
}


/* /////////////////////////////////////// */
/* Now structs that involve heap allocation to store data. */

/** Storage for a string. */
attr_string_t :: struct {
	length: i32,
	/** If this is non-zero, the string owns the data, if 0, is a const ref to a static string. */
	alloc_size: i32,

	str: cstring,
}

/** Storage for a string vector. */
attr_string_vector_t :: struct {
	n_strings: i32,
	/** If this is non-zero, the string vector owns the data, if 0, is a const ref. */
	alloc_size: i32,

	strings: [^]attr_string_t,
}

/** Float vector storage struct. */
attr_float_vector_t :: struct {
	length: i32,
	/** If this is non-zero, the float vector owns the data, if 0, is a const ref. */
	alloc_size: i32,

	arr: [^]f32,
}

/** Hint for lossy compression methods about how to treat values
 * (logarithmic or linear), meaning a human sees values like R, G, B,
 * luminance difference between 0.1 and 0.2 as about the same as 1.0
 * to 2.0 (logarithmic), where chroma coordinates are closer to linear
 * (0.1 and 0.2 is about the same difference as 1.0 and 1.1).
 */
perceptual_treatment_t :: enum c.int {
	LOGARITHMIC = 0,
	LINEAR      = 1,
}

/** Individual channel information. */
attr_chlist_entry_t :: struct {
	name: attr_string_t,
	/** Data representation for these pixels: uint, half, float. */
	pixel_type: pixel_type_t,
	/** Possible values are 0 and 1 per docs perceptual_treatment_t. */
	p_linear: u8,
	reserved: [3]u8,
	x_sampling: i32,
	y_sampling: i32,
}

/** List of channel information (sorted alphabetically). */
attr_chlist_t :: struct {
	num_channels: c.int,
	num_alloced:  c.int,

	entries: [^]attr_chlist_entry_t,
}

/** @brief Struct to define attributes of an embedded preview image. */
attr_preview_t :: struct {
	width: u32,
	height: u32,
	/** If this is non-zero, the preview owns the data, if 0, is a const ref. */
	alloc_size: c.size_t,

	rgba: [^]u8,
}

/** Custom storage structure for opaque data.
 *
 * Handlers for opaque types can be registered, then when a
 * non-builtin type is encountered with a registered handler, the
 * function pointers to unpack/pack it will be set up.
 *
 * @sa register_attr_type_handler
 */
attr_opaquedata_t :: struct {
	size:              i32,
	unpacked_size:     i32,
	/** If this is non-zero, the struct owns the data, if 0, is a const ref. */
	packed_alloc_size: i32,
	pad: [4]u8,

	packed_data: rawptr,

	/** When an application wants to have custom data, they can store
	 * an unpacked form here which will be requested to be destroyed
	 * upon destruction of the attribute.
	 */
	unpacked_data: rawptr,

	/** An application can register an attribute handler which then
	 * fills in these function pointers. This allows a user to delay
	 * the expansion of the custom type until access is desired, and
	 * similarly, to delay the packing of the data until write time.
	 */
	unpack_func_ptr: proc "c" (
		ctxt: context_t,
		data: rawptr,
		attrsize: i32,
		outsize: ^i32,
		outbuffer: ^rawptr) -> result_t,
	pack_func_ptr: proc "c" (
		ctxt: context_t,
		data: rawptr,
		datasize: i32,
		outsize: ^i32,
		outbuffer: rawptr) -> result_t,
	destroy_unpacked_func_ptr: proc "c" (
		ctxt: context_t, data: rawptr, attrsize: i32),
}

/* /////////////////////////////////////// */

/** @brief Built-in/native attribute type enum.
 *
 * This will enable us to do a tagged type struct to generically store
 * attributes.
 */
attribute_type_t :: enum c.int {
	UNKNOWN = 0,    // Type indicating an error or uninitialized attribute.
	BOX2I,          // Integer region definition. @see attr_box2i_t.
	BOX2F,          // Float region definition. @see attr_box2f_t.
	CHLIST,         // Definition of channels in file @see chlist_entry.
	CHROMATICITIES, // Values to specify color space of colors in file @see attr_chromaticities_t.
	COMPRESSION,    // ``u8`` declaring compression present.
	DOUBLE,         // Double precision floating point number.
	ENVMAP,         // ``u8`` declaring environment map type.
	FLOAT,          // Normal (4 byte) precision floating point number.
	FLOAT_VECTOR,   // List of normal (4 byte) precision floating point numbers.
	INT,            // 32-bit signed integer value.
	KEYCODE,        // Struct recording keycode @see attr_keycode_t.
	LINEORDER,      // ``u8`` declaring scanline ordering.
	M33F,           // 9 32-bit floats representing a 3x3 matrix.
	M33D,           // 9 64-bit floats representing a 3x3 matrix.
	M44F,           // 16 32-bit floats representing a 4x4 matrix.
	M44D,           // 16 64-bit floats representing a 4x4 matrix.
	PREVIEW,        // 2 ``unsigned ints`` followed by 4 x w x h ``u8`` image.
	RATIONAL,       // \c int followed by ``unsigned int``
	STRING,         // ``int`` (length) followed by char string data.
	STRING_VECTOR,  // 0 or more text strings (int + string). number is based on attribute size.
	TILEDESC,       // 2 ``unsigned ints`` ``xSize``, ``ySize`` followed by mode.
	TIMECODE,       // 2 ``unsigned ints`` time and flags, user data.
	V2I,            // Pair of 32-bit integers.
	V2F,            // Pair of 32-bit floats.
	V2D,            // Pair of 64-bit floats.
	V3I,            // Set of 3 32-bit integers.
	V3F,            // Set of 3 32-bit floats.
	V3D,            // Set of 3 64-bit floats.
	OPAQUE,         // User/unknown provided type.
}

/** @brief Storage, name and type information for an attribute.
 *
 * Attributes (metadata) for the file cause a surprising amount of
 * overhead. It is not uncommon for a production-grade EXR to have
 * many attributes. As such, the attribute struct is designed in a
 * slightly more complicated manner. It is optimized to have the
 * storage for that attribute: the struct itself, the name, the type,
 * and the data all allocated as one block. Further, the type and
 * standard names may use a static string to avoid allocating space
 * for those as necessary with the pointers pointing to static strings
 * (not to be freed). Finally, small values are optimized for.
 */
attribute_t :: struct {
	/** Name of the attribute. */
	name:             cstring,
	/** String type name of the attribute. */
	type_name:        cstring,
	/** Length of name string (short flag is 31 max, long allows 255). */
	name_length:      u8,
	/** Length of type string (short flag is 31 max, long allows 255). */
	type_name_length: u8,

	pad: [2]u8,

	/** Enum of the attribute type. */
	type: attribute_type_t,

	/** Union of pointers of different types that can be used to type
	 * pun to an appropriate type for builtins. Do note that while
	 * this looks like a big thing, it is only the size of a single
	 * pointer.  These are all pointers into some other data block
	 * storing the value you want, with the exception of the pod types
	 * which are just put in place (i.e. small value optimization).
	 *
	 * The attribute type \c type should directly correlate to one
	 * of these entries.
	 */
	using _: struct #raw_union {
		// NB: not pointers for POD types
		uc: u8,
		d: f64,
		f: f32,
		i: i32,

		box2i:          ^attr_box2i_t,
		box2f:          ^attr_box2f_t,
		chlist:         ^attr_chlist_t,
		chromaticities: ^attr_chromaticities_t,
		keycode:        ^attr_keycode_t,
		floatvector:    ^attr_float_vector_t,
		m33f:           ^attr_m33f_t,
		m33d:           ^attr_m33d_t,
		m44f:           ^attr_m44f_t,
		m44d:           ^attr_m44d_t,
		preview:        ^attr_preview_t,
		rational:       ^attr_rational_t,
		string:         ^attr_string_t,
		stringvector:   ^attr_string_vector_t,
		tiledesc:       ^attr_tiledesc_t,
		timecode:       ^attr_timecode_t,
		v2i:            ^attr_v2i_t,
		v2f:            ^attr_v2f_t,
		v2d:            ^attr_v2d_t,
		v3i:            ^attr_v3i_t,
		v3f:            ^attr_v3f_t,
		v3d:            ^attr_v3d_t,
		opaque:         ^attr_opaquedata_t,
		rawptr:         ^u8,
	},
}