import asyncio
from typing import Callable, Optional

import carb
import carb.settings
import omni.ext
import omni.kit.app
import omni.kit.menu.utils
import omni.kit.ui
import omni.ui as ui
from omni.kit.menu.utils import MenuItemDescription

from .actions import deregister_actions, register_actions
from .welcome_model import WelcomeModel
from .welcome_window import WelcomeWindow

WELCOME_MENU_ROOT = "File"
_extension_instance = None


class WelcomeExtension(omni.ext.IExt):
    def on_startup(self, ext_id):
        self._ext_name = omni.ext.get_extension_name(ext_id)
        self.__stage_opened = False

        # Window
        self._model = WelcomeModel()
        visible = carb.settings.get_settings().get("/persistent/exts/omni.kit.welcome.window/visible_at_startup")
        self._window = WelcomeWindow(self._model, visible=visible)
        self._window.set_visibility_changed_fn(self.__on_visibility_changed)
        if visible:
            self.__center_window()
        else:
            # Listen for the app ready event
            def _on_app_ready(event):
                self.__on_close()
                self._app_ready_sub = None

            startup_event_stream = omni.kit.app.get_app().get_startup_event_stream()
            self._app_ready_sub = startup_event_stream.create_subscription_to_pop_by_type(
                omni.kit.app.EVENT_APP_READY, _on_app_ready, name="Welcome window"
            )

        # Menu item
        self._register_menuitem()

        global _extension_instance
        _extension_instance = self

        # Action
        register_actions(self._ext_name, _extension_instance)

    def on_shutdown(self):
        self._hooks = None
        self._app_ready_sub = None
        deregister_actions(self._ext_name)
        self._model = None
        if self._menu_list is not None:
            omni.kit.menu.utils.remove_menu_items(self._menu_list, WELCOME_MENU_ROOT)
            self._menu_list = None
        if self._window is not None:
            self._window.destroy()
            self._window = None

        global _extension_instance
        _extension_instance = self

    def toggle_window(self):
        self._window.visible = not self._window.visible

    def _register_menuitem(self):
        self._menu_list = [
            MenuItemDescription(
                name="Welcome",
                glyph="none.svg",
                ticked=True,
                ticked_fn=self._on_tick,
                onclick_action=(self._ext_name, "toggle_window"),
                appear_after="Open",
            )
        ]
        omni.kit.menu.utils.add_menu_items(self._menu_list, WELCOME_MENU_ROOT)

    def _on_tick(self):
        if self._window:
            return self._window.visible
        else:
            return False

    def _on_click(self, *args):
        self._window.visible = not self._window.visible

    def __on_visibility_changed(self, visible: bool):
        omni.kit.menu.utils.refresh_menu_items(WELCOME_MENU_ROOT)
        if visible:
            # Always center. Cannot use layout file to change window position because window may be invisible when startup
            self.__center_window()
            return
        self.__on_close()

    def __open_default_stage(self):
        self.__execute_action("omni.kit.stage.templates", "create_new_stage_empty")

    def __on_close(self):
        # When welcome window is closed, setup the Viewport to a complete state
        settings = carb.settings.get_settings()
        if settings.get("/exts/omni.kit.welcome.window/autoSetupOnClose"):
            self.__execute_action("omni.app.setup", "enable_viewport_rendering")

            # Do not open default stage if stage already opened
            if not self.__stage_opened and not omni.usd.get_context().get_stage():
                self.__open_default_stage()

        workflow = settings.get("/exts/omni.kit.welcome.window/default_workflow")
        if workflow:
            self.__active_workflow(workflow)

    def __active_workflow(self, workflow: str):
        carb.log_info(f"Trying to active workflow '{workflow}'...")
        self.__execute_action("omni.app.setup", "active_workflow", workflow)


    def __center_window(self):
        async def __delay_center():
            await omni.kit.app.get_app().next_update_async()
            app_width = ui.Workspace.get_main_window_width()
            app_height = ui.Workspace.get_main_window_height()
            self._window.position_x = (app_width - self._window.frame.computed_width) / 2
            self._window.position_y = (app_height - self._window.frame.computed_height) / 2

        asyncio.ensure_future(__delay_center())

    def __execute_action(self, ext_id: str, action_id: str, *args, **kwargs):
        try:
            import omni.kit.actions.core
            action_registry = omni.kit.actions.core.get_action_registry()
            action_registry.execute_action(ext_id, action_id, *args, **kwargs)
        except Exception as e:
            carb.log_error(f"Failed to execute action '{ext_id}::{action_id}': {e}")


    def register_page(self, ext_id: str, build_fn: Callable[[None], None]) -> bool:
        return self._model.register_page(ext_id, build_fn)

    def show_window(self, visible: bool = True, stage_opened: bool = False) -> None:
        self.__stage_opened = stage_opened or self.__stage_opened
        self._window.visible = visible

def register_page(ext_id: str, build_fn: Callable[[None], None]) -> bool:
    if _extension_instance:
        return _extension_instance.register_page(ext_id, build_fn)
    else:
        return False

def show_window(visible: bool = True, stage_opened: bool = False) -> None:
    if _extension_instance:
        _extension_instance.show_window(visible=visible, stage_opened=stage_opened)
