import abc
from typing import Optional, Callable
import carb
import omni.ui as ui

from typing import Optional, Callable
from pxr import Sdf
from omni.kit.viewport.menubar.core import ViewportMenuDelegate, LabelMenuDelegate
from ..camera_widget_delegate_manager import CameraWidgetDelegateManager
from ..utils import get_camera_display


class SingleCameraMenuItemBase(ui.MenuItem):
    """
    A single menu item represent a single camera in the camera list
    """

    def __init__(
        self, camera_path: Sdf.Path, viewport_api, lock_model, root: ui.Menu, triggered_fn: Callable,
        widget_delegate_manager: CameraWidgetDelegateManager
    ):
        self._lock_model = lock_model
        self.__root = root
        self.__camera_path = camera_path
        self.__viewport_api = viewport_api
        self.__lock_button: Optional[ui.Button] = None
        self.__lock_model_sub = None
        self.__options_button = None
        self.__widget_delegate_manager = widget_delegate_manager

        super().__init__(
            get_camera_display(camera_path, viewport_api.stage),
            checkable=True,
            checked=viewport_api.camera_path == camera_path,
            hide_on_click=False,
            triggered_fn=triggered_fn,
            delegate=ViewportMenuDelegate(
                force_checked=False, build_custom_widgets=lambda delegate, item: self._build_menuitem_widgets()
            ),
        )

    @property
    def camera_path(self):
        return self.__camera_path

    @property
    def viewport_api(self):
        return self.__viewport_api

    def destroy(self):
        if self.__widget_delegate_manager:
            self.__widget_delegate_manager.on_parent_destroyed(id(self), False)
            self.__widget_delegate_manager = None
        self.__options_button = None
        super().destroy()
        self.__lock_model_sub = None

    def __build_custom_widgets(self):
        if not self.__widget_delegate_manager:
            return

        try:
            self.__widget_delegate_manager.build_widgets(
                id(self), self.__viewport_api, self.__camera_path, False
            )
        except Exception as e:
            carb.log_error(f"Failed to build widget: {str(e)}")

    def _build_menuitem_widgets(self):
        """Build additional buttons in the camera menu item"""
        ui.Spacer(width=10)
        with ui.VStack(content_clipping=1, width=0):
            # Button to select camera
            self.__options_button = ui.Button(
                style_type_name_override="Menu.Item.Button",
                name="OptionBox",
                width=16,
                height=16,
                image_width=16,
                image_height=16,
                visible=self.checked,
                clicked_fn=self._option_clicked,
            )
        if self._lock_model:
            # Button to toggle lock status
            with ui.VStack(content_clipping=1, width=0):
                self.__lock_button = ui.Button(
                    style_type_name_override="Menu.Item.Button",
                    name=self.__get_lock_button_name(),
                    width=16,
                    height=16,
                    image_width=16,
                    image_height=16,
                    clicked_fn=self.__toggle_camera_lock,
                )

            self.__lock_model_sub = self._lock_model.subscribe_value_changed_fn(self._on_lock_changed)

        self.__build_custom_widgets()

    @abc.abstractmethod
    def _option_clicked(self):
        """Function to implement when the option is clicked. Used by RTX Remix"""
        pass

    def __toggle_camera_lock(self):
        self._lock_model.set_value(not self._lock_model.as_bool)
        self.__lock_button.name = self.__get_lock_button_name()

    def _on_lock_changed(self, model: ui.AbstractValueModel) -> None:
        self.__lock_button.name = self.__get_lock_button_name()

    def __get_lock_button_name(self):
        return "CameraLocked" if self._lock_model.as_bool else "CameraUnlocked"

    def set_checked(self, checked: bool) -> None:
        self.checked = checked
        self.delegate.checked = checked

        # Only show options button when it is current camera
        self.__options_button.visible = checked


class SingleCameraMenuItem(SingleCameraMenuItemBase):
    """
    A single menu item represent a single camera in the camera list
    """

    def _option_clicked(self):
        self.viewport_api.usd_context.get_selection().set_selected_prim_paths([self.camera_path.pathString], True)


class NoCameraMenuItem(ui.MenuItem):
    """
    A single menu item represent a no camera in the camera list
    """

    def __init__(self):
        super().__init__(
            "No camera",
            hide_on_click=False,
            delegate=LabelMenuDelegate(enabled=False, width=120, alignment=ui.Alignment.CENTER),
        )
