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

from typing import List, Callable, Union
from omni.kit.window.filepicker import FilePickerAPI
from omni.kit.widget.filebrowser import FileBrowserItem
from .file_ops import add_file_open_handler, delete_file_open_handler, get_file_open_handler


class ContentBrowserAPI(FilePickerAPI):
    """This class defines the API methods for :obj:`ContentBrowserWidget`."""
    def __init__(self):
        super().__init__()
        self._on_selection_changed_subs = set()

    def add_import_menu(self, name: str, glyph: str, click_fn: Callable, show_fn: Callable) -> str:
        """
        Adds menu item, with corresponding callbacks, to the Import combo box.

        Args:
            name (str): Name of the menu item, this name must be unique across the menu.
            glyph (str): Associated glyph to display for this menu item.
            click_fn (Callable): This callback function is executed when the menu item is clicked. Function signature:
                void fn(name: str, path: str), where name is menu name and path is absolute path to clicked item.
            show_fn (Callable): Returns True to display this menu item. Function signature: bool fn(path: str).
                For example, test filename extension to decide whether to display a 'Play Sound' action.

        Returns:
            str: Name of menu item if successful, None otherwise.

        """
        if self.tool_bar:
            import_menu = self.tool_bar.import_menu
            if import_menu:
                return import_menu.add_menu_item(name, glyph, click_fn, show_fn)
        return None

    def delete_import_menu(self, name: str):
        """
        Deletes the menu item, with the given name, from the Import combo box.

        Args:
            name (str): Name of the menu item.

        """
        if self.tool_bar:
            import_menu = self.tool_bar.import_menu
            if import_menu:
                import_menu.delete_menu_item(name)

    def add_file_open_handler(self, name: str, open_fn: Callable, file_type: Union[int, Callable]) -> str:
        """
        Registers callback/handler to open a file of matching type.

        Args:
            name (str): Unique name of handler.
            open_fn (Callable): This function is executed when a matching file is selected for open, i.e. double clicked,
                right mouse menu open, or path submitted to browser bar.  Function signature:
                void open_fn(full_path: str), full_path is the file's system path.
            file_type (Union[int, func]): Can either be an enumerated int that is one of: [FILE_TYPE_USD,
                FILE_TYPE_IMAGE, FILE_TYPE_SOUND, FILE_TYPE_TEXT, FILE_TYPE_VOLUME] or a more general boolean function
                that returns True if this function should be activated on the given file.  Function
                signature: bool file_type(full_path: str).

        Returns:
            str: Name if successful, None otherwise.

        """
        return add_file_open_handler(name, open_fn, file_type)

    def delete_file_open_handler(self, name: str):
        """
        Unregisters the named file open handler.

        Args:
            name (str): Name of the handler.

        """
        delete_file_open_handler(name)

    def get_file_open_handler(self, url: str) -> Callable:
        """
        Returns the matching file open handler for the given file path.

        Args:
            url str: The url of the file to open.

        """
        return get_file_open_handler(url)

    def subscribe_selection_changed(self, fn: Callable):
        """
        Subscribes to file selection changes.

        Args:
            fn (Callable): callback function when file selection changed.

        """
        self._on_selection_changed_subs.add(fn)

    def unsubscribe_selection_changed(self, fn: Callable):
        """
        Unsubscribe this callback from selection changes.

        Args:
            fn (Callable): callback function when file selection changed.

        """
        if fn in self._on_selection_changed_subs:
            self._on_selection_changed_subs.remove(fn)

    def _notify_selection_subs(self, pane: int, selected: List[FileBrowserItem]):
        for fn in self._on_selection_changed_subs or []:

            async def async_cb(pane: int, selected: List[FileBrowserItem]):
                # Making sure callback was not removed between async schedule/execute
                if fn in self._on_selection_changed_subs:
                    fn(pane, selected)

            asyncio.ensure_future(async_cb(pane, selected))

    def destroy(self):
        super().destroy()
        if self._on_selection_changed_subs:
            self._on_selection_changed_subs.clear()
