import asyncio
import carb
import omni.usd
import omni.ui as ui
from omni.kit import ui_test
from carb.input import KeyboardInput
from omni.kit.menu.utils import MenuItemDescription, MenuLayout
from omni.kit.viewport.utility import get_active_viewport_window, get_num_viewports

DPI_SCALE_OVERRIDE_SETTING = "/app/window/dpiScaleOverride"

def get_hide_ui():
    hide_ui = carb.settings.get_settings().get("/app/window/hideUi")
    if hide_ui == None:
        return False
    return hide_ui


async def common_test_func_window_viewport(tester, menu_item = None):
    # XXX: Unclear where this API is used, but it originally took an unused menu_item
    if menu_item:
        carb.log_error("common_test_func_window_new_viewport_window does not use a menu_item")

    # Get the default Viewport name, most likely 'Viewport'
    default_vp_window_name = carb.settings.get_settings().get("/exts/omni.kit.viewport.window/startup/windowName")
    default_vp_window_name = default_vp_window_name or "Viewport"

    window_menu = ui_test.get_menubar().find_menu("Window")
    tester.assertIsNotNone(window_menu)

    # There will always be a "Viewport" menu item, but on legacy it is a single item otherwise a sub-menu container
    viewport_menu = window_menu.find_menu("Viewport")
    tester.assertIsNotNone(viewport_menu)

    # Build up the sub-menu (and full menu-path) that will be used
    vp_menu_entry_name = f"{default_vp_window_name} 1"
    full_default_vp_menu_path = f"Window/Viewport/{default_vp_window_name} 1"

    # If it doesnt't exists as a child of the "Viewport" menu-item, assume legacy
    vp_show_menu_entry = viewport_menu.find_menu(vp_menu_entry_name)
    if not vp_show_menu_entry:
        viewport_menu = window_menu
        vp_menu_entry_name = default_vp_window_name
        full_default_vp_menu_path = f"Window/{default_vp_window_name}"
        vp_show_menu_entry = window_menu.find_menu(vp_menu_entry_name)

    # Needs to be valid by now
    tester.assertIsNotNone(vp_show_menu_entry)
    # Pre-check that the Viewport Window can also be found
    viewport_window = get_active_viewport_window(window_name=default_vp_window_name)
    tester.assertIsNotNone(viewport_window)

    # verify initial visibility and check state
    tester.assertTrue(viewport_window.visible)
    tester.assertTrue(vp_show_menu_entry.widget.checked)

    # use menu
    await ui_test.menu_click(full_default_vp_menu_path)
    await ui_test.human_delay()

    # verify
    tester.assertFalse(viewport_window.visible)
    tester.assertFalse(vp_show_menu_entry.widget.checked)

    # use menu
    await ui_test.menu_click(full_default_vp_menu_path)
    await ui_test.human_delay()

    # verify
    tester.assertTrue(viewport_window.visible)
    tester.assertTrue(vp_show_menu_entry.widget.checked)


async def common_test_func_window_ui_toggle_visibility(tester, menu_item: MenuItemDescription):
    # verify default state
    tester.assertTrue(get_hide_ui() == False)

    # use menu
    await ui_test.menu_click("Window/UI Toggle Visibility")
    await ui_test.human_delay(50)

    # verify state
    tester.assertTrue(get_hide_ui() == True)

    # cannot use menu so use hotkey instead
    await ui_test.emulate_keyboard_press(menu_item.hotkey[1], menu_item.hotkey[0])
    await ui_test.human_delay(50)

    # verify  state
    tester.assertTrue(get_hide_ui() == False)


async def common_test_hotkey_func_window_ui_toggle_visibility(tester, menu_item: MenuItemDescription):
    # verify default state
    tester.assertTrue(get_hide_ui() == False)

    # hotkey
    await ui_test.emulate_keyboard_press(menu_item.hotkey[1], menu_item.hotkey[0])
    await ui_test.human_delay(50)

    # verify state
    tester.assertTrue(get_hide_ui() == True)

    # hotkey
    await ui_test.emulate_keyboard_press(menu_item.hotkey[1], menu_item.hotkey[0])
    await ui_test.human_delay(50)

    # verify  state
    tester.assertTrue(get_hide_ui() == False)


async def common_test_func_window_fullscreen_mode(tester, menu_item: MenuItemDescription):
    # verify default state
    tester.assertTrue(omni.appwindow.get_default_app_window().is_fullscreen() == False)

    # use menu
    await ui_test.menu_click("Window/Fullscreen Mode")
    await ui_test.human_delay(50)

    # verify state
    tester.assertTrue(omni.appwindow.get_default_app_window().is_fullscreen() == True)

    # cannot use menu so use hotkey instead
    await ui_test.emulate_keyboard_press(menu_item.hotkey[1], menu_item.hotkey[0])
    await ui_test.human_delay(50)

    # verify  state
    tester.assertTrue(omni.appwindow.get_default_app_window().is_fullscreen() == False)


async def common_test_hotkey_func_window_fullscreen_mode(tester, menu_item: MenuItemDescription):
    # verify default state
    tester.assertTrue(omni.appwindow.get_default_app_window().is_fullscreen() == False)

    # hotkey
    await ui_test.emulate_keyboard_press(menu_item.hotkey[1], menu_item.hotkey[0])
    await ui_test.human_delay(50)

    # verify state
    tester.assertTrue(omni.appwindow.get_default_app_window().is_fullscreen() == True)

    # hotkey
    await ui_test.emulate_keyboard_press(menu_item.hotkey[1], menu_item.hotkey[0])
    await ui_test.human_delay(50)

    # verify  state
    tester.assertTrue(omni.appwindow.get_default_app_window().is_fullscreen() == False)


async def common_test_func_window_new_viewport_window(tester, menu_item = None):
    # XXX: Unclear where this API is used, but it originally took an unused menu_item
    if menu_item:
        carb.log_error("common_test_func_window_new_viewport_window does not use a menu_item")

    # verify default state
    tester.assertEqual(get_num_viewports(), 1)

    # All functionaliyt contained in Window menu
    window_menu = ui_test.get_menubar().find_menu("Window")
    tester.assertIsNotNone(window_menu)

    # There will always be a "Viewport" menu item, but on legacy it is a single item otherwise a sub-menu container
    viewport_menu = window_menu.find_menu("Viewport")
    tester.assertIsNotNone(viewport_menu)

    # use menu
    if viewport_menu.find_menu("Viewport 2"):
        await ui_test.menu_click("Window/Viewport/Viewport 2")
    else:
        await ui_test.menu_click("Window/New Viewport Window")
    await ui_test.human_delay()

    # verify
    tester.assertEqual(get_num_viewports(), 2)

    # close new viewport window
    viewport_window = get_active_viewport_window(window_name="Viewport 2")
    if viewport_window:
        viewport_window.visible = False
        viewport_window.destroy()
        del viewport_window


async def common_test_func_window_dpi_scale_increase(tester, menu_item: MenuItemDescription):
    # verify default state
    tester.assertEqual(omni.appwindow.get_default_app_window().get_dpi_scale_override(), 1.0)

    # use menu
    await ui_test.menu_click("Window/DPI Scale/Increase")
    await ui_test.human_delay(50)

    # verify state
    tester.assertEqual(omni.appwindow.get_default_app_window().get_dpi_scale_override(), 1.5)

    # cannot use menu so use hotkey instead
    await ui_test.emulate_keyboard_press(menu_item.hotkey[1], menu_item.hotkey[0])
    await ui_test.human_delay(50)

    # verify state
    tester.assertEqual(omni.appwindow.get_default_app_window().get_dpi_scale_override(), 2.0)

    # reset state
    carb.settings.get_settings().set(DPI_SCALE_OVERRIDE_SETTING, 1.0)
    tester.assertEqual(omni.appwindow.get_default_app_window().get_dpi_scale_override(), 1.0)


async def common_test_hotkey_func_window_dpi_scale_increase(tester, menu_item: MenuItemDescription):
    # verify default state
    tester.assertEqual(omni.appwindow.get_default_app_window().get_dpi_scale_override(), 1.0)

    # hotkey
    await ui_test.emulate_keyboard_press(menu_item.hotkey[1], menu_item.hotkey[0])
    await ui_test.human_delay(50)

    # verify state
    tester.assertEqual(omni.appwindow.get_default_app_window().get_dpi_scale_override(), 1.5)

    # hotkey
    await ui_test.emulate_keyboard_press(menu_item.hotkey[1], menu_item.hotkey[0])
    await ui_test.human_delay(50)

    # verify state
    tester.assertEqual(omni.appwindow.get_default_app_window().get_dpi_scale_override(), 2.0)

    # reset state
    carb.settings.get_settings().set(DPI_SCALE_OVERRIDE_SETTING, 1.0)
    tester.assertEqual(omni.appwindow.get_default_app_window().get_dpi_scale_override(), 1.0)


async def common_test_func_window_dpi_scale_decrease(tester, menu_item: MenuItemDescription):
    # verify default state
    tester.assertEqual(omni.appwindow.get_default_app_window().get_dpi_scale_override(), 1.0)

    # use menu
    await ui_test.menu_click("Window/DPI Scale/Decrease")
    await ui_test.human_delay(50)

    # verify state
    tester.assertEqual(omni.appwindow.get_default_app_window().get_dpi_scale_override(), 0.5)

    # cannot use menu so use hotkey instead
    await ui_test.emulate_keyboard_press(menu_item.hotkey[1], menu_item.hotkey[0])
    await ui_test.human_delay(50)

    # verify state - still 0.5 because it gets clamped to the min
    tester.assertEqual(omni.appwindow.get_default_app_window().get_dpi_scale_override(), 0.5)

    # reset state
    carb.settings.get_settings().set(DPI_SCALE_OVERRIDE_SETTING, 1.0)
    tester.assertEqual(omni.appwindow.get_default_app_window().get_dpi_scale_override(), 1.0)


async def common_test_hotkey_func_window_dpi_scale_decrease(tester, menu_item: MenuItemDescription):
    # verify default state
    tester.assertEqual(omni.appwindow.get_default_app_window().get_dpi_scale_override(), 1.0)

    # hotkey
    await ui_test.emulate_keyboard_press(menu_item.hotkey[1], menu_item.hotkey[0])
    await ui_test.human_delay(50)

    # verify state
    tester.assertEqual(omni.appwindow.get_default_app_window().get_dpi_scale_override(), 0.5)

    # hotkey
    await ui_test.emulate_keyboard_press(menu_item.hotkey[1], menu_item.hotkey[0])
    await ui_test.human_delay(50)

    # verify state - still 0.5 because it gets clamped to the min
    tester.assertEqual(omni.appwindow.get_default_app_window().get_dpi_scale_override(), 0.5)

    # reset state
    carb.settings.get_settings().set(DPI_SCALE_OVERRIDE_SETTING, 1.0)
    tester.assertEqual(omni.appwindow.get_default_app_window().get_dpi_scale_override(), 1.0)


async def common_test_func_window_dpi_scale_reset(tester, menu_item: MenuItemDescription):
    # verify default state
    tester.assertEqual(omni.appwindow.get_default_app_window().get_dpi_scale_override(), 1.0)

    # use menu
    await ui_test.menu_click("Window/DPI Scale/Reset")
    await ui_test.human_delay(50)

    # verify state
    tester.assertEqual(omni.appwindow.get_default_app_window().get_dpi_scale_override(), -1.0)

    # reset state
    carb.settings.get_settings().set(DPI_SCALE_OVERRIDE_SETTING, 1.0)
    tester.assertEqual(omni.appwindow.get_default_app_window().get_dpi_scale_override(), 1.0)
