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

import os


def _encode_content(content):
    if type(content) == str:
        payload = bytes(content.encode("utf-8"))
    elif type(content) != type(None):
        payload = bytes(content)
    else:
        payload = bytes()

    return payload


class MenuOptions:
    def __init__(self, **kwargs):
        self._dialog = None
        self._current_filename = None
        self._current_dir = None
        self.load_data = kwargs.pop("load_data", None)
        self.save_data = kwargs.pop("save_data", None)
        self.get_file_path = kwargs.pop("get_file_path", None)

    def destroy(self):
        if self._dialog:
            self._dialog.destroy()

    def __on_apply_save(self, filename: str, dir: str):
        """Called when the user press "Export" in the pick filename dialog"""

        # don't accept as long as no filename is selected
        if not filename or not dir:
            return

        self._dialog.hide()

        # add the file extension if missing
        if self._dialog.current_filter_option == 0 and not filename.lower().endswith(".py"):
            filename += ".py"

        self._current_filename = filename
        self._current_dir = dir

        # add a trailing slash for the client library
        if dir[-1] != os.sep:
            dir = dir + os.sep
        current_export_path = omni.client.combine_urls(dir, filename)

        self.save(current_export_path)

    def __on_apply_load(self, filename: str, dir: str):
        """Called when the user press "Export" in the pick filename dialog"""

        # don't accept as long as no filename is selected
        if not filename or not dir:
            return

        self._dialog.hide()

        # add a trailing slash for the client library
        if dir[-1] != os.sep:
            dir = dir + os.sep
        # OM-93082: Fix issue with omni.client.combine_urls will eat up the subfolder path for omniverse urls if
        #  the dirname is ending with "\" on windows
        dir = dir.replace("\\", "/")

        current_path = omni.client.combine_urls(dir, filename)
        if omni.client.stat(current_path)[0] != omni.client.Result.OK:
            # add the file extension if missing
            if self._dialog.current_filter_option == 0 and not filename.lower().endswith(".py"):
                filename += ".py"
                current_path = omni.client.combine_urls(dir, filename)
                if omni.client.stat(current_path)[0] != omni.client.Result.OK:
                    # Still can't find
                    return

        self._current_filename = filename
        self._current_dir = dir

        self.open(current_path)

    def __menu_filter_files(self, item: FileBrowserItem) -> bool:
        """Used by pick folder dialog to hide all the files"""
        if not item or item.is_folder:
            return True

        if self._dialog.current_filter_option == 0:
            # Show only files with listed extensions
            if item.path.endswith(".py"):
                return True
            else:
                return False
        else:
            # Show All Files (*)
            return True

    def menu_open(self):
        """Open "Open" dialog"""
        if self._dialog:
            self._dialog.destroy()

        self._dialog = FilePickerDialog(
            "Open...",
            allow_multi_selection=False,
            apply_button_label="Open",
            click_apply_handler=self.__on_apply_load,
            item_filter_options=["Python Files (*.py)", "Any file (*.*)"],
            item_filter_fn=self.__menu_filter_files,
            current_filename=self._current_filename,
            current_directory=self._current_dir,
        )

    def menu_save(self):
        file_path = self.get_file_path()
        if file_path:
            self.save(file_path)
        else:
            self.menu_save_as()

    def menu_save_as(self):
        """Open "Save As" dialog"""
        if self._dialog:
            self._dialog.destroy()

        self._dialog = FilePickerDialog(
            "Save As...",
            allow_multi_selection=False,
            apply_button_label="Save",
            click_apply_handler=self.__on_apply_save,
            item_filter_options=["Python Files (*.py)", "Any file (*.*)"],
            item_filter_fn=self.__menu_filter_files,
            current_filename=self._current_filename,
            current_directory=self._current_dir,
        )

    def save(self, filename: str):
        """Save the current model to external file"""
        data = self.save_data(filename)
        if not data:
            return

        # Save to the file
        result = omni.client.write_file(filename, _encode_content(data))
        if result != omni.client.Result.OK:
            carb.log_error(f"[omni.kit.window.script_editor] Cannot write to {filename}, error code: {result}")
            return
        carb.log_info(f"[omni.kit.window.script_editor] The scripts have saved to {filename}")

    def open(self, filename: str):
        """Load the model from the file"""
        result, _, content = omni.client.read_file(filename)

        if result != omni.client.Result.OK:
            carb.log_error(f"[omni.kit.window.script_editor] Can't read file {filename}, error code: {result}")
            return

        data = memoryview(content).tobytes().decode("utf-8")
        self.load_data(filename, data)
