# 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.
#
import carb
import carb.dictionary
import carb.settings
import omni.kit.app
import omni.ui as ui

from .common import REGISTRIES_CHANGED_EVENT, USER_REGISTRIES_SETTING, get_registries
from .ext_components import SimpleCheckBox
from .styles import get_style
from .utils import copy_text, get_setting

DEFAUT_PUBLISH_SETTING = "app/extensions/registryPublishDefault"
EMPTY_REGISTRY_NAME = "[enter_name]"
REGISTRIES_COLUMNS = ["name", "url", "default", "user"]


class RegistryItem(ui.AbstractItem):
    def __init__(self, name, url, is_user=True, is_default=False, add_dummy=False):
        super().__init__()
        self.name_model = ui.SimpleStringModel(name)
        self.url_model = ui.SimpleStringModel(url)
        self.default_model = ui.SimpleBoolModel(is_default)
        self.is_user = is_user
        self.add_dummy = add_dummy


class RegistriesModel(ui.AbstractItemModel):
    def __init__(self):
        super().__init__()
        self._children = []
        self._load()
        self._add_dummy = RegistryItem("", "", add_dummy=True)
        self._default_changing = False

    def destroy(self):
        self._children = []

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

        return self._children + [self._add_dummy]

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

    def get_item_value_model(self, item, column_id):
        if column_id == 0:
            return item.name_model
        if column_id == 1:
            return item.url_model
        return None

    def _load(self):
        self._children = []

        default_registry = get_setting(DEFAUT_PUBLISH_SETTING, "")

        # Registries
        ui.Label("Registries:")
        for name, url, is_user in get_registries():
            self._children.append(RegistryItem(name, url, is_user, is_default=(name == default_registry)))

        self._item_changed(None)

    def add_empty(self):
        self._children.append(RegistryItem(EMPTY_REGISTRY_NAME, "", is_user=True))
        self._item_changed(None)

    def remove_item(self, item):
        self._children.remove(item)
        self._item_changed(None)
        self.save()

    def save(self):
        registries = []
        for child in self._children:
            if child.is_user:
                name = child.name_model.as_string
                url = child.url_model.as_string
                if name and url:
                    # Auto fill name?
                    if name == EMPTY_REGISTRY_NAME:
                        name = "/".join(url.split("/")[-2:])  # Take last 2
                        child.name_model.as_string = name
                    registries.append({"name": name, "url": url})
        settings_dict = carb.settings.get_settings().get_settings_dictionary("")
        settings_dict[USER_REGISTRIES_SETTING[1:]] = registries

        # Notify registry
        omni.kit.app.get_app().get_message_bus_event_stream().push(REGISTRIES_CHANGED_EVENT)

    def set_default(self, item, value):
        # to avoid recursion
        if self._default_changing:
            return
        self._default_changing = True

        for c in self._children:
            c.default_model.as_bool = False
        item.default_model.as_bool = value
        carb.settings.get_settings().set(DEFAUT_PUBLISH_SETTING, item.name_model.as_string if value else "")

        self._default_changing = False


class EditableDelegate(ui.AbstractItemDelegate):
    def __init__(self):
        super().__init__()
        self._subscription = None
        self._context_menu = ui.Menu("Context menu")

    def destroy(self):
        self._subscription = None
        self._context_menu = None

    def _show_copy_context_menu(self, x, y, button, modifier, text):
        if button != 1:
            return

        self._context_menu.clear()
        with self._context_menu:
            ui.MenuItem("Copy", triggered_fn=lambda: copy_text(text))
        self._context_menu.show()

    def build_widget(self, model, item, column_id, level, expanded):
        """Create a widget per column per item"""
        with ui.HStack(width=20):
            value_model = model.get_item_value_model(item, column_id)
            if column_id in (0, 1):
                stack = ui.ZStack(height=20)
                with stack:
                    label = ui.Label(value_model.as_string, width=500, name=("config" if item.is_user else "builtin"))
                    field = ui.StringField(value_model, visible=False)

                    # Start editing when double clicked
                    stack.set_mouse_double_clicked_fn(
                        lambda x, y, b, _, f=field, l=label, m=model, i=item: self.on_double_click(b, f, l, m, i)
                    )
                    # Right click is copy menu
                    stack.set_mouse_pressed_fn(
                        lambda x, y, b, m, t=value_model.as_string: self._show_copy_context_menu(x, y, b, m, t)
                    )
            elif column_id == 2:
                if not item.add_dummy:
                    with ui.HStack():
                        ui.Spacer(width=10)
                        SimpleCheckBox(
                            item.default_model.as_bool,
                            lambda value, item=item: model.set_default(item, value),
                            model=item.default_model,
                            text="",
                        )
                    ui.Spacer(width=20)
            else:
                if item.is_user:

                    def on_click():
                        if item.add_dummy:
                            model.add_empty()
                        else:
                            model.remove_item(item)

                    with ui.HStack():
                        ui.Spacer(width=10)
                        ui.Button(
                            name=("add" if item.add_dummy else "remove"),
                            style_type_name_override="ItemButton",
                            width=20,
                            height=20,
                            clicked_fn=on_click,
                        )
                        ui.Spacer()

    def on_double_click(self, button, field, label, model, item):
        """Called when the user double-clicked the item in TreeView"""
        if button != 0:
            return

        if item.add_dummy:
            return
        if not item.is_user:
            copy_text(field.model.as_string)
            return

        # Make Field visible when double clicked
        field.visible = True
        field.focus_keyboard()
        # When editing is finished (enter pressed of mouse clicked outside of the viewport)
        self._subscription = field.model.subscribe_end_edit_fn(
            lambda m, f=field, l=label, md=model: self.on_end_edit(m.as_string, f, l, md)
        )

    def on_end_edit(self, text, field, label, model):
        """Called when the user is editing the item and pressed Enter or clicked outside of the item"""
        field.visible = False
        label.text = text
        self._subscription = None
        if text:
            model.save()

    def build_header(self, column_id):
        with ui.HStack():
            ui.Spacer(width=10)
            ui.Label(REGISTRIES_COLUMNS[column_id], name="header")


class ExtsRegistriesWidget:
    def __init__(self):
        self._model = RegistriesModel()
        self._delegate = EditableDelegate()

        with ui.VStack(style=get_style(self)):
            ui.Spacer(height=20)
            with ui.ScrollingFrame(
                height=200,
                horizontal_scrollbar_policy=ui.ScrollBarPolicy.SCROLLBAR_ALWAYS_OFF,
                vertical_scrollbar_policy=ui.ScrollBarPolicy.SCROLLBAR_ALWAYS_OFF,
                style_type_name_override="TreeView",
            ):
                tree_view = ui.TreeView(
                    self._model,
                    delegate=self._delegate,
                    root_visible=False,
                    header_visible=True,
                )
                tree_view.column_widths = [ui.Fraction(1), ui.Fraction(4), ui.Pixel(60), ui.Pixel(40)]

            ui.Spacer(height=10)

    def destroy(self):
        self._model.destroy()
        self._model = None
        self._delegate.destroy()
        self._delegate = None
