"""This module defines functions for real-time interactive rendering using a graphical user interface (GUI):

    * :py:func:`create_qwidget` - Create a :py:class:`WidgetViewportWindow` that displays the contents of a virtual :py:class:`Viewport`
    * :py:func:`create_ipywidget` - Create an interactive viewport widget for embedding in Jupyter notebooks
    * :py:class:`MainWindow` - An OVITO application window with the full graphical user interface

The :py:class:`ViewportWindow` and :py:class:`WidgetViewportWindow` classes represent the physical, on-screen windows that give a
non-visual :py:class:`Viewport` a pixel size and turn it into something the user can actually see and interact with.

Furthermore, the module defines the :py:class:`UtilityInterface` abstract base class, which lets you
implement custom utility applets for the command panel of OVITO Pro."""
__all__ = ['create_window', 'create_qwidget', 'UtilityInterface', 'MainWindow', 'ViewportWindow', 'WidgetViewportWindow', 'create_ipywidget']
from __future__ import annotations
from typing import Union, Optional, Dict, List
import PySide6.QtWidgets
import PySide6.QtGui
import ipywidgets
from dataclasses import dataclass
from ovito.pipeline import Pipeline
from ovito.vis import Viewport
from ovito import Scene

class UtilityInterface:
    """Base: :py:class:`traits.has_traits.HasTraits`

Base class for utility applets running in the command panel of OVITO Pro.
See :ref:`writing_custom_utilities`."""
    pass

@dataclass(kw_only=True)
class ViewportWindow:
    """A viewport window gives a non-visual :py:class:`Viewport` a physical, on-screen presence: it provides the window, with a size
in device pixels, that the viewport's contents are rendered into, and it translates mouse and keyboard input from the user back into
camera movements and other viewport interactions. A single :py:class:`Viewport` may be associated with zero, one, or several
windows at the same time; call :py:meth:`get_windows` to obtain the list of windows currently showing a given viewport.
Conversely, :py:meth:`MainWindow.get_viewport_windows` returns all :py:class:`WidgetViewportWindow` instances belonging to a given main window.

:py:class:`WidgetViewportWindow` is the concrete kind of viewport window that embeds a viewport in a Qt widget; it is the type of object
returned by :py:func:`create_qwidget`, and it also underlies the interactive viewports of a :py:class:`MainWindow`."""
    viewport: Optional[Viewport]
    'viewport() -> Optional[ovito.vis.Viewport]\n\nThe :py:class:`Viewport` currently displayed by this window. Assign a different :py:class:`Viewport` to this property to make the window switch to displaying that viewport instead.'
    visible: bool
    'visible() -> bool\n\nIndicates whether this window is currently shown on screen.'
    show_title: bool = True
    "show_title() -> bool\n\nControls whether the viewport's title caption is displayed in the window's upper-left corner.\n\nDefault: ``True``"
    show_orientation_indicator: bool = True
    "show_orientation_indicator() -> bool\n\nControls whether the coordinate axis tripod, indicating the orientation of the viewport's camera, is displayed in the window's lower-left corner.\n\nDefault: ``True``"
    rendered_frames_counter: int
    'rendered_frames_counter() -> int\n\nCounts how often this window has rendered and displayed a frame since the current program session started. Mainly useful for benchmarking and debugging purposes.'
    auto_update_enabled: bool = True
    "auto_update_enabled() -> bool\n\nControls whether the window automatically refreshes its displayed contents whenever the viewport's camera or the scene shown in it change. Temporarily disable this to avoid redundant re-rendering while making several changes to the scene or camera in a row.\n\nDefault: ``True``"
    size: tuple[int, int]
    "size() -> tuple[int, int]\n\nThe current size, in physical device pixels, of the window's drawing surface, as a ``(width, height)`` tuple."
    device_pixel_ratio: float
    'device_pixel_ratio() -> float\n\nThe ratio between physical device pixels and device-independent pixels of the window\'s drawing surface, e.g. ``2.0`` on a high-DPI ("Retina") display.'

@dataclass(kw_only=True)
class WidgetViewportWindow(ViewportWindow):
    """Base: :py:class:`ViewportWindow`

A :py:class:`ViewportWindow` that displays its :py:class:`Viewport` inside a Qt
`QWidget <https://doc.qt.io/qtforpython-6/PySide6/QtWidgets/QWidget.html>`__, accessible through the :py:attr:`widget` property.
This is the type of window created by :py:func:`create_qwidget`, and it is also the base class of the viewport windows underlying
the interactive viewports of a :py:class:`MainWindow` -- see :py:meth:`MainWindow.get_viewport_windows` to retrieve them."""
    widget: PySide6.QtWidgets.QWidget
    "widget() -> PySide6.QtWidgets.QWidget\n\nThe Qt widget that renders and displays this viewport window's contents on screen, which you can embed into your own PySide6-based user interface. This is the widget underlying the :py:class:`WidgetViewportWindow` object returned by :py:func:`create_qwidget`."

    def grab_image(self) -> PySide6.QtGui.QImage:
        """Captures the current on-screen appearance of this viewport window and returns it as a Qt `QImage <https://doc.qt.io/qtforpython-6/PySide6/QtGui/QImage.html>`__. The image has the same resolution, in physical device pixels, as the window's drawing surface -- see :py:attr:`size`.

The captured image reproduces what the user sees, including the viewport caption, the orientation indicator, and any interactive gizmos. This makes it different from `Viewport.render_image()`, which performs a full offscreen rendering of the scene at an arbitrary resolution and without any interactive decorations. Grabbing is cheap in comparison, because it reuses the frame that has already been computed for the on-screen display."""
        ...

@dataclass(kw_only=True)
class JupyterViewportWidget(ipywidgets.DOMWidget):
    antialiasing: bool = True
    picking: bool = False
    vr_scale: float = 0.0

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

@dataclass(kw_only=True)
class MainWindow:
    """Represents a top-level main window in the *OVITO Pro* desktop application, including all GUI elements such as 3D viewports and the interactive pipeline editor.

This class is meant to be used in Jupyter notebooks to launch an OVITO Pro session with a full graphical user interface.
Changes you make to the scene, pipeline(s), or viewport(s) are synchronized between the Jupyter notebook and the GUI in both directions.

:param contents: The :py:class:`Pipeline` or :py:class:`Viewport` to be displayed in the OVITO main window.
                 If none is provided, the current :py:data:`ovito.scene`, including all added pipelines, will be displayed.

Use the :py:meth:`all_windows` class method to obtain a list of all already open main windows."""
    widget: PySide6.QtWidgets.QWidget
    'widget() -> PySide6.QtWidgets.QWidget\n\nReturns the actual window, which is a top-level `QWidget <https://doc.qt.io/qtforpython-6/PySide6/QtWidgets/QWidget.html>`__ from the PySide6 Qt library. This property may be ``None`` if the main window has already been closed and is no longer available.'
    scene: Scene
    'scene() -> ovito.Scene\n\nThe :py:class:`~ovito.Scene` object that is currently displayed in this main window.'

    def __init__(self, contents: Union[Pipeline, Viewport, None]=None):
        ...

    @staticmethod
    def all_windows() -> list[MainWindow]:
        """Returns the list of all currently open main windows."""
        ...

    @staticmethod
    def get_current() -> Optional[MainWindow]:
        """Returns the currently active main window, or ``None`` if no main windows are open. The active window is the one displaying the same :py:class:`~ovito.Scene` as the calling context. If no window matches, but exactly one main window is open, that window is returned."""
        ...

    def take_screenshot(self) -> list[tuple[PySide6.QtGui.QPixmap, str]]:
        """Captures a screenshot of this main window in its current on-screen appearance. If any other top-level windows belonging to this main window are currently visible, e.g. modal or non-modal dialogs the user has opened, a screenshot of each of them is captured as well.

:return: A list of ``(image, title)`` tuples, one for the main window followed by one for each additional visible window, where *image* is a `QPixmap <https://doc.qt.io/qtforpython-6/PySide6/QtGui/QPixmap.html>`__ holding the captured screenshot and *title* is the window's title text."""
        ...

    def get_actions(self) -> Dict[str, PySide6.QtGui.QAction]:
        ...

    def get_viewport_windows(self) -> List[WidgetViewportWindow]:
        """Returns the list of all :py:class:`WidgetViewportWindow` instances currently associated with this main window, one for each of its on-screen viewport panes. Use :py:attr:`viewport` on an individual window to find out which :py:class:`Viewport` it is displaying."""
        ...

def create_qwidget(contents: Union[Pipeline, Viewport, None]=None, parent: Optional[PySide6.QtWidgets.QWidget]=None, *, show_orientation_indicator: bool=True, show_title: bool=False) -> WidgetViewportWindow:
    """Creates an interactive :py:class:`WidgetViewportWindow` that displays the three-dimensional scene as seen through a
virtual :py:class:`Viewport`. The window accepts mouse input from the user similar to the viewport windows
of the OVITO desktop application. You can use this method to develop custom user interfaces based on the `Qt cross-platform framework <https://www.qt.io/qt-for-python>`__
that integrate OVITO's functionality and display the output of a data pipeline.

:param contents: The :py:class:`Pipeline` or :py:class:`Viewport` object to be displayed by the widget.
:param parent: An optional Qt widget to be set as parent for the new viewport widget.
:param show_orientation_indicator: Controls the visibility of the coordinate axes in the lower left corner of the viewport widget.
:param show_title: Controls the visibility of viewport title in the upper left corner of the viewport widget.
:return: :py:class:`WidgetViewportWindow`

   This method used to return the `PySide6.QtWidgets.QWidget <https://doc.qt.io/qtforpython-6/PySide6/QtWidgets/QWidget.html>`__
   directly. It now returns the encompassing :py:class:`WidgetViewportWindow` object instead, whose
   :py:attr:`widget` property gives access to the underlying ``QWidget``.

The returned :py:class:`WidgetViewportWindow` object manages the interactive viewport window; use its
:py:attr:`widget` property to get the actual `PySide6.QtWidgets.QWidget <https://doc.qt.io/qtforpython-6/PySide6/QtWidgets/QWidget.html>`__
that you can embed into your own Qt user interface.

The window is linked to the :py:class:`Viewport` instance if provided.
Any changes your Python script subsequently makes to the non-visual :py:class:`Viewport` object,
for example setting its :py:attr:`camera_pos` or :py:attr:`camera_dir`, will automatically be reflected by the
visual widget. Vice versa will user interactions with the viewport widget
automatically lead to changes of the associated :py:class:`Viewport` object.

If you provide a :py:class:`Pipeline` object as the *contents* argument, the method will create an
ad-hoc viewport showing the output of just the given pipeline. If you provide no *contents* argument, the method
will create an ad-hoc viewport showing the contents of the global :py:data:`ovito.scene`, including all pipelines that have been added.

OVITO automatically creates a global QApplication object if necessary, which can be accessed via the :py:meth:`!QApplication.instance()` static method.

The following code example demonstrates the use of the :py:meth:`create_qwidget` function in a standalone Python program. Please see the
`Qt for Python <https://doc.qt.io/qtforpython-6/>`__ documentation for more information on how to create graphical
user interfaces using the Qt framework.

```python
  from ovito.io import import_file
  from ovito.vis import Viewport
  from ovito.gui import create_qwidget
  from PySide6.QtWidgets import QApplication
  import sys
  
  # Import a particle model and add it to the global visualization scene.
  pipeline = import_file('input/simulation.dump')
  pipeline.add_to_scene()
  
  # Create a virtual viewport.
  vp = Viewport(type=Viewport.Type.Perspective, camera_dir=(2, 1, -1))
  
  # Create a WidgetViewportWindow associated with the viewport, and get its physical Qt widget.
  vpwin = create_qwidget(vp)
  widget = vpwin.widget
  widget.resize(500, 400)
  widget.setWindowTitle('OVITO Viewport Demo')
  widget.show()
  widget.raise_()
  vp.zoom_all((widget.width(), widget.height()))
  
  # Start the Qt event loop by invoking the QApplication.exec() method.
  sys.exit(QApplication.instance().exec())
```"""
    ...

def create_ipywidget(contents: Union[Pipeline, Viewport, None]=None, *, antialiasing: bool=True, picking: bool=False, vr_scale: float=0.0, layout=None, **kwargs) -> JupyterViewportWidget:
    """Creates an interactive widget for embedding in a Jupyter notebook, which displays the 3d scene as seen through a virtual :py:class:`Viewport`.
The method returns an interactive notebook element, which accepts mouse inputs similar to the viewport windows
of the OVITO desktop application. It may be necessary to call :func:`display <ipython:IPython.display.display>` in order to show the widget:

.. code-block::

    vp = Viewport(type=Viewport.Type.Perspective, camera_dir=(0.5, 1.0, -0.4))
    vp.zoom_all()
    widget = ovito.gui.create_ipywidget(vp)
    display(widget)

The `Jupyter widget <https://ipywidgets.readthedocs.io/en/stable/>`__ returned by this method is permanently linked to the :py:class:`Viewport` instance.
Any changes you subsequently make to the non-visual :py:class:`Viewport`, for example, setting its :py:attr:`camera_pos` or :py:attr:`camera_dir`, will be
reflected by the visual viewport widget. Vice versa, all user interactions with the viewport widget
update the corresponding fields of the :py:class:`Viewport` object.

:param contents: The :py:class:`Pipeline` or :py:class:`Viewport` object to be displayed by the widget.
:param antialiasing: Enables anti-aliasing to reduce jagged edges, which appear during WebGL rasterization.
:param picking: Enables object picking. When hovering the mouse cursor over an object, the widget will display the object's properties as text.
:param vr_scale: Enables VR support (WebXR browser interface) if set to a positive value. The parameter value specifies the ratio of 1 length unit
                 of the simulation model and 1 meter in VR space. It thus controls the apparent size (scaling) of the model in virtual reality mode.
                 For example, if object dimensions are specified in terms of nanometers in the simulation model, then a *vr_scale* value of 0.2 would let
                 a 1 nanometer sphere appear 20 centimeters large in virtual reality space.
:param layout: The `layout attribute <https://ipywidgets.readthedocs.io/en/latest/examples/Widget%20Layout.html>`__ for the new Jupyter widget.
:return: `ipywidgets.DOMWidget <https://ipywidgets.readthedocs.io/en/stable/>`__

If you provide a :py:class:`Pipeline` object as the *contents* argument, the method will create an
ad-hoc viewport showing the output of just that pipeline. If you provide no *contents* argument at all,
an ad-hoc viewport is created showing the contents of the global :py:data:`ovito.scene`, including all pipelines that have been added.

The ``layout`` attribute lets you control the `size of the widget <https://ipywidgets.readthedocs.io/en/stable/examples/Widget%20Layout.html>`__, e.g.:

.. code-block::

    from ipywidgets import Layout
    widget = create_ipywidget(vp, layout=Layout(width='100%', height='400px'))"""
    ...