# Copyright (c) 2018-2022, 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.
#
__all__ = ["DocumentationBuilderCatalog"]

import asyncio
import weakref
from typing import Any, Dict, List

import omni.kit.app
import omni.ui as ui


class _CatalogModelItem(ui.AbstractItem):
    def __init__(self, catalog_data):
        super().__init__()
        self.__catalog_data = catalog_data
        self.name_model = ui.SimpleStringModel(catalog_data["name"])
        self.__children = None
        self.widget = None

    def destroy(self):
        for c in self.__children or []:
            c.destroy()
        self.__children = None
        self.__catalog_data = None
        self.widget = None

    @property
    def page_name(self):
        return self.__catalog_data["page"]

    @property
    def children(self):
        if self.__children is None:
            # Lazy initialization
            self.__children = [_CatalogModelItem(c) for c in self.__catalog_data.get("children", [])]

        return self.__children


class _CatalogModel(ui.AbstractItemModel):
    def __init__(self, catalog_data):
        super().__init__()
        self.__root = _CatalogModelItem({"name": "Root", "children": catalog_data})

    def destroy(self):
        self.__root.destroy()
        self.__root = None

    def get_item_children(self, item):
        """Returns all the children when the widget asks it."""
        if item is None:
            return self.__root.children

        return item.children

    def get_item_value_model_count(self, item):
        """The number of columns"""
        return 1

    def get_item_value_model(self, item, column_id):
        """
        Return value model.
        """
        return item.name_model


class _CatalogDelegate(ui.AbstractItemDelegate):
    def destroy(self):
        pass

    def build_branch(self, model, item, column_id, level, expanded):
        """Create a branch widget that opens or closes subtree"""
        name = f"tree{level+1}"
        with ui.ZStack(width=(level + 1) * 20):
            ui.Rectangle(height=28, name=name)
            if level > 0:
                with ui.VStack():
                    ui.Spacer()
                    with ui.HStack(height=10):
                        ui.Spacer()
                        if model.can_item_have_children(item):
                            image = "minus" if expanded else "plus"
                            ui.Image(name=image, width=10, height=10)
                    ui.Spacer()

    def build_widget(self, model, item, column_id, level, expanded):
        """Create a widget per column per item"""
        name = f"tree{level}"
        label = model.get_item_value_model(item, column_id).as_string
        if level == 1:
            label = label.upper()
        with ui.ZStack():
            ui.Rectangle(height=28, name=name)
            with ui.VStack():
                item.widget = ui.Label(label, name=name, style_type_name_override="Text")


class DocumentationBuilderCatalog:
    """The left panel"""

    def __init__(self, catalog_data, parent: weakref, **kwargs):
        """
        `settings_menu: List[Dict[str, Any]]`
            List of items. [{"name": "Export", "clicked_fn": on_export}]
        """
        width = kwargs.get("width", 200)
        self.__settings_menu: List[Dict[str, Any]] = kwargs.pop("settings_menu", None)
        self._model = _CatalogModel(catalog_data)
        self._delegate = _CatalogDelegate()
        self.__parent = parent
        with ui.ScrollingFrame(**kwargs):
            with ui.VStack(height=0):
                with ui.ZStack():
                    ui.Image(height=width, name="main_logo")
                    if self.__settings_menu:
                        with ui.Frame(height=16, width=16, mouse_hovered_fn=self._on_settings_hovered):
                            self.__settings_image = ui.Image(
                                name="settings", visible=False, mouse_pressed_fn=self._on_settings
                            )
                tree_view = ui.TreeView(
                    self._model,
                    name="catalog",
                    delegate=self._delegate,
                    root_visible=False,
                    header_visible=False,
                    selection_changed_fn=self.__selection_changed,
                )

        # Generate the TreeView children
        ui.Inspector.get_children(tree_view)

        # Expand the first level
        for i in self._model.get_item_children(None):
            tree_view.set_expanded(i, True, False)

        self.__stop_event = asyncio.Event()
        self._menu = None

    def destroy(self):
        self.__settings_menu = []
        self.__stop_event.set()
        self.__settings_image = None
        self._model.destroy()
        self._model = None
        self._delegate.destroy()
        self._delegate = None
        self._menu = None

    async def _show_settings(self, delay, event: asyncio.Event):
        for _i in range(delay):
            await omni.kit.app.get_app().next_update_async()

            if event.is_set():
                return

        self.__settings_image.visible = True

    def _on_settings_hovered(self, hovered):
        self.__stop_event.set()
        self.__settings_image.visible = False

        if hovered:
            self.__stop_event = asyncio.Event()
            asyncio.ensure_future(self._show_settings(100, self.__stop_event))

    def _on_settings(self, x, y, button, mod):
        self._menu = ui.Menu("Scene Docs Settings")
        with self._menu:
            for item in self.__settings_menu:
                ui.MenuItem(item["name"], triggered_fn=item["clicked_fn"])
        self._menu.show()

    def __selection_changed(self, selection):
        if not selection:
            return

        self.__parent().set_page(selection[0].page_name, selection[0].name_model.as_string)
