from __future__ import annotations
from dataclasses import dataclass
from typing import Any, Optional
import enum
import PySide6.QtGui
from ovito.vis import Viewport
import ovito.pipeline

@dataclass(kw_only=True)
class PipelineStatus:

    class Type(enum.Enum):
        Success = enum.auto()
        Warning = enum.auto()
        Error = enum.auto()
    type: PipelineStatus.Type
    text: str

@dataclass(kw_only=True)
class ActiveObject:
    status: PipelineStatus
    enabled: bool
    title: str

@dataclass(kw_only=True)
class AnimationSettings:
    frames_per_second: float
    'frames_per_second() -> float\n\nControls the playback speed of the animation. This parameter also determines the playback speed of movie files generated by OVITO.\n\nDefault: ``10.0``'
    current_frame: int
    "current_frame() -> int\n\nThe current animation frame. This parameter controls the position of the time slider in OVITO's main window and determines which animation frame is shown in the viewports. It also affects various other functions that operate on the current animation frame.\n\nDefault: ``0``"
    last_frame: int
    'last_frame() -> int\n\nThe zero-based index of the last animation frame. You can set this property to change the animation length. \n\nDefault: ``0``'
    first_frame: int
    'first_frame() -> int\n\nThe zero-based index of the first animation frame.\n\nDefault: ``0``'
    auto_adjust_interval: bool
    'auto_adjust_interval() -> bool\n\nControls whether the animation interval (:py:attr:`first_frame`/:py:attr:`last_frame`) is automatically adjusted to accommodate all trajectory frames produced by the data pipelines in the :py:class:`~ovito.Scene`. When enabled, loading a new simulation trajectory or importing an additional trajectory automatically extends :py:attr:`last_frame` to cover its full length.\n\nIn other words, if this option is enabled, the animation interval always covers the maximum `Pipeline.num_frames` of all data pipelines in the current scene. If this option is disabled, the animation interval is not automatically adjusted, and you can set :py:attr:`first_frame` and :py:attr:`last_frame` manually to any values you like, even if they are outside the range of the loaded simulation trajectory.\n\nDefault: ``True``'
    play_every_nth_frame: int
    'play_every_nth_frame() -> int\n\nSpecifies the number of animation frames to advance per step during interactive playback in the viewports. A value of 1 plays every frame; a value of *n* skips ahead by *n* frames on each playback step, which can be used to move through long trajectories more quickly at the cost of temporal resolution. This setting has no effect on movie export.\n\nDefault: ``1``'
    loop_playback: bool
    'loop_playback() -> bool\n\nControls whether interactive playback in the viewports restarts from :py:attr:`first_frame` after reaching :py:attr:`last_frame`, instead of stopping there.\n\nDefault: ``True``'
    playback_speed: int
    'playback_speed() -> int\n\nAn integer speed factor controlling the rate of interactive playback in the viewports, using an asymmetric encoding: values greater than 1 play the animation faster than realtime, at *playback_speed* times the normal rate; values less than -1 play it slower than realtime, at a rate of 1 / abs(playback_speed) times the normal rate. This setting has no effect on movie export, which is governed by :py:attr:`frames_per_second` instead.\n\nDefault: ``1``'
    prefer_simulation_time_display: bool
    'prefer_simulation_time_display() -> bool\n\nControls whether the timeline in the GUI preferentially displays simulation time or timestep values (if available) instead of zero-based trajectory frame numbers.\n\nDefault: ``False``'

    def jump_to_animation_start(self) -> None:
        """Sets :py:attr:`current_frame` to :py:attr:`first_frame`, moving the time slider to the start of the
animation interval."""
        ...

    def jump_to_animation_end(self) -> None:
        """Sets :py:attr:`current_frame` to :py:attr:`last_frame`, moving the time slider to the end of the
animation interval."""
        ...

    def jump_to_next_frame(self) -> None:
        """Advances :py:attr:`current_frame` by one frame, unless it is already at :py:attr:`last_frame`."""
        ...

    def jump_to_previous_frame(self) -> None:
        """Moves :py:attr:`current_frame` back by one frame, unless it is already at :py:attr:`first_frame`."""
        ...

@dataclass(kw_only=True)
class SceneRenderer:
    pass

@dataclass(kw_only=True)
class RenderSettings:

    class Range(enum.Enum):
        CurrentFrame = enum.auto()
        Animation = enum.auto()
        CustomInterval = enum.auto()
        CustomFrame = enum.auto()
    renderer: SceneRenderer
    'renderer() -> ovito.nonpublic.SceneRenderer\n\nThe renderer that is used to generate the image or movie. Depending on the selected renderer you can use this to set additional parameters such as the anti-aliasing level.\n\nSee the :py:class:`StandardRenderer`, :py:class:`TachyonRenderer`, :py:class:`OSPRayRenderer`, and :py:class:`AnariRenderer` classes for the list of parameters specific to each rendering backend.'
    range: RenderSettings.Range
    'range() -> RenderSettings.Range\n\nSelects the animation frames to be rendered. The current animation frame and the total animation range are determined by the :py:class:`AnimationSettings` object associated with the scene. \n\nPossible values:\n  * ``RenderSettings.Range.CurrentFrame`` (default): Renders a single image at the current animation time.\n  * ``RenderSettings.Range.Animation``: Renders a movie of the entire animation sequence.\n  * ``RenderSettings.Range.CustomInterval``: Renders a movie of the animation interval given by the :py:attr:`custom_range_start`/:py:attr:`custom_range_end` attributes.\n  * ``RenderSettings.Range.CustomFrame``: Renders a single image at the animation frame given by the :py:attr:`custom_frame` attribute.'
    output_image_width: int
    'output_image_width() -> int\n\nThe width of the rendered image or movie in pixels.\n\nDefault: ``640``'
    output_image_height: int
    'output_image_height() -> int\n\nThe height of the rendered image or movie in pixels.\n\nDefault: ``480``'
    background_color: tuple[float, float, float]
    'background_color() -> tuple[float, float, float]\n\nControls the background color of the rendered image.\n\nDefault: ``(1.0, 1.0, 1.0)`` -- white'
    generate_alpha: bool
    'generate_alpha() -> bool\n\nWhen saving the image to a file format that supports transparency information (e.g. PNG), this option will make those parts of the output image transparent which are not covered by objects.\n\nDefault: ``False``'
    save_to_file: bool
    'save_to_file() -> bool\n\nControls whether the rendered image or movie is saved to the file path given by :py:attr:`output_filename`. If ``False``, the image is still generated and displayed in the frame buffer window (in GUI mode), but not written to disk.\n\nDefault: ``False``'
    output_filename: str
    "output_filename() -> str\n\nThe output file path for the rendered image or movie. Only used if :py:attr:`save_to_file` is set to ``True``. The filename's extension determines the output format: if it denotes a video container format (e.g. AVI, MP4), all rendered animation frames are written to a single movie file; otherwise a sequence of image files is produced, one per frame, with the frame number automatically appended to the base filename (or substituted for a ``*`` wildcard if present).\n\nDefault: ``''``"
    skip_existing_images: bool
    'skip_existing_images() -> bool\n\nControls whether animation frames for which the output image file already exists will be skipped when rendering an animation sequence. This flag is ignored when directly rendering to a movie file and not an image file sequence. Use this flag when the image sequence has already been partially rendered and you want to render just the missing frames. \n\nDefault: ``False``'
    custom_range_start: int
    'custom_range_start() -> int\n\nThe first animation frame to render when :py:attr:`range` is set to ``RenderSettings.Range.CustomInterval``.\n\nDefault: ``0``'
    custom_range_end: int
    'custom_range_end() -> int\n\nThe last animation frame to render when :py:attr:`range` is set to ``RenderSettings.Range.CustomInterval``.\n\nDefault: ``100``'
    custom_frame: int
    'custom_frame() -> int\n\nThe single animation frame to render when :py:attr:`range` is set to ``RenderSettings.Range.CustomFrame``.\n\nDefault: ``0``'
    every_nth_frame: int
    'every_nth_frame() -> int\n\nSpecifies which fraction of the animation frames in the selected :py:attr:`range` is actually rendered. A value of 1 renders every frame; a value of *n* renders only every *n*-th frame. Use this to reduce the number of frames generated for extremely long animation sequences.\n\nDefault: ``1``'
    file_number_base: int
    'file_number_base() -> int\n\nWhen rendering an animation as a sequence of image files (rather than a single movie file), OVITO appends the frame number to the output filename. This setting specifies an offset added to those frame numbers, which can be useful to collect frames from several rendering passes in one directory before combining them into one contiguous movie externally.\n\nDefault: ``0``'
    frames_per_second: int
    'frames_per_second() -> int\n\nThe frame rate (FPS) used when encoding a movie file. A value less than or equal to 0 makes the renderer fall back to the frame rate given by `AnimationSettings.frames_per_second` of the associated `Scene.anim` instead. Has no effect when rendering to an image file sequence.\n\nDefault: ``0``'
    render_all_viewports: bool
    'render_all_viewports() -> bool\n\nControls whether a composed output image with multiple side-by-side views is generated, one for each viewport of the current viewport layout, instead of rendering just the single active viewport.\n\nDefault: ``False``'
    layout_separators_enabled: bool
    'layout_separators_enabled() -> bool\n\nControls the visibility of separator lines drawn between the individual viewports when :py:attr:`render_all_viewports` is enabled and an entire viewport layout is rendered.\n\nDefault: ``False``'
    layout_separator_width: int
    'layout_separator_width() -> int\n\nThe width, in pixels, of the separator lines drawn between viewports when :py:attr:`layout_separators_enabled` is set.\n\nDefault: ``2``'
    layout_separator_color: tuple[float, float, float]
    'layout_separator_color() -> tuple\n\nThe RGB color of the separator lines drawn between viewports when :py:attr:`layout_separators_enabled` is set.\n\nDefault: ``(0.5, 0.5, 0.5)``'
    stop_on_pipeline_error: bool
    'stop_on_pipeline_error() -> bool\n\nControls whether an error occurring in a data pipeline while rendering an animation sequence aborts the entire rendering process. If ``False``, the offending frame is skipped and rendering continues with the next one.\n\nDefault: ``False``'
    outlines_enabled: bool
    'outlines_enabled() -> bool\n\nTurns on depth-aware object outlines.\n\nDefault: ``False``\n\n   The outline parameters moved from the individual renderer classes to the render settings, so that they are preserved when switching between rendering backends.'
    outlines_depth_from: float
    'outlines_depth_from() -> float\n\nSpecifies the minimum depth difference required to draw an outline with the minimum width. The outline width is linearly interpolated from :py:attr:`outlines_min_width` to :py:attr:`outlines_max_width` as the depth difference varies between :py:attr:`outlines_depth_from` and :py:attr:`outlines_depth_to`.\n\nDefault: ``0.5``'
    outlines_depth_to: float
    "outlines_depth_to() -> float\n\nSpecifies the depth difference at which the outline reaches its maximum width. The outline width is linearly interpolated from :py:attr:`outlines_min_width` to :py:attr:`outlines_max_width` as the depth difference varies between :py:attr:`outlines_depth_from` and :py:attr:`outlines_depth_to`. \n\nA special value of ``float('inf')`` can be used to enable `uniform width mode`, where :py:attr:`outlines_depth_to` is set equal to :py:attr:`outlines_depth_from` and the outline width remains constant, equal to :py:attr:`outlines_min_width`.\n\nDefault: ``float('inf')``"
    outlines_min_width: float
    'outlines_min_width() -> float\n\nMinimum line width used for outlines at a depth difference of :py:attr:`outlines_depth_from`. Outline width is interpolated linearly from :py:attr:`outlines_min_width` to :py:attr:`outlines_max_width` as the depth difference ranges from :py:attr:`outlines_depth_from` to :py:attr:`outlines_depth_to`. The maximum valid outline width is 63. Fractional (sub-pixel) widths are supported.\n\nDefault: ``1.0``'
    outlines_max_width: float
    'outlines_max_width() -> float\n\nMaximum line width used for outlines at a depth difference of :py:attr:`outlines_depth_to` or greater. Outline width is interpolated linearly from :py:attr:`outlines_min_width` to :py:attr:`outlines_max_width` as the depth difference ranges from :py:attr:`outlines_depth_from` to :py:attr:`outlines_depth_to`. The maximum valid outline width is 63. Fractional (sub-pixel) widths are supported. This value is ignored in `uniform width mode`, see :py:attr:`outlines_depth_to` for details.\n\nDefault: ``4.0``'
    outlines_color: Optional[tuple[float, float, float]]
    'outlines_color() -> Optional[tuple[float, float, float]]\n\nSets the RGB outline color (without an alpha channel). If ``None`` is provided as the color value, the outline color is automatically determined based on the viewport background: white outlines for dark backgrounds and black outlines for light backgrounds.\n\nDefault: ``None``'

    def render(self, viewport_config: ViewportConfiguration, anim: AnimationSettings) -> PySide6.QtGui.QImage:
        ...

@dataclass(kw_only=True)
class ViewportLayoutCell:
    viewport: Optional[Viewport]
    'viewport() -> Optional[ovito.vis.Viewport]\n\nThe viewport window occupying this cell; or ``None`` if this is an interior cell that instead subdivides its area among its :py:attr:`children` cells.'
    parent: Optional[ViewportLayoutCell]
    'parent() -> Optional[ovito.nonpublic.ViewportLayoutCell]\n\nThe parent cell in the layout tree that this cell is a child of; or ``None`` if this is the :py:attr:`layout_root_cell`.'
    child_weights: list[float]
    'child_weights() -> list[float]\n\nThe relative sizes of the :py:attr:`children` cells along the :py:attr:`split_direction` axis. This list has the same length as :py:attr:`children`; the fraction of the available space given to a child cell equals its weight divided by the sum of all weights.'
    split_direction: int
    "split_direction() -> int\n\nFor an interior cell with two or more :py:attr:`children`, specifies the axis along which this cell's area is subdivided among them.\n\nPossible values:\n  * ``0`` (*None*): This is a leaf cell hosting a single :py:attr:`viewport` and has no children.\n  * ``1`` (*Horizontal*): The children are arranged side by side, left to right.\n  * ``2`` (*Vertical*): The children are stacked on top of each other, top to bottom."
    children: list[ViewportLayoutCell]
    "children() -> collections.abc.MutableSequence[ovito.nonpublic.ViewportLayoutCell]\n\nThe list of sub-cells that this interior cell's area is subdivided into, arranged according to :py:attr:`split_direction` and sized according to :py:attr:`child_weights`. Empty for a leaf cell that directly hosts a :py:attr:`viewport`."

@dataclass(kw_only=True)
class ViewportConfiguration:
    active_vp: Optional[Viewport]
    "active_vp() -> Optional[ovito.vis.Viewport]\n\nThe viewport that is currently active. It is marked with a colored border in OVITO's :py:class:`MainWindow`."
    maximized_vp: Optional[Viewport]
    'maximized_vp() -> Optional[ovito.vis.Viewport]\n\nThe viewport that is currently maximized; or ``None`` if no viewport is maximized.'
    layout_root_cell: ViewportLayoutCell
    'layout_root_cell() -> ovito.nonpublic.ViewportLayoutCell\n\nThe root cell of the tree of nested :py:class:`ViewportLayoutCell` instances that describes the current arrangement of viewport windows, see :ref:`manual:viewport_layouts`.'
    viewports: list[Viewport]
    'viewports() -> collections.abc.Sequence[ovito.vis.Viewport]\n\nThe flat list of all :py:class:`Viewport` instances that are part of the current viewport layout, in no particular order. Use :py:attr:`layout_root_cell` to determine their on-screen arrangement.'

    def zoom_all_when_ready(self) -> None:
        """Schedules all viewports in the :py:attr:`viewports` list to be zoomed to the extents of the scene once all data pipelines in the scene have been fully evaluated and their spatial extents are known. Unlike an immediate zoom operation, this defers the camera adjustment until the pending pipeline evaluations complete."""
        ...

    def get_viewport_rectangles(self) -> list[tuple[Viewport, tuple[float, float, float, float]]]:
        """Computes the effective rectangular area occupied by each viewport in the current :py:attr:`layout_root_cell` hierarchy. Returns a list of ``(viewport, rect)`` pairs, where each ``rect`` is a ``(x, y, width, height)`` tuple given in normalized coordinates relative to the overall layout area, with ``(0, 0)`` denoting the top-left corner and ``(1, 1)`` the bottom-right corner."""
        ...

@dataclass(kw_only=True)
class Controller:

    class Type(enum.Enum):
        Float = enum.auto()
        Int = enum.auto()
        Vector3 = enum.auto()
        Position = enum.auto()
        Rotation = enum.auto()
        Scaling = enum.auto()
        Transformation = enum.auto()
    type: Controller.Type
    is_animated: bool

    def get_float_value(self, time: int) -> float:
        ...

    def get_int_value(self, time: int) -> int:
        ...

    def get_vector3_value(self, time: int) -> tuple[float, float, float]:
        ...

    def get_color_value(self, time: int) -> tuple[float, float, float]:
        ...

    def get_position_value(self, time: int) -> tuple[float, float, float]:
        ...

    def get_rotation_value(self, time: int) -> tuple[float, float, float]:
        ...

    def set_float_value(self, time: int, value: float) -> None:
        ...

    def set_int_value(self, time: int, value: int) -> None:
        ...

    def set_vector3_value(self, time: int, value: tuple[float, float, float]) -> None:
        ...

    def set_color_value(self, time: int, value: tuple[float, float, float]) -> None:
        ...

    def set_position_value(self, time: int, value: tuple[float, float, float], is_absolute: bool) -> None:
        ...

    def set_rotation_value(self, time: int, value: tuple[float, float, float], is_absolute: bool) -> None:
        ...

    def set_scaling_value(self, time: int, value: Any, is_absolute: bool) -> None:
        ...

@dataclass(kw_only=True)
class PRSTransformationController(Controller):
    position: Controller
    rotation: Controller
    scaling: Controller

@dataclass(kw_only=True)
class AnimationKey:
    time: int

@dataclass(kw_only=True)
class FloatAnimationKey(AnimationKey):
    value: float

@dataclass(kw_only=True)
class IntegerAnimationKey(AnimationKey):
    value: int

@dataclass(kw_only=True)
class Vector3AnimationKey(AnimationKey):
    value: tuple[float, float, float]

@dataclass(kw_only=True)
class PositionAnimationKey(AnimationKey):
    value: tuple[float, float, float]

@dataclass(kw_only=True)
class RotationAnimationKey(AnimationKey):
    value: tuple[float, float, float]

@dataclass(kw_only=True)
class ScalingAnimationKey(AnimationKey):
    value: Any

@dataclass(kw_only=True)
class KeyframeController(Controller):
    keys: list[AnimationKey]

    def create_key(self, time: int, value: Any) -> AnimationKey:
        ...

@dataclass(kw_only=True)
class SceneNode:
    name: str
    display_color: tuple[float, float, float]
    parent_node: Optional[SceneNode]
    lookat_node: Optional[SceneNode]
    transform_ctrl: Controller
    translation: tuple[float, float, float]
    rotation: tuple[float, float, float]
    pipeline: Optional[ovito.pipeline.Pipeline]
    is_selected: bool
    children: list[SceneNode]

    def delete(self) -> None:
        ...

    def set_viewport_visibility(self, viewport: Viewport, visible: bool) -> None:
        ...

    def is_hidden_in_viewport(self, viewport: Viewport, include_hierarchy_parent: bool) -> bool:
        ...

    def is_hidden_in_all_viewports(self, include_hierarchy_parent: bool) -> bool:
        ...

    def contains_pipeline(self, pipeline: ovito.pipeline.Pipeline) -> bool:
        ...

@dataclass(kw_only=True)
class SceneRoot(SceneNode):
    anim: AnimationSettings
    selection: SelectionSet

@dataclass(kw_only=True)
class SelectionSet:
    first: Optional[SceneNode]
    nodes: list[SceneNode]