# 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.
#

# pylint: disable=attribute-defined-outside-init, protected-access

import asyncio
import contextlib
import weakref
from typing import Callable

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

from . import common, ext_controller, ext_info_widget
from .exts_list_widget import ExtsListWidget
from .utils import get_setting
from .window import ExtsWindow

MENU_PATH = "Window/Extensions"

_ext_instance = None


def show_window(value: bool):
    """Show/Hide Extensions window"""
    if _ext_instance:
        _ext_instance.show_window(value)


def get_instance() -> "weakref.ReferenceType[ExtsWindowExtension]":
    return weakref.ref(_ext_instance)


class ExtsWindowExtension(omni.ext.IExt):
    """The entry point exts 2.0 window"""

    def on_startup(self, ext_id):
        global _ext_instance
        _ext_instance = self

        app = omni.kit.app.get_app()
        common.EXT_ROOT = app.get_extension_manager().get_extension_path(ext_id)

        self._window = None

        # # Add to menu - will fail if the editor is not loaded
        self._menu = None  # Throwing is ok, but leaves self._menu undefined
        with contextlib.suppress(Exception):
            self._menu = omni.kit.ui.get_editor_menu().add_item(
                MENU_PATH, lambda _, v: self.show_window(v), toggle=True, value=False, priority=100
            )

        ui.Workspace.set_show_window_fn(
            "Extensions",
            lambda value: self.show_window(value),  # pylint: disable=unnecessary-lambda
        )

        # Start enabling autoloadable extensions:
        asyncio.ensure_future(ext_controller.autoload_extensions())

        # Auto show window, for convenience
        show = get_setting("/exts/omni.kit.window.extensions/showWindow", False)
        if self._menu:
            omni.kit.ui.get_editor_menu().set_value(MENU_PATH, show)
        if show:
            self.show_window(True)

        # Some events trigger rebuild of a whole window
        bus = app.get_message_bus_event_stream()
        self._subs = []

        def on_rebuild(_):
            if self._window:
                self.show_window(False)
                self.show_window(True)

        self._subs.append(bus.create_subscription_to_pop_by_type(common.COMMUNITY_TAB_TOGGLE_EVENT, on_rebuild))

    def on_shutdown(self):
        global _ext_instance
        _ext_instance = None

        self.show_window(False)
        self._menu = None
        self._subs = None

    def show_window(self, value):
        if value:

            def on_visibility_changed(visible):
                omni.kit.ui.get_editor_menu().set_value(MENU_PATH, visible)

            self._window = ExtsWindow(on_visibility_changed if self._menu else None)
        else:
            if self._window:
                self._window.destroy()
                self._window = None

    # --------------------------------------------------------------------------------------------------------------
    # API to add a new tab to the extension info pane
    @classmethod
    def refresh_extension_info_widget(cls):
        if _ext_instance._window and _ext_instance._window._ext_info_widget:
            _ext_instance._window._ext_info_widget.update_tabs()
            _ext_instance._window._ext_info_widget._refresh()

    @classmethod
    def add_tab_to_info_widget(cls, tab: ext_info_widget.PageBase):
        ext_info_widget.ExtInfoWidget.pages.append(tab)
        cls.refresh_extension_info_widget()

    @classmethod
    def remove_tab_from_info_widget(cls, tab: ext_info_widget.PageBase):
        if tab in ext_info_widget.ExtInfoWidget.pages:
            ext_info_widget.ExtInfoWidget.pages.remove(tab)
            ext_info_widget.ExtInfoWidget.current_page = 0
        cls.refresh_extension_info_widget()

    # --------------------------------------------------------------------------------------------------------------
    # API to add a searchable keyword
    @classmethod
    def refresh_search_items(cls):
        if _ext_instance._window and _ext_instance._window._exts_list_widget:
            _ext_instance._window._exts_list_widget.rebuild_filter_menu()
            _ext_instance._window._exts_list_widget._model.refresh_all()

    @classmethod
    def add_searchable_keyword(cls, keyword: str, description: str, filter_on_keyword: Callable, clear_cache: Callable):
        ExtsListWidget.searches[keyword] = (description, filter_on_keyword, clear_cache)
        cls.refresh_search_items()

    @classmethod
    def remove_searchable_keyword(cls, keyword_to_remove: str):
        if keyword_to_remove in ExtsListWidget.searches:
            del ExtsListWidget.searches[keyword_to_remove]
        cls.refresh_search_items()
