from collections.abc import Callable, Iterator
from concurrent.futures import Future
from fractions import Fraction
from typing import IO, Any, Final, Literal, Self, overload
from warnings import deprecated

from ._typing import _IntLike, _VSValue
from .constants import FilterMode, MessageType, SampleType, VapourSynthAPIVersion, VapourSynthVersion
from .env import CoreTimings
from .formats import ChannelLayout, VideoFormat
from .frames import AudioFrame, RawFrame, VideoFrame
from .logging import LogHandle
from .plugin import Plugin

class RawNode:
    def __repr__(self) -> str: ...
    def __str__(self) -> str: ...
    def __eq__(self, other: object) -> bool: ...
    def __hash__(self) -> int: ...
    def __dir__(self) -> list[str]: ...
    def __getitem__(self, index: int | slice[int | None, int | None, int | None]) -> Self: ...
    def __len__(self) -> int: ...
    def __add__(self, other: Self) -> Self: ...
    def __mul__(self, other: int) -> Self: ...
    def __getattr__(self, name: str) -> Plugin: ...
    @property
    def node_name(self) -> str: ...
    @property
    def timings(self) -> int: ...
    @timings.setter
    def timings(self, value: Literal[0]) -> None: ...
    @property
    def mode(self) -> FilterMode: ...
    @property
    def dependencies(self) -> tuple[Self, ...]: ...
    @property
    def _name(self) -> str: ...
    @property
    def _inputs(self) -> dict[str, _VSValue]: ...
    def get_frame(self, n: _IntLike) -> RawFrame: ...
    @overload
    def get_frame_async(self, n: _IntLike) -> Future[RawFrame]: ...
    @overload
    def get_frame_async(self, n: _IntLike, cb: Callable[[RawFrame | None, Exception | None], None]) -> None: ...
    def frames(
        self, prefetch: int | None = None, backlog: int | None = None, close: bool = False
    ) -> Iterator[RawFrame]: ...
    def set_output(self, index: _IntLike = 0) -> None: ...
    def clear_cache(self) -> None: ...
    def is_inspectable(self, version: int | None = None) -> bool: ...

type _CurrentFrame = int
type _TotalFrames = int

# Behave like a Sequence
class VideoNode(RawNode):
    format: Final[VideoFormat]
    width: Final[int]
    height: Final[int]
    num_frames: Final[int]
    fps_num: Final[int]
    fps_den: Final[int]
    fps: Final[Fraction]
    def get_frame(self, n: _IntLike) -> VideoFrame: ...
    @overload  # type: ignore[override]
    def get_frame_async(self, n: _IntLike) -> Future[VideoFrame]: ...
    @overload
    def get_frame_async(  # pyright: ignore[reportIncompatibleMethodOverride]
        self, n: _IntLike, cb: Callable[[VideoFrame | None, Exception | None], None]
    ) -> None: ...
    def frames(
        self, prefetch: int | None = None, backlog: int | None = None, close: bool = False
    ) -> Iterator[VideoFrame]: ...
    def set_output(self, index: _IntLike = 0, alpha: Self | None = None, alt_output: Literal[0, 1, 2] = 0) -> None: ...
    def output(
        self,
        fileobj: IO[bytes],
        y4m: bool = False,
        progress_update: Callable[[_CurrentFrame, _TotalFrames], None] | None = None,
        prefetch: int = 0,
        backlog: int = -1,
    ) -> None: ...

# <plugins/bound/VideoNode>
# </plugins/bound/VideoNode>

# Behave like a Sequence
class AudioNode(RawNode):
    sample_type: Final[SampleType]
    bits_per_sample: Final[int]
    bytes_per_sample: Final[int]
    channel_layout: Final[int]
    num_channels: Final[int]
    sample_rate: Final[int]
    num_samples: Final[int]
    num_frames: Final[int]
    @property
    def channels(self) -> ChannelLayout: ...
    def get_frame(self, n: _IntLike) -> AudioFrame: ...
    @overload  # type: ignore[override]
    def get_frame_async(self, n: _IntLike) -> Future[AudioFrame]: ...
    @overload
    def get_frame_async(  # pyright: ignore[reportIncompatibleMethodOverride]
        self, n: _IntLike, cb: Callable[[AudioFrame | None, Exception | None], None]
    ) -> None: ...
    def frames(
        self, prefetch: int | None = None, backlog: int | None = None, close: bool = False
    ) -> Iterator[AudioFrame]: ...

# <plugins/bound/AudioNode>
# </plugins/bound/AudioNode>

class Core:
    timings: Final[CoreTimings]
    def __repr__(self) -> str: ...
    def __str__(self) -> str: ...
    def __dir__(self) -> list[str]: ...
    def __getattr__(self, name: str) -> Plugin: ...
    @property
    def api_version(self) -> VapourSynthAPIVersion: ...
    @property
    def core_version(self) -> VapourSynthVersion: ...
    @property
    def num_threads(self) -> int: ...
    @num_threads.setter
    def num_threads(self, value: _IntLike) -> None: ...
    @property
    def max_cache_size(self) -> int: ...
    @max_cache_size.setter
    def max_cache_size(self, mb: _IntLike) -> None: ...
    @property
    def used_cache_size(self) -> int: ...
    @property
    def flags(self) -> int: ...
    def plugins(self) -> Iterator[Plugin]: ...
    def query_video_format(
        self,
        color_family: _IntLike,
        sample_type: _IntLike,
        bits_per_sample: _IntLike,
        subsampling_w: _IntLike = 0,
        subsampling_h: _IntLike = 0,
    ) -> VideoFormat: ...
    def get_video_format(self, id: _IntLike) -> VideoFormat: ...
    def create_video_frame(self, format: VideoFormat, width: _IntLike, height: _IntLike) -> VideoFrame: ...
    def log_message(self, message_type: _IntLike, message: str) -> None: ...
    def add_log_handler(self, handler_func: Callable[[MessageType, str], None]) -> LogHandle: ...
    def remove_log_handler(self, handle: LogHandle) -> None: ...
    def clear_cache(self) -> None: ...
    @deprecated("core.version() is deprecated, use str(core)!", category=DeprecationWarning)
    def version(self) -> str: ...
    @deprecated(
        "core.version_number() is deprecated, use core.core_version.release_major!", category=DeprecationWarning
    )
    def version_number(self) -> int: ...

# <plugins/bound/Core>
# </plugins/bound/Core>

# _CoreProxy doesn't inherit from Core but __getattr__ returns the attribute from the actual core
class _CoreProxy(Core):
    def __setattr__(self, name: str, value: Any) -> None: ...
    @property
    def core(self) -> Core: ...

core: _CoreProxy

# <plugins/implementations>
# </plugins/implementations>
