# Copyright (c) 2021, 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.

from typing import Callable

import asyncio
import carb
import omni.ext
from enum import IntFlag
from .preference_builder import PreferenceBuilder, PreferenceBuilderUI, SettingType
from .preferences_actions import register_actions, deregister_actions

_extension_instance = None
_preferences_page_list = []

PERSISTENT_SETTINGS_PREFIX = "/persistent"
DEVELOPER_PREFERENCE_PATH = "/app/show_developer_preference_section"
GLOBAL_PREFERENCES_PATH = "/exts/omni.kit.window.preferences/show_globals"
AUDIO_PREFERENCES_PATH = "/exts/omni.kit.window.preferences/show_audio"
RENDERING_PREFERENCES_PATH = "/exts/omni.kit.window.preferences/show_rendering"
RESOURCE_MONITOR_PREFERENCES_PATH = "/exts/omni.kit.window.preferences/show_resource_monitor"
TAGGING_PREFERENCES_PATH = "/exts/omni.kit.window.preferences/show_tagging"

class PreferencesExtension(omni.ext.IExt):

    class PreferencesState(IntFlag):
        Invalid = 0
        Created = 1

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

        self._ext_name = omni.ext.get_extension_name(ext_id)
        register_actions(self._ext_name, PreferencesExtension, lambda: _extension_instance)

        self._hooks = []
        self._ready_state = PreferencesExtension.PreferencesState.Invalid
        self._window = PreferenceBuilderUI(self._on_visibility_changed_fn)
        self._window.update_page_list(get_page_list())
        self._window.create_window()
        self._window_is_visible = False
        self._create_menu()
        self._register_pages()

        self._resourcemonitor_preferences = None
        manager = omni.kit.app.get_app().get_extension_manager()

        if carb.settings.get_settings().get(RESOURCE_MONITOR_PREFERENCES_PATH):
            self._hooks.append(
                manager.subscribe_to_extension_enable(
                    on_enable_fn=lambda _: self._register_resourcemonitor_preferences(),
                    on_disable_fn=lambda _: self._unregister_resourcemonitor_preferences(),
                    ext_name="omni.resourcemonitor",
                    hook_name="omni.kit.window.preferences omni.resourcemonitor listener",
                )
            )

        # set app started trigger. refresh_menu_items & rebuild_menus won't do anything until self._ready_state is MenuState.Created
        self._app_ready_sub = (
            omni.kit.app.get_app()
            .get_startup_event_stream()
            .create_subscription_to_pop_by_type(
                omni.kit.app.EVENT_APP_READY, self._rebuild_after_loading, name="omni.kit.menu.utils app started trigger"
            )
        )

    def on_shutdown(self):

        self._hooks = None

        deregister_actions(self._ext_name)

        self._window.destroy()
        del self._window
        self._window = None

        self._remove_menu()

        self._unregister_resourcemonitor_preferences()
        for page in self._created_preferences:
            unregister_page(page, rebuild=False)
        self._created_preferences = None

        # clear globals
        global _preferences_page_list
        _preferences_page_list = None
        global _extension_instance
        _extension_instance = None

    def _rebuild_after_loading(self, event):
        self._ready_state = PreferencesExtension.PreferencesState.Created
        self.rebuild_pages()

    def _register_pages(self):
        from .pages.stage_page import StagePreferences
        from .pages.rendering_page import RenderingPreferences
        from .pages.screenshot_page import ScreenshotPreferences
        from .pages.thumbnail_generation_page import ThumbnailGenerationPreferences
        from .pages.audio_page import AudioPreferences
        from .pages.tagging_page import TaggingPreferences
        from .pages.datetime_format_page import DatetimeFormatPreferences
        from .pages.globals import GlobalPreferences

        self._developer_preferences = None
        self._created_preferences = []
        for page in [
            ScreenshotPreferences(),
            ThumbnailGenerationPreferences(),
            StagePreferences(),
            DatetimeFormatPreferences(),
        ]:
            self._created_preferences.append(register_page(page))

        if carb.settings.get_settings().get(GLOBAL_PREFERENCES_PATH):
            self._created_preferences.append(register_page(GlobalPreferences()))
        if carb.settings.get_settings().get(AUDIO_PREFERENCES_PATH):
            self._created_preferences.append(register_page(AudioPreferences()))
        if carb.settings.get_settings().get(RENDERING_PREFERENCES_PATH):
            self._created_preferences.append(register_page(RenderingPreferences()))
        if carb.settings.get_settings().get(TAGGING_PREFERENCES_PATH):
            self._created_preferences.append(register_page(TaggingPreferences()))

        # developer options
        self._hooks.append(carb.settings.get_settings().subscribe_to_node_change_events(
            DEVELOPER_PREFERENCE_PATH, self._on_developer_preference_section_changed
        ))
        self._on_developer_preference_section_changed(None, None)

    def _on_developer_preference_section_changed(self, item, event_type):
        if event_type == carb.settings.ChangeEventType.CHANGED:
            if carb.settings.get_settings().get(DEVELOPER_PREFERENCE_PATH):
                if not self._developer_preferences:
                    from .pages.developer_page import DeveloperPreferences

                    self._developer_preferences = register_page(DeveloperPreferences())
            elif self._developer_preferences:
                    self._developer_preferences = unregister_page(self._developer_preferences)

    def _register_resourcemonitor_preferences(self):
        from .pages.resourcemonitor_page import ResourceMonitorPreferences
        self._resourcemonitor_preferences = register_page(ResourceMonitorPreferences())

    def _unregister_resourcemonitor_preferences(self):
        if self._resourcemonitor_preferences:
            unregister_page(self._resourcemonitor_preferences)
            self._resourcemonitor_preferences = None

    def rebuild_pages(self):
        if self._ready_state == PreferencesExtension.PreferencesState.Invalid:
            return

        if self._window:
            self._window.update_page_list(get_page_list())
            self._window.rebuild_pages()

    def select_page(self, page):
        if self._window:
            if self._window.select_page(page):
                return True
        return False

    async def _refresh_menu_async(self):
        omni.kit.menu.utils.refresh_menu_items("Edit")
        self._refresh_menu_task = None

    def _on_visibility_changed_fn(self, visible):
        self._window_is_visible = visible
        if visible:
            self._window.show_window()
        else:
            self._window.hide_window()

    def _create_menu(self):
        from omni.kit.menu.utils import MenuItemDescription, MenuItemOrder

        self._edit_menu_list = None
        self._refresh_menu_task = None

        self._edit_menu_list = [
            MenuItemDescription(
                name="Preferences",
                glyph="cog.svg",
                appear_after=["Capture Screenshot", MenuItemOrder.LAST],
                ticked=True,
                ticked_fn=lambda: self._window_is_visible,
                onclick_action=("omni.kit.window.preferences", "toggle_preferences_window"),
            ),
            MenuItemDescription(
                appear_after=["Capture Screenshot", MenuItemOrder.LAST],
            ),

        ]
        omni.kit.menu.utils.add_menu_items(self._edit_menu_list, "Edit", -9)

    def _remove_menu(self):
        if self._refresh_menu_task:
            self._refresh_menu_task.cancel()
            self._refresh_menu_task = None

        # remove menu
        omni.kit.menu.utils.remove_menu_items(self._edit_menu_list, "Edit")

    def _toggle_preferences_window(self):
        self._window_is_visible = not self._window_is_visible

        async def show_windows():
            if self._window_is_visible:
                self._window.show_window()
            else:
                self._window.hide_window()

        asyncio.ensure_future(show_windows())
        asyncio.ensure_future(self._refresh_menu_async())

    def show_preferences_window(self):
        """Show the Preferences window to the User."""
        if not self._window_is_visible:
            self._toggle_preferences_window()

    def hide_preferences_window(self):
        """Hide the Preferences window from the User."""
        if self._window_is_visible:
            self._toggle_preferences_window()


def get_instance():
    global _extension_instance

    return _extension_instance


def show_preferences_window():
    """Show the Preferences window to the User."""
    get_instance().show_preferences_window()


def hide_preferences_window():
    """Hide the Preferences window from the User."""
    get_instance().hide_preferences_window()


def get_page_list():
    global _preferences_page_list

    return _preferences_page_list


def register_page(page):
    global _preferences_page_list

    _preferences_page_list.append(page)
    _preferences_page_list = sorted(_preferences_page_list, key=lambda page: page._title)

    get_instance().rebuild_pages()
    return page


def select_page(page):
    return get_instance().select_page(page)

def rebuild_pages():
    get_instance().rebuild_pages()


def unregister_page(page, rebuild: bool=True):
    global _preferences_page_list

    if hasattr(page, 'destroy'):
        page.destroy()
    # Explicitly clear properties, that helps to release some C++ objects
    page.__dict__.clear()

    if _preferences_page_list:
        _preferences_page_list.remove(page)
        preferences = get_instance()
        if preferences and rebuild:
            preferences.rebuild_pages()


def show_file_importer(
    title: str,
    file_exts: list = [("All Files(*)", "")],
    filename_url: str = None,
    click_apply_fn: Callable = None,
    show_only_folders: bool = False,
):
    from functools import partial
    from omni.kit.window.file_importer import get_file_importer

    def on_import(click_fn: Callable, filename: str, dirname: str, selections=[]):
        dirname = dirname.strip()
        if dirname and not dirname.endswith("/"):
            dirname += "/"
        fullpath = f"{dirname}{filename}"
        if click_fn:
            click_fn(fullpath)

    file_importer = get_file_importer()
    if file_importer:
        file_importer.show_window(
            title=title,
            import_button_label="Select",
            import_handler=partial(on_import, click_apply_fn),
            file_extension_types=file_exts,
            filename_url=filename_url,
            # OM-96626: Add show_only_folders option to file importer
            show_only_folders=show_only_folders,
        )
