import sys
import re
import asyncio
import carb
import omni.kit.test
import omni.kit.helper.file_utils as file_utils

from .test_func_media import *
from .test_func_payload import *
from .test_func_templates import *
from .test_recent_menu import test_file_recent_menu_current_stage

class TestMenuFile(omni.kit.test.AsyncTestCase):
    async def setUp(self):
        self._light_path = carb.tokens.get_tokens_interface().resolve("${kit}/exts/omni.kit.menu.file/data/tests/4Lights.usda")
        # load stage so menu shows reopen
        await omni.usd.get_context().open_stage_async(self._light_path)

    async def tearDown(self):
        pass

    async def test_file_menus(self):
        import omni.kit.material.library

        # wait for material to be preloaded so create menu is complete & menus don't rebuild during tests
        await omni.kit.material.library.get_mdl_list_async()
        await ui_test.human_delay()

        menus = omni.kit.menu.utils.get_merged_menus()
        prefix = "file_test"
        to_test = []
        this_module = sys.modules[__name__]
        for key in menus.keys():
            if key.startswith("File"):
                for item in menus[key]['items']:
                    if item.name != "" and item.has_action():
                        key_name = re.sub('\W|^(?=\d)','_', key.lower())
                        func_name = re.sub('\W|^(?=\d)','_', item.name.replace("${kit}/", "").lower())
                        while key_name and key_name[-1] == "_":
                            key_name = key_name[:-1]
                        while func_name and func_name[-1] == "_":
                            func_name = func_name[:-1]

                        test_fn = f"{prefix}_func_{key_name}_{func_name}"
                        if test_fn.startswith("file_test_func_file_open_recent_reopen_current_stage"):
                            test_fn = "file_test_func_file_reopen"
                        elif test_fn.startswith("file_test_func_file_open_recent"):
                            continue

                        try:
                            to_call = getattr(this_module, test_fn)
                            to_test.append((to_call, test_fn, item))
                        except AttributeError:
                            carb.log_error(f"test function \"{test_fn}\" not found")

                        if item.original_menu_item and item.original_menu_item.hotkey:
                            test_fn = f"{prefix}_hotkey_func_{key_name}_{func_name}"
                            try:
                                to_call = getattr(this_module, test_fn)
                                to_test.append((to_call, test_fn, item.original_menu_item))
                            except AttributeError:
                                carb.log_error(f"test function \"{test_fn}\" not found")

        self._future_test = None
        self._required_stage_event = -1
        self._stage_event_sub = omni.usd.get_context().get_stage_event_stream().create_subscription_to_pop(self._on_stage_event, name="omni.usd.menu.file")

        # add in any additional tests
        test_fn = "test_file_recent_menu_current_stage"
        to_call = getattr(this_module, test_fn)
        to_test.append((to_call, test_fn, None))

        for to_call, test_fn, menu_item in to_test:
            file_utils.reset_file_event_queue()

            carb.settings.get_settings().set("/persistent/app/omniverse/lastOpenDirectory", "")
            carb.settings.get_settings().set("/persistent/app/file_exporter/directory", "")
            carb.settings.get_settings().set("/persistent/app/file_exporter/filename", "")
            carb.settings.get_settings().set("/persistent/app/file_exporter/file_extension", "")
            await self.wait_stage_loading()
            await omni.usd.get_context().new_stage_async()
            await ui_test.human_delay()
            print(f"Running test {test_fn}")
            await to_call(self, menu_item)

        self._stage_event_sub = None

    def find_content_file(self, window_name, filename):
        for widget in ui_test.find_all(f"{window_name}//Frame/**/TreeView[*]"):
            for file_widget in widget.find_all(f"**/Label[*]"):
                if file_widget.widget.text==filename:
                    return file_widget
        return None

    def _on_stage_event(self, event):
        if self._future_test and int(self._required_stage_event) == int(event.type) and not self._future_test.done():
            self._future_test.set_result(event.type)

    async def reset_stage_event(self, stage_event):
        self._required_stage_event = stage_event
        self._future_test = asyncio.Future()

    async def wait_for_stage_event(self):
        async def wait_for_event():
            await self._future_test

        try:
            await asyncio.wait_for(wait_for_event(), timeout=30.0)
        except asyncio.TimeoutError:
            carb.log_error(f"wait_for_stage_event timeout waiting for {self._required_stage_event}")

        self._future_test = None
        self._required_stage_event = -1

    async def wait_stage_loading(self):
        while True:
            _, files_loaded, total_files = omni.usd.get_context().get_stage_loading_status()
            if files_loaded or total_files:
                await ui_test.human_delay()
                continue
            break

        await ui_test.human_delay()
