import os
import typing

import numpy as np

EPR_C_API_VERSION: str
E_SMID_LIN: int
E_SMID_LOG: int
E_SMID_NON: int
E_SMOD_1OF1: int
E_SMOD_1OF2: int
E_SMOD_2OF2: int
E_SMOD_2TOF: int
E_SMOD_3TOI: int
E_TID_CHAR: int
E_TID_DOUBLE: int
E_TID_FLOAT: int
E_TID_INT: int
E_TID_SHORT: int
E_TID_SPARE: int
E_TID_STRING: int
E_TID_TIME: int
E_TID_UCHAR: int
E_TID_UINT: int
E_TID_UNKNOWN: int
E_TID_USHORT: int

class EPRTime(typing.NamedTuple):
    days: int
    seconds: int
    microseconds: int

MJD: np.dtype

_EPR_MAGIC_FIELD: int
_EPR_MAGIC_RASTER: int
_EPR_MAGIC_RECORD: int
_EPR_MAGIC_BAND_ID: int
_EPR_MAGIC_PRODUCT_ID: int

class EPRError(Exception):
    def __init__(
        self, message: str = ..., code: int = ..., *args, **kargs
    ) -> None: ...

class EPRValueError(EPRError, ValueError): ...
class EprObject: ...

def get_numpy_dtype(type_id: int) -> type | np.dtype | None: ...
def get_data_type_size(type_id: int) -> int: ...
def data_type_id_to_str(type_id: int) -> str: ...
def get_scaling_method_name(method: int) -> str: ...
def get_sample_model_name(model: int) -> str: ...

class DSD(EprObject):
    def __eq__(self, other: object) -> bool: ...
    def __ge__(self, other: object) -> bool: ...
    def __gt__(self, other: object) -> bool: ...
    def __le__(self, other: object) -> bool: ...
    def __lt__(self, other: object) -> bool: ...
    def __ne__(self, other: object) -> bool: ...

class Field(EprObject):
    tot_size: int

    def get_description(self) -> str: ...
    def get_elem(self, index: int = ...): ...
    def get_elems(self) -> np.ndarray: ...
    def get_name(self) -> str: ...
    def get_num_elems(self) -> int: ...
    def get_offset(self) -> int: ...
    def get_type(self) -> int: ...
    def get_unit(self) -> str: ...
    def print(self, ostream=...) -> None: ...
    def set_elem(self, elem, index=...) -> None: ...
    def set_elems(self, elems) -> None: ...
    def __eq__(self, other: object) -> bool: ...
    def __ge__(self, other: object) -> bool: ...
    def __gt__(self, other: object) -> bool: ...
    def __le__(self, other: object) -> bool: ...
    def __len__(self) -> int: ...
    def __lt__(self, other: object) -> bool: ...
    def __ne__(self, other: object) -> bool: ...

class Record(EprObject):
    dataset_name: str
    tot_size: int
    index: int

    def fields(self) -> list[Field]: ...
    def get_field(self, name: str) -> Field: ...
    def get_field_at(self, index: str) -> Field: ...
    def get_field_names(self) -> list[str]: ...
    def get_num_fields(self) -> int: ...
    def get_offset(self) -> int | None: ...
    def print(self, ostream=...) -> None: ...
    def print_element(
        self, field_index: int, element_index: int, ostream=...
    ) -> None: ...
    def __iter__(self) -> typing.Generator[Field]: ...

class Raster(EprObject):
    data_type: int
    source_width: int
    source_height: int
    source_step_x: int
    source_step_y: int
    data: np.ndarray

    def get_elem_size(self) -> int: ...
    def get_height(self) -> int: ...
    def get_pixel(self, x: int, y: int): ...
    def get_width(self) -> int: ...
    def toarray(self) -> np.ndarray: ...

def create_raster(
    data_type: int,
    src_width: int,
    src_height: int,
    xstep: int = ...,
    ystep: int = ...,
) -> Raster: ...
def create_bitmask_raster(
    src_width: int, src_height: int, xstep: int = ..., ystep: int = ...
) -> Raster: ...

class Band(EprObject):
    product: Product
    spectr_band_index: int
    sample_model: int
    data_type: int
    scaling_method: int
    scaling_offset: float
    scaling_factor: float
    bm_expr: str | None
    unit: str | None
    description: str
    lines_mirrored: bool
    dataset: Dataset

    def create_compatible_raster(
        self,
        src_width: int = ...,
        src_height: int = ...,
        xstep: int = ...,
        ystep: int = ...,
    ) -> Raster: ...
    def get_name(self) -> str: ...
    def read_as_array(
        self,
        width: int = ...,
        height: int = ...,
        xoffset: int = ...,
        yoffset: int = ...,
        xstep: int = ...,
        ystep: int = ...,
    ) -> np.ndarray: ...
    def read_raster(
        self,
        xoffset: int = ...,
        yoffset: int = ...,
        raster: Raster | None = ...,
    ) -> Raster: ...

class Dataset(EprObject):
    product: Product
    description: str

    def create_record(self) -> Record: ...
    def get_dsd(self) -> DSD: ...
    def get_dsd_name(self) -> str: ...
    def get_name(self) -> str: ...
    def get_num_records(self) -> int: ...
    def read_record(
        self, index: int, record: Record | None = ...
    ) -> Record: ...
    def records(self) -> list[Record]: ...
    def __iter__(self) -> typing.Generator[Record]: ...

class Product(EprObject):
    file_path: str
    mode: str
    tot_size: int
    id_string: str
    meris_iodd_version: int
    closed: bool

    def __init__(
        self, filename: str | os.PathLike[str], mode: str = ...
    ) -> None: ...
    def bands(self) -> list[Band]: ...
    def close(self) -> None: ...
    def datasets(self) -> list[Dataset]: ...
    def flush(self) -> None: ...
    def get_band(self, name: str) -> Band: ...
    def get_band_at(self, index: int) -> Band: ...
    def get_band_names(self) -> list[str]: ...
    def get_dataset(self, name: str) -> Dataset: ...
    def get_dataset_at(self, index) -> Dataset: ...
    def get_dataset_names(self) -> list[str]: ...
    def get_dsd_at(self, index: int) -> DSD: ...
    def get_mph(self) -> Record: ...
    def get_num_bands(self) -> int: ...
    def get_num_datasets(self) -> int: ...
    def get_num_dsds(self) -> int: ...
    def get_scene_height(self) -> int: ...
    def get_scene_width(self) -> int: ...
    def get_sph(self) -> Record: ...
    def read_bitmask_raster(
        self, bm_expr: str, xoffset: int, yoffset: int, raster: Raster
    ) -> Raster: ...
    def get_complex_band_as_array(
        self, i_band_name: str, q_band_name: str, *, strict: bool = ...
    ) -> np.ndarray: ...

def open(  # noqa: A001
    filename: str | os.PathLike[str], mode: str = ...
) -> Product: ...
