"""Type stubs for the C++ core module."""

from __future__ import annotations

import numpy as np
from numpy.typing import NDArray

class ImageSize:
    width: int
    height: int
    def __init__(self, width: int, height: int) -> None: ...
    def half_width(self) -> float: ...
    def half_height(self) -> float: ...
    def __repr__(self) -> str: ...

# 1D pixel-tangent conversions
def pixel_tan_from_fov(pixel: int, width: int, height: int, fov_rad: float) -> float: ...
def tan_to_pixel_by_fov(pixel_tan: float, width: int, height: int, fov_rad: float) -> int: ...
def pixel_tan_by_pixel_to_tan(pixel: int, width: int, height: int, pixel_to_tan: float) -> float: ...
def angle_tan_to_pixel(angle_tan: float, width: int, height: int, pixel_to_tan: float) -> int: ...
def pixel_tan_by_pixel_to_tan_clipped(
    pixel: int, width: int, height: int, pixel_to_tan: float, threshold: float
) -> float: ...
def tan_to_pixel_by_pixel_to_tan(
    pixel_tan: float, width: int, height: int, pixel_to_tan: float, round_back: bool = ...
) -> int: ...
def pixel_to_tan_from_fov(width: int, height: int, fov_rad: float) -> float: ...

# Body space
def cam_to_body_from_angle(angle_rad: float) -> NDArray[np.float64]: ...
def tangents_to_ned(w_tan: float, h_tan: float) -> NDArray[np.float64]: ...
def ned_to_tangents(ned: NDArray[np.float64]) -> tuple[float, float]: ...
def ned_to_azimuth_elevation(ned: NDArray[np.float64]) -> tuple[float, float]: ...
def azimuth_elevation_to_ned(azimuth: float, elevation: float) -> NDArray[np.float64]: ...
def warp_image_to_body(
    w_tan: float, h_tan: float, cam_to_body: NDArray[np.float64]
) -> NDArray[np.float64]: ...
def warp_body_to_image(
    dir_body: NDArray[np.float64], cam_to_body: NDArray[np.float64]
) -> tuple[float, float]: ...
def pixel_to_ned(
    row: int, col: int, width: int, height: int,
    pixel_to_tan: float,
    cam_to_body: NDArray[np.float64], attitude: NDArray[np.float64],
) -> NDArray[np.float64]: ...
def ned_to_pixel(
    dir_ned: NDArray[np.float64], width: int, height: int,
    pixel_to_tan: float,
    cam_to_body: NDArray[np.float64], attitude: NDArray[np.float64],
) -> tuple[int, int]: ...
def pixel_after_rotation(
    row: int, col: int, width: int, height: int,
    pixel_to_tan: float,
    cam_to_body: NDArray[np.float64],
    q_old: NDArray[np.float64], q_new: NDArray[np.float64],
    round_back: bool = ...,
) -> tuple[int, int]: ...
def is_pixel_inside_frame(
    row: int, col: int, width: int, height: int, boundary: float
) -> bool: ...
def is_ned_inside_frame(
    dir_ned: NDArray[np.float64], width: int, height: int,
    pixel_to_tan: float,
    cam_to_body: NDArray[np.float64], attitude: NDArray[np.float64],
    boundary: float,
) -> bool: ...
def pixel_at_elevation(
    row: int, col: int, width: int, height: int,
    pixel_to_tan: float,
    cam_to_body: NDArray[np.float64], attitude: NDArray[np.float64],
    desired_elevation: float,
) -> tuple[int, int]: ...
def ned_angle_in_pixels(
    ned1: NDArray[np.float64], ned2: NDArray[np.float64], pixel_to_tan: float
) -> float: ...

# Batch operations
def pixel_to_ned_batch(
    rows: NDArray[np.uint64], cols: NDArray[np.uint64],
    width: int, height: int, pixel_to_tan: float,
    cam_to_body: NDArray[np.float64], attitude: NDArray[np.float64],
) -> NDArray[np.float64]: ...
def ned_to_pixel_batch(
    dirs_ned: NDArray[np.float64], width: int, height: int,
    pixel_to_tan: float,
    cam_to_body: NDArray[np.float64], attitude: NDArray[np.float64],
) -> NDArray[np.uint64]: ...
def pixel_after_rotation_batch(
    rows: NDArray[np.uint64], cols: NDArray[np.uint64],
    width: int, height: int, pixel_to_tan: float,
    cam_to_body: NDArray[np.float64],
    q_old: NDArray[np.float64], q_new: NDArray[np.float64],
    round_back: bool = ...,
) -> NDArray[np.uint64]: ...
def warp_image_to_body_batch(
    w_tans: NDArray[np.float64], h_tans: NDArray[np.float64],
    cam_to_body: NDArray[np.float64],
) -> NDArray[np.float64]: ...
