# Copyright (c) 2018-2022, 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.
#
__all__ = ["DocumentationBuilderWindow"]

import weakref

import carb
import omni.ui as ui
from omni.kit.widget.filebrowser import FileBrowserItem
from omni.kit.window.filepicker import FilePickerDialog

from .documentation_capture import DocumentationBuilderCapture
from .documentation_catalog import DocumentationBuilderCatalog
from .documentation_md import DocumentationBuilderMd
from .documentation_page import DocumentationBuilderPage
from .documentation_style import get_style


class DocumentationBuilderWindow(ui.Window):
    """The window with the documentation"""

    def __init__(self, title: str, **kwargs):
        """
        ### Arguments

            `title: str`
                The title of the window.

            `filenames: List[str]`
                The list of .md files to process.

            `show_catalog: bool`
                Show catalog pane (default True)
        """
        self._pages = []
        self._catalog = None
        self.__content_frame = None
        self.__content_page = None
        self.__source_filenames = kwargs.pop("filenames", [])
        self._show_catalog = kwargs.pop("show_catalog", True)

        if "width" not in kwargs:
            kwargs["width"] = 1200
        if "height" not in kwargs:
            kwargs["height"] = 720

        if "flags" not in kwargs:
            kwargs["flags"] = ui.WINDOW_FLAGS_NO_SCROLL_WITH_MOUSE | ui.WINDOW_FLAGS_NO_SCROLLBAR

        super().__init__(title, **kwargs)
        self._set_style()
        self.frame.set_build_fn(self.__build_window)

        self.__pick_folder_dialog = None

    def get_page_names(self):
        """Names of all the pages to use in `set_page`"""
        return [p.name for p in self._pages]

    def set_page(self, name: str = None, scroll_to: str = None):
        """
        Set current page

        ### Arguments

            `name: str`
                The name of the page to switch to. If None, it will set the
                first page.

            `scroll_to: str`
                The name of the section to scroll to.
        """
        if self.__content_page and self.__content_page.name == name:
            self.__content_page.scroll_to(scroll_to)
            return

        if name is None:
            page = self._pages[0]
        else:
            page = next((p for p in self._pages if p.name == name), None)
        if page:
            if self.__content_page:
                self.__content_page.destroy()

            with self.__content_frame:
                self.__content_page = DocumentationBuilderPage(page, scroll_to)

    def generate_for_sphinx(self, path: str):
        """
        Produce the set of files and screenshots readable by Sphinx.

        ### Arguments

            `path: str`
                The path to produce the files.
        """
        for page in self._pages:
            for code_block in page.code_blocks:
                image_file = f"{path}/{code_block['name']}.png"
                height = int(code_block["height"] or 200)
                with DocumentationBuilderCapture(image_file, 800, height):
                    with ui.ZStack():
                        # Background
                        ui.Rectangle(name="code_background")

                        # The code
                        with ui.VStack():
                            # flake8: noqa: PLW0212
                            self.__content_page._execute_code(code_block["code"])
                carb.log_info(f"[omni.docs] Generated image: '{image_file}'")

            md_file = f"{path}/{page.name}.md"
            with open(md_file, "w", encoding="utf-8") as f:
                f.write(page.source)
            carb.log_info(f"[omni.docs] Generated md file: '{md_file}'")

    def destroy(self):
        if self.__pick_folder_dialog:
            self.__pick_folder_dialog.destroy()
            self.__pick_folder_dialog = None
        if self._catalog:
            self._catalog.destroy()
            self._catalog = None

        for page in self._pages:
            page.destroy()
        self._pages = []

        if self.__content_page:
            self.__content_page.destroy()
            self.__content_page = None

        self.__content_frame = None

        super().destroy()

    @staticmethod
    def get_style():
        """
        Deprecated: use omni.kit.documentation.builder.get_style()
        """
        return get_style()

    def _set_style(self):
        self.frame.set_style(get_style())

    def __build_window(self):
        """Called to build the widgets of the window"""
        self.__reload_pages()

    def __on_export(self):
        """Open Pick Folder dialog to add compounds"""
        if self.__pick_folder_dialog:
            self.__pick_folder_dialog.destroy()

        self.__pick_folder_dialog = FilePickerDialog(
            "Pick Output Folder",
            apply_button_label="Use This Folder",
            click_apply_handler=self.__on_apply_folder,
            item_filter_options=["All Folders (*)"],
            item_filter_fn=self.__on_filter_folder,
        )

    def __on_apply_folder(self, filename: str, root_path: str):
        """Called when the user press "Use This Folder" in the pick folder dialog"""
        self.__pick_folder_dialog.hide()
        self.generate_for_sphinx(root_path)

    @staticmethod
    def __on_filter_folder(item: FileBrowserItem) -> bool:
        """Used by pick folder dialog to hide all the files"""
        return item.is_folder

    def __reload_pages(self):
        """Called to reload all the pages"""
        # Properly destroy all the past pages
        for page in self._pages:
            page.destroy()
        self._pages = []

        # Reload the pages at the drawing time
        for page_source in self.__source_filenames:
            if page_source.endswith(".md"):
                self._pages.append(DocumentationBuilderMd(page_source))

        def keep_page_name(page, catalog_data):
            """Puts the page name to the catalog data"""
            for c in catalog_data:
                c["page"] = page
                keep_page_name(page, c["children"])

        catalog_data = []
        for page in self._pages:
            catalog = page.catalog
            keep_page_name(page.name, catalog)
            catalog_data += catalog

        # The layout
        with ui.HStack():
            if self._show_catalog:
                self._catalog = DocumentationBuilderCatalog(
                    catalog_data,
                    weakref.ref(self),
                    horizontal_scrollbar_policy=ui.ScrollBarPolicy.SCROLLBAR_ALWAYS_OFF,
                    vertical_scrollbar_policy=ui.ScrollBarPolicy.SCROLLBAR_ALWAYS_OFF,
                    width=300,
                    settings_menu=[{"name": "Export Markdown", "clicked_fn": self.__on_export}],
                )

            with ui.ZStack():
                ui.Rectangle(name="content_background")
                with ui.ScrollingFrame(
                    horizontal_scrollbar_policy=ui.ScrollBarPolicy.SCROLLBAR_ALWAYS_OFF,
                    style={"ScrollingFrame": {"background_color": 0x0}},
                ):
                    self.__content_frame = ui.Frame(height=1)

        self.set_page()
