# Copyright (c) 2018-2020, NVIDIA CORPORATION.  All rights reserved.
#
# NVIDIA CORPORATION and its licensors retain all intellectual property
# and proprietary rights in and to this software, related documentation
# and any modifications thereto.  Any use, reproduction, disclosure or
# distribution of this software and related documentation without an express
# license agreement from NVIDIA CORPORATION is strictly prohibited.
#
"""The documentation for Abstract Model """

from omni import ui
from pxr import UsdGeom
import omni.kit.commands
import omni.usd

from .doc_page import DocPage

SPACING = 5


class VisibilityWatchModel(ui.AbstractValueModel):
    """The value model that is reimplemented in Python to watch the visibility of the selection"""

    def __init__(self):
        super(VisibilityWatchModel, self).__init__()
        self._usd_context = omni.usd.get_context()
        self._selection = None
        self._events = None
        self._stage_event_sub = None
        if self._usd_context is not None:
            self._selection = self._usd_context.get_selection()
            self._events = self._usd_context.get_stage_event_stream()
            self._stage_event_sub = self._events.create_subscription_to_pop(
                self._on_stage_event, name="omni.example.ui abstract model stage update"
            )
        self.prim = None

    def clean(self):
        """Should be called when the extesion us unloaded or reloaded"""
        self._usd_context = None
        self._selection = None
        self._events = None
        self._stage_event_sub = None
        self.prim = None

    def _on_stage_event(self, event):
        if event.type == int(omni.usd.StageEventType.SELECTION_CHANGED):
            self._on_selection_changed()

    def _on_selection_changed(self):
        selection = self._selection.get_selected_prim_paths()
        stage = self._usd_context.get_stage()
        # When TC runs tests, it's possible that stage is None
        if selection and stage:
            self.prim = stage.GetPrimAtPath(selection[0])
        # Say to the widgets that the model value is changed
        self._value_changed()

    def get_value_as_bool(self):
        """Reimplemented get bool"""
        if self.prim:
            if self.prim.HasAttribute(UsdGeom.Tokens.visibility):
                attribute = self.prim.GetAttribute(UsdGeom.Tokens.visibility)
                if attribute:
                    return attribute.Get() == UsdGeom.Tokens.inherited

    def set_value(self, value):
        """Reimplemented set bool"""
        if self.prim:
            omni.kit.commands.execute("ToggleVisibilitySelectedPrims", selected_paths=[self.prim.GetPath()])
            # Say to the widgets that the model value is changed
            self._value_changed()


class AbstractModelDoc(DocPage):
    """ document for CheckBox"""

    def __init__(self, extension_path):
        super().__init__(extension_path)
        self._visibility_model = VisibilityWatchModel()

    def clean(self):
        """Should be called when the extesion us unloaded or reloaded"""
        # Unfortunatley, the member variables are not destroyed when the extension is unloaded. We need to do it
        # automatically. Usually, it's OK because the Python garbage collector will eventually destroy everythigng. But
        # we need the images to be destroyed right now because Kit know nothing about Python garbage collector and it
        # will fire warning that texture is not destroyed.
        if self._visibility_model:
            self._visibility_model.clean()
            self._visibility_model = None

    def create_doc(self, navigate_to=None):
        self._section_title("AbstractValueModel")

        self._text(
            "The AbstractValueModel class provides an abstract interface for the model that holds a single value. It's "
            "a central component of the model-delegate-view pattern used in many widgets like CheckBox, StringField(TODO), "
            "Slider(TODO), RadioBox(TODO). It's a dynamic data structure independent of the widget implementation. And "
            "it holds the data, logic, and rules of the widgets and defines the standard interface that widgets must "
            "use to be able to interoperate with the data model. It is not supposed to be instantiated directly. "
            "Instead, the user should subclass it to create new models."
        )

        self._text(
            "In the next example, AbstractValueModel class is reimplemented to provide control over the visibility of "
            "the selected object."
        )

        with ui.HStack(width=0, spacing=SPACING):
            ui.CheckBox(self._visibility_model)
            self._text("Visibility of Selected Prim")

        self._code(
            """
        class VisibilityWatchModel(ui.AbstractValueModel):
            '''
            The value model that is reimplemented in Python
            to watch the visibility of the selection.
            '''

            def __init__(self):
                super(VisibilityWatchModel, self).__init__()
                self._usd_context = omni.usd.get_context()
                self._selection = self._usd_context.get_selection()
                self._events = self._usd_context.get_stage_event_stream()
                self._stage_event_sub = \\
                    self._events.create_subscription_to_pop(
                        self._on_stage_event, name="omni.example.ui visibilitywatch stage update")
                self.prim = None

            def _on_stage_event(self, event):
                if event.type == int(omni.usd.StageEventType.SELECTION_CHANGED):
                    self._on_selection_changed()

            def _on_selection_changed(self):
                selection = self._selection.get_selected_prim_paths()
                stage = self._usd_context.get_stage()
                if selection and stage:
                    self.prim = stage.GetPrimAtPath(selection[0])
                # Say to the widgets that the model value is changed
                self._value_changed()

            def get_value_as_bool(self):
                '''Reimplemented get bool'''
                if self.prim:
                    attribute = \\
                        UsdGeom.Imageable(self.prim).GetVisibilityAttr()
                    inherited = \\
                        attribute.Get() == UsdGeom.Tokens.inherited
                    return inherited if attribute else False

            def set_value(self, value):
                '''Reimplemented set bool'''
                if self.prim:
                    omni.kit.commands.execute(
                        "ToggleVisibilitySelectedPrims",
                        selected_paths=[self.prim.GetPath()])
                    # Say to the widgets that the model value is changed
                    self._value_changed()
            """
        )

        ui.Spacer(height=10)
