import type { HybridObject } from 'react-native-nitro-modules';
import type { CameraOrientation } from '../common-types/CameraOrientation';
import type { NativeBuffer } from '../common-types/NativeBuffer';
import type { PixelFormat } from '../common-types/PixelFormat';
import type { Point } from '../common-types/Point';
/**
 * Represents a single Plane of a **planar** {@linkcode Frame}.
 *
 * @see {@linkcode Frame.getPlanes | Frame.getPlanes()}
 */
export interface FramePlane extends HybridObject<{
    ios: 'swift';
    android: 'kotlin';
}> {
    /**
     * Returns the width of this Plane.
     *
     * @note If this Plane (or its parent Frame) is invalid ({@linkcode isValid}),
     * this just returns `0`.
     */
    readonly width: number;
    /**
     * Returns the height of this Plane.
     *
     * @note If this Plane (or its parent Frame) is invalid ({@linkcode isValid}),
     * this just returns `0`.
     */
    readonly height: number;
    /**
     * Get the number of bytes per row of the
     * underlying pixel buffer.
     *
     * @see {@linkcode width}
     * @see {@linkcode getPixelBuffer | getPixelBuffer()}
     */
    readonly bytesPerRow: number;
    /**
     * Gets whether this {@linkcode FramePlane} (or its parent {@linkcode Frame})
     * is still valid, or not.
     *
     * If the Plane is invalid, you cannot access its data anymore.
     * A Plane is automatically invalidated via {@linkcode HybridObject.dispose | dispose()}.
     */
    readonly isValid: boolean;
    /**
     * Gets the {@linkcode FramePlane}'s pixel data as an `ArrayBuffer`.
     *
     * @discussion
     * This does **not** perform a copy, but since the {@linkcode Frame}'s data is stored
     * on the GPU, it might lazily perform a GPU -> CPU download.
     *
     * @discussion
     * Once the {@linkcode FramePlane} gets invalidated ({@linkcode isValid} == false),
     * this ArrayBuffer is no longer safe to access.
     *
     * @note If this Plane (or its parent Frame) is invalid ({@linkcode isValid}),
     * this method throws.
     */
    getPixelBuffer(): ArrayBuffer;
}
/**
 * Represents a single Frame the Camera "sees".
 *
 * @discussion
 * Typically, a {@linkcode CameraOutput} like
 * {@linkcode CameraFrameOutput} produces
 * instances of this type.
 *
 * @discussion
 * The Frame is either **planar** or **non-planar** ({@linkcode isPlanar}).
 * - A **planar** Frame's (often YUV) pixel data can be accessed on
 * the CPU via {@linkcode getPlanes | Frame.getPlanes()}, where each
 * {@linkcode FramePlane} represents one plane of the pixel data. In YUV,
 * this is often 1 **Y** Plane in full resolution, and 1 **UV** Plane in half
 * resolution. In this case, {@linkcode getPixelBuffer | Frame.getPixelBuffer()}'s
 * behaviour is undefined - sometimes it contains the whole pixel data in a
 * contiguous block of memory, sometimes it just contains the data in the control block.
 * - A **non-planar** Frame's (often RGB) pixel data can be accessed
 * on the CPU via {@linkcode getPixelBuffer | Frame.getPixelBuffer()} as one
 * contiguous block of memory. In this case, {@linkcode getPlanes | Frame.getPlanes()}
 * will return an empty array (`[]`).
 *
 * It is recommended to not rely on {@linkcode getPixelBuffer | Frame.getPixelBuffer()}
 * for **planar** Frames.
 *
 * @discussion
 * Frames have to be disposed (see {@linkcode Frame.dispose | dispose()})
 * to free up the memory, otherwise the producing pipeline
 * might stall.
 */
export interface Frame extends HybridObject<{
    ios: 'swift';
    android: 'kotlin';
}> {
    /**
     * Gets the presentation timestamp this Frame was timed at.
     *
     * @note If this Frame is invalid ({@linkcode isValid}), this just returns `0`.
     */
    readonly timestamp: number;
    /**
     * Gets whether this Frame is still valid, or not.
     * If the Frame is invalid, you cannot access its data anymore.
     * A Frame is automatically invalidated via {@linkcode HybridObject.dispose | dispose()}.
     */
    readonly isValid: boolean;
    /**
     * Gets the total width of the Frame.
     *
     * If this is a planar Frame (see {@linkcode isPlanar}),
     * the individual planes (see {@linkcode getPlanes | getPlanes()})
     * will likely have different widths than the total width.
     * For example, a YUV Frame's UV Plane is half the size of its Y Plane.
     *
     * @note If this Frame is invalid ({@linkcode isValid}), this just returns `0`.
     */
    readonly width: number;
    /**
     * Gets the total height of the Frame.
     *
     * If this is a planar Frame (see {@linkcode isPlanar}),
     * the individual planes (see {@linkcode getPlanes | getPlanes()})
     * will likely have different heights than the total height.
     * For example, a YUV Frame's UV Plane is half the size of its Y Plane.
     *
     * @note If this Frame is invalid ({@linkcode isValid}), this just returns `0`.
     */
    readonly height: number;
    /**
     * Get the number of bytes per row of the
     * underlying pixel buffer.
     *
     * This may return `0` if the {@linkcode Depth}
     * is planar, in which case you should get the
     * number of bytes per row of individual planes
     * using {@linkcode getPlanes | getPlanes()} /
     * {@linkcode FramePlane.bytesPerRow}.
     *
     * @see {@linkcode width}
     * @see {@linkcode getPixelBuffer | getPixelBuffer()}
     */
    readonly bytesPerRow: number;
    /**
     * Gets the {@linkcode PixelFormat} of this Frame's pixel data.
     * Common formats are {@linkcode PixelFormat | 'yuv-420-8-bit-video'}
     * for native YUV Frames, {@linkcode PixelFormat | 'rgb-bgra-32-bit'}
     * for processed RGB Frames, or {@linkcode PixelFormat | 'private'} for
     * zero-copy GPU-only Frames.
     *
     * @note
     * Frames are usually not in Depth Pixel Formats (like
     * {@linkcode PixelFormat | 'depth-32-bit'}) unless they
     * have been manually converted from a {@linkcode Depth} Frame to
     * a {@linkcode Frame}.
     *
     * @discussion
     * If the {@linkcode Frame} is a GPU-only buffer, its {@linkcode pixelFormat}
     * is {@linkcode PixelFormat | 'private'}, wich allows zero-copy importing it
     * into GPU pipelines directly, however its pixel data is likely not accessible
     * on the CPU.
     * You can use {@linkcode getNativeBuffer | getNativeBuffer()} to get a handle
     * to the native GPU-based buffer, which can be used in GPU pipelines like
     * Skia, or custom implementations using OpenGL/Vulkan/Metal shaders with
     * external texture importers.
     *
     * @note If this Frame is invalid ({@linkcode isValid}), this just returns
     * {@linkcode PixelFormat | 'unknown'}.
     */
    readonly pixelFormat: PixelFormat;
    /**
     * The rotation of this {@linkcode Frame} relative to the
     * {@linkcode CameraOutput}'s target output orientation. (see
     * {@linkcode CameraOutput.outputOrientation})
     *
     * Frames are not automatically rotated to `'up'` because physically
     * rotating buffers is expensive. The Camera streams frames in the
     * hardware's native orientation and adjusts presentation later using
     * metadata (such as EXIF), transforms, or here; a flag.
     *
     * Examples:
     * - `'up'` — The Frame is already correctly oriented.
     * - `'right'` — The pixel data is rotated +90° relative to the desired orientation.
     * - `'left'` — The pixel data is rotated -90° relative to the desired orientation.
     * - `'down'` — The pixel data is rotated 180° upside down relative to the desired orientation.
     *
     * @discussion
     * If you process the Frame, **you** must interpret the
     * pixel data in this {@linkcode Frame} to be rotated by
     * this `orientation`.
     * In rendering libraries you would typically counter-rotate
     * the {@linkcode Frame} by this `orientation` to get
     * it "up-right".
     *
     * Most ML libraries (for example Google MLKit) accept an
     * orientation flag for input images - pass `orientation`
     * directly in those cases.
     */
    readonly orientation: CameraOrientation;
    /**
     * Indicates whether this {@linkcode Frame}'s pixel data must be
     * interpreted as mirrored along the vertical axis.
     *
     * This value is always relative to the target output's mirror mode.
     * (see {@linkcode CameraOutputConfiguration.mirrorMode})
     *
     * Frames are not automatically mirroring their pixels
     * because physically mirroring buffers is expensive.
     * The Camera streams frames in the hardware's native
     * mirroring mode and adjusts presentation later using
     * metadata (such as EXIF), transforms, or here; a flag.
     *
     * - If the output is mirrored but the underlying pixel buffer is NOT,
     *   `isMirrored` will be `true`. You must treat the pixels as flipped
     *   (for example, read them right-to-left).
     * - If both the output and the pixel buffer are mirrored
     *   (for example when {@linkcode FrameOutputOptions.enablePhysicalBufferRotation}
     *   is enabled), `isMirrored` will be `false` because the buffer already
     *   matches the output orientation.
     * - If neither the output nor the pixel buffer are mirrored,
     *   `isMirrored` will be `false`.
     *
     * @discussion
     * If you process the Frame, **you** must interpret the
     * pixel data in this {@linkcode Frame} as mirrored if
     * `isMirrored` is true.
     * In rendering libraries you would typically scale the
     * {@linkcode Frame} by `-1` on the X axis if it is mirrored
     * to cancel out the mirroring at render time.
     *
     * Most ML libraries (for example Google MLKit) accept a mirroring flag for
     * input images — pass `isMirrored` directly in those cases.
     */
    readonly isMirrored: boolean;
    /**
     * Returns whether this {@linkcode Frame} is **planar** or **non-planar**.
     * - If a Frame is **planar** (e.g. YUV), you should only access its pixel buffer
     * via {@linkcode getPlanes | getPlanes()} instead of {@linkcode getPixelBuffer | getPixelBuffer()}.
     * - If a Frame is **non-planar** (e.g. RGB), you can access its pixel buffer
     * via {@linkcode getPixelBuffer | getPixelBuffer()} instead of {@linkcode getPlanes | getPlanes()}.
     *
     * @note If this Frame is invalid ({@linkcode isValid}), this just returns `false`.
     */
    readonly isPlanar: boolean;
    /**
     * Get whether this {@linkcode Frame} has a readable Pixel Buffer
     * attached to it.
     *
     * @discussion
     * Usually a {@linkcode Frame} has an application-accessible Pixel Buffer
     * if its {@linkcode pixelFormat} is application-accessible - aka
     * every {@linkcode PixelFormat} except for {@linkcode PixelFormat | 'private'}.
     * On iOS, every Frame has an application-accessible Pixel Buffer.
     *
     * @see {@linkcode getPixelBuffer | getPixelBuffer()}
     */
    readonly hasPixelBuffer: boolean;
    /**
     * Get whether this {@linkcode Frame} has a Native Buffer
     * attached to it.
     *
     * @discussion
     * Usually a {@linkcode Frame} has a Native Buffer if
     * its {@linkcode pixelFormat} is application-accessible - aka
     * every {@linkcode PixelFormat} except for {@linkcode PixelFormat | 'private'}.
     * On iOS, every Frame has a Native Buffer.
     *
     * @see {@linkcode getNativeBuffer | getNativeBuffer()}
     * @see {@linkcode NativeBuffer}
     */
    readonly hasNativeBuffer: boolean;
    /**
     * Returns each plane of a **planar** Frame (see {@linkcode isPlanar}).
     * If this Frame is **non-planar**, this method returns an empty array (`[]`).
     *
     * @throws If this Frame is invalid ({@linkcode isValid}).
     */
    getPlanes(): FramePlane[];
    /**
     * Gets the {@linkcode Frame}'s entire pixel data as a full contiguous `ArrayBuffer`,
     * if it contains one.
     *
     * @discussion
     * - If the frame is planar (see {@linkcode isPlanar | Frame.isPlanar}, e.g. YUV), this
     * might or might not contain valid data.
     * - If the frame is **not** planar (e.g. RGB), this will contain the entire pixel data.
     *
     * @discussion
     * This does **not** perform a copy, but since the {@linkcode Frame}'s data is stored
     * on the GPU, it might lazily perform a GPU -> CPU download.
     *
     * @discussion
     * Once the {@linkcode Frame} gets invalidated ({@linkcode isValid} == false),
     * this ArrayBuffer is no longer safe to access.
     *
     * @throws If this Frame is invalid ({@linkcode isValid}) or
     * {@linkcode hasPixelBuffer | hasPixelBuffer} is false.
     *
     * @example
     * ```ts
     * if (frame.hasPixelBuffer) {
     *   const pixelBuffer = frame.getPixelBuffer()
     *   console.log(`Frame has ${pixelBuffer.byteLength} bytes.`)
     * }
     * ```
     */
    getPixelBuffer(): ArrayBuffer;
    /**
     * Get a {@linkcode NativeBuffer} that points to
     * this {@linkcode Frame}.
     *
     * This is a shared contract between libraries to pass
     * native buffers around without natively typed bindings.
     *
     * The {@linkcode NativeBuffer} must be released
     * again by its consumer via {@linkcode NativeBuffer.release | release()},
     * otherwise the Camera pipeline might stall.
     *
     * @throws If {@linkcode hasNativeBuffer | hasNativeBuffer} is false.
     *
     * @example
     * ```ts
     * if (frame.hasNativeBuffer) {
     *   const nativeBuffer = frame.getNativeBuffer()
     *   console.log(`Native Buffer pointer: ${nativeBuffer.pointer}`)
     *   nativeBuffer.release()
     * }
     * ```
     */
    getNativeBuffer(): NativeBuffer;
    /**
     * Gets the Camera intrinsic matrix for this Frame.
     *
     * The returned array is a 3x3 matrix with column-major ordering.
     * Its origin is the top-left of the Frame.
     *
     * ```
     * K = [ fx   0  cx ]
     *     [  0  fy  cy ]
     *     [  0   0   1 ]
     * ```
     * - `fx`, `fy`: focal length in pixels
     * - `cx`, `cy`: principal point in pixels
     *
     *
     * @platform iOS
     * @note The {@linkcode Frame} only has a Camera intrinsic matrix attached
     * if {@linkcode FrameOutputOptions.enableCameraMatrixDelivery | enableCameraMatrixDelivery}
     * is set to true on the `CameraFrameOutput`.
     * @example
     * ```ts
     * const matrix = frame.cameraIntrinsicMatrix
     * const fx = matrix[0]
     * const fy = matrix[4]
     * ```
     */
    readonly cameraIntrinsicMatrix?: number[];
    /**
     * Converts the given {@linkcode cameraPoint} in
     * camera sensor coordinates into a {@linkcode Point}
     * in Frame coordinates, relative to this {@linkcode Frame}.
     *
     * @note Camera sensor coordinates are not necessarily normalized from `0.0` to `1.0`. Some implementations may have a different opaque coordinate system.
     * @example
     * ```ts
     * const cameraPoint = { x: 0.5, y: 0.5 }
     * const framePoint = frame.convertCameraPointToFramePoint(cameraPoint)
     * console.log(framePoint) // { x: 960, y: 360 }
     * ```
     */
    convertCameraPointToFramePoint(cameraPoint: Point): Point;
    /**
     * Converts the given {@linkcode framePoint} in
     * Frame coordinates relative to this {@linkcode Frame}
     * into a {@linkcode Point} in camera sensor coordinates.
     *
     * @note Camera sensor coordinates are not necessarily normalized from `0.0` to `1.0`. Some implementations may have a different opaque coordinate system.
     * @example
     * ```ts
     * const framePoint = { x: frame.width / 2, y: frame.height / 2 }
     * const cameraPoint = frame.convertFramePointToCameraPoint(framePoint)
     * console.log(cameraPoint) // { x: 0.5, y: 0.5 }
     * ```
     */
    convertFramePointToCameraPoint(framePoint: Point): Point;
}
