## Copyright (c) 2018-2019, 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 omni.kit.test
import omni.kit.app
import carb.settings

from unittest.mock import patch, Mock
from omni.kit import ui_test
from omni.kit.widget.filebrowser import TREEVIEW_PANE, LISTVIEW_PANE
from omni.kit.widget.nucleus_connector import NucleusConnectorExtension
from ..dialog import FilePickerDialog
from ..widget import FilePickerWidget
from ..api import FilePickerAPI
from ..view import FilePickerView
from ..model import FilePickerModel
from ..test_helper import FilePickerTestHelper

import omni.client

class TestAddDeleteRenameServer(omni.kit.test.AsyncTestCase):
    """Testing FilePickerView.*server* methods"""
    async def setUp(self):
        self.previous_retry_values = omni.client.set_retries(0, 0, 0)
        # OM-78933: Init_view mounts localhost, which emits a BOOKMARK_ADDED_EVENT.  This corrupts the event stream
        # and subsequently adds unaccounted calls to mock_add_bookmark below.  The simple fix is to add a few frames
        # here in order to steer clear of the initialization step.
        for _ in range(10):
            await omni.kit.app.get_app().next_update_async()

    async def tearDown(self):
        omni.client.set_retries(*self.previous_retry_values)

    async def test_add_server(self):
        """Testing FilePickerView.add_server adds the server"""
        with patch("omni.client.add_bookmark") as mock_add_bookmark:
            test_name, test_path = "ov-added", "omniverse://ov-added"
            under_test = FilePickerView("test-view")

            self.assertFalse(under_test.has_connection_with_name(test_name, collection="omniverse"))
            under_test.add_server(test_name, test_path)
            self.assertTrue(under_test.has_connection_with_name(test_name, collection="omniverse"))

            await omni.kit.app.get_app().next_update_async()
            mock_add_bookmark.assert_called_with(test_name, test_path)

    async def test_delete_server(self):
        """Testing FilePickerView.delete_server deletes the server"""
        with patch("omni.client.remove_bookmark") as mock_remove_bookmark:
            test_name, test_path = "ov-test", "omniverse://ov-test"
            under_test = FilePickerView("test-view")

            self.assertFalse(under_test.has_connection_with_name(test_name, collection="omniverse"))
            model = under_test.add_server(test_name, test_path)
            self.assertTrue(under_test.has_connection_with_name(test_name, collection="omniverse"))
            # Confirm that after being deleted, test_path is no longer in the connections liset
            under_test.delete_server(model.root)
            self.assertFalse(under_test.has_connection_with_name(test_name, collection="omniverse"))

            await omni.kit.app.get_app().next_update_async()
            mock_remove_bookmark.assert_called_with(test_name)

    async def test_rename_server(self):
        """Testing FilePickerView.rename_server renames the server"""
        with patch("omni.client.add_bookmark") as mock_add_bookmark,\
            patch("omni.client.remove_bookmark") as mock_remove_bookmark:
            test_name, test_path, test_rename = "ov-test", "omniverse://ov-test", "ov-renamed"
            under_test = FilePickerView("test-view")

            model = under_test.add_server(test_name, test_path)
            self.assertTrue(under_test.has_connection_with_name(test_name, collection="omniverse"))

            await omni.kit.app.get_app().next_update_async()
            under_test.rename_server(model.root, test_rename)
            self.assertFalse(under_test.has_connection_with_name(test_name, collection="omniverse"))
            self.assertTrue(under_test.has_connection_with_name(test_rename, collection="omniverse"))

            # Confirm that previous name deleted, then new name added
            for _ in range(8):
                await omni.kit.app.get_app().next_update_async()
            mock_remove_bookmark.assert_called_with(test_name)
            mock_add_bookmark.assert_called_with(test_rename, test_path)

    async def test_log_out_server(self):
        """Testing FilePickerView.log_out_server logs out the server"""
        with patch("omni.client.sign_out") as mock_sign_out:
            test_name, test_path = "ov-test", "omniverse://ov-test"
            under_test = FilePickerView("test-view")

            self.assertFalse(under_test.has_connection_with_name(test_name, collection="omniverse"))
            under_test.add_server(test_name, test_path)
            self.assertTrue(under_test.has_connection_with_name(test_name, collection="omniverse"))

            # Confirm that sign out is correctly called
            model = under_test.get_connection_with_url(test_path)
            under_test.log_out_server(model)
            await omni.kit.app.get_app().next_update_async()
            mock_sign_out.assert_called_with(test_path)


class TestUpdateConnectionsFromApi(omni.kit.test.AsyncTestCase):
    """Testing FilePickerAPI.*connections* methods"""
    async def setUp(self):
        self.previous_retry_values = omni.client.set_retries(0, 0, 0)
        # OM-78933: Init_view mounts localhost, which emits a BOOKMARK_ADDED_EVENT.  This corrupts the event stream
        # and subsequently adds unaccounted calls to mock_add_bookmark below.  The simple fix is to add a few frames
        # here in order to steer clear of the initialization step.
        for _ in range(10):
            await omni.kit.app.get_app().next_update_async()

    async def tearDown(self):
        omni.client.set_retries(*self.previous_retry_values)

    async def test_add_connections(self):
        """Testing FilePickerAPI.add_connections writes new connection to persistent settings"""
        with patch("omni.client.add_bookmark") as mock_add_bookmark:
            test_name, test_path = "ov-test", "omniverse://ov-test"
            test_view = FilePickerView("test-view")
            under_test = FilePickerAPI(FilePickerModel(), test_view)

            self.assertFalse(test_view.has_connection_with_name(test_name, collection="omniverse"))
            under_test.add_connections({test_name: test_path})
            self.assertTrue(test_view.has_connection_with_name(test_name, collection="omniverse"))

            await omni.kit.app.get_app().next_update_async()
            mock_add_bookmark.assert_called_with(test_name, test_path)

    async def test_update_connections_when_settings_changed(self):
        """Testing FilePickerAPI.subscribe_client_bookmarks_changed updates connections in view when bookmarks changed"""
        test_view = FilePickerView("test-view")
        under_test = FilePickerAPI(FilePickerModel(), test_view)
        under_test.subscribe_client_bookmarks_changed()

        # Assert connection not initially in the view
        test_name, test_path = "ov-ax98zjbm", "omniverse://ov-ax98zjbm"
        self.assertFalse(test_view.has_connection_with_name(test_name, collection="omniverse"))

        # Confirm that when an external source adds a bookmark, the view updates appropriately
        omni.client.add_bookmark(test_name, test_path)
        await ui_test.human_delay(10)
        self.assertTrue(test_view.has_connection_with_name(test_name, collection="omniverse"))

        # Confirm that after deleting connection from settings, the view updates appropriately
        success = False
        for attempt in range(200):
            omni.client.remove_bookmark(test_name)
            await ui_test.human_delay(10)
            if not test_view.has_connection_with_name(test_name, collection="omniverse"):
                success = True
                break
        self.assertTrue(success)


class TestAddNewConnectionUI(omni.kit.test.AsyncTestCase):
    """Testing UX fr adding new connections"""
    async def setUp(self):
        self.previous_retry_values = omni.client.set_retries(0, 0, 0)

    async def tearDown(self):
        omni.client.set_retries(*self.previous_retry_values)

    async def test_treeview_add_new_connection_clicked(self):
        """Testing that when 'Add New Connection ...' item in treeview is clicked, launches connection flow"""
        dialog = FilePickerDialog("test_treeview_add_new_connection_clicked", treeview_identifier="FilePicker")
        await ui_test.human_delay(10)

        async with FilePickerTestHelper(dialog._widget) as filepicker_helper:
            with patch.object(NucleusConnectorExtension, "connect_with_dialog") as mock_connector:
                item = await filepicker_helper.get_item_async("FilePicker", "Add New Connection ...", pane=TREEVIEW_PANE)
                await item.click()
                await ui_test.human_delay(10)
                mock_connector.assert_called_once()

    async def test_gridview_add_new_connection_clicked(self):
        """Testing that when 'Add New Connection ...' item in gridview is clicked, launches connection flow"""
        dialog = FilePickerDialog("test_gridview_add_new_connection_clicked", treeview_identifier="FilePicker")
        await ui_test.human_delay(10)

        async with FilePickerTestHelper(dialog._widget) as filepicker_helper:
            with patch.object(NucleusConnectorExtension, "connect_with_dialog") as mock_connector:
                item = await filepicker_helper.get_item_async("FilePicker", "Omniverse", pane=TREEVIEW_PANE)
                await item.click()
                await ui_test.human_delay(10)

                item = await filepicker_helper.get_item_async("FilePicker", "Add New Connection ...", pane=LISTVIEW_PANE)
                await item.click()
                await ui_test.human_delay(10)
                mock_connector.assert_called_once()
                           
    async def test_not_has_default_localhost(self):
        """Testing that there is no default localhost server in filepicker dialog"""
        # TODO: some test machine has the localhost bookmark stored in omniverse.toml
        # and the omni client's remove_bookmark is not time stable, so it cause flaky failed
        # comment his until find a better test solution
        return
        omni.client.remove_bookmark("localhost")
        await ui_test.human_delay(100)

        dialog = FilePickerDialog("test_not_has_default_localhost", treeview_identifier="FilePicker")
        await ui_test.human_delay(10)

        test_view = dialog._widget._view
        self.assertFalse(test_view.has_connection_with_name("localhost", collection="omniverse"))
       
    async def test_default_connection_setting(self):
        """Testing that default connection setting is working as expect"""
        test_connections = {
            "ov-test": "omniverse://ov-test",
            "test": "omniverse://ov-test",
        }
        old_setting_value = carb.settings.get_settings().get("exts/omni.kit.window.filepicker/mounted_servers")
        carb.settings.get_settings().set("exts/omni.kit.window.filepicker/mounted_servers", test_connections)
        await ui_test.human_delay(30)
        widget = FilePickerWidget("test_default_connection_setting")
        await ui_test.human_delay(30)

        test_view = widget._view
        for test_name in test_connections.keys():
            success = False
            for attempt in range(100):
                await ui_test.human_delay(10)
                if test_view.has_connection_with_name(test_name, collection="omniverse"):
                    success = True
                    break
            self.assertTrue(success)
        await ui_test.human_delay(10)
        carb.settings.get_settings().set("exts/omni.kit.window.filepicker/mounted_servers", old_setting_value)
        for test_name in test_connections.keys():
            # Confirm that after deleting connection from settings, the view updates appropriately
            omni.client.remove_bookmark(test_name)
            success = False
            for attempt in range(100):
                await ui_test.human_delay(10)
                if not test_view.has_connection_with_name(test_name, collection="omniverse"):
                    success = True
                    break
            self.assertTrue(success)
        widget.destroy()

    async def test_show_add_new_connection_setting(self):
        """Testing that show add new connection setting is working as expect"""
        old_setting_value = carb.settings.get_settings().get("exts/omni.kit.window.filepicker/show_add_new_connection")
        carb.settings.get_settings().set("exts/omni.kit.window.filepicker/show_add_new_connection", True)
        await ui_test.human_delay(30)
        widget = FilePickerWidget("test_show_add_new_connection_setting")
        await ui_test.human_delay(30)

        test_view = widget._view
        mock_handle = Mock()
        collections = test_view._collections.values()
        for coll in collections:
            if coll and coll.children:
                for name, conn in coll.children.items():
                    if test_view._is_placeholder(conn):
                        self.assertEqual(name, "Add New Connection ...")
                        mock_handle()
        mock_handle.assert_called_once()
        widget.destroy()
        mock_handle.reset_mock()
        await ui_test.human_delay(10)
        carb.settings.get_settings().set("exts/omni.kit.window.filepicker/show_add_new_connection", False)
        widget = FilePickerWidget("test_not_show_add_new_connection_setting")
        await ui_test.human_delay(30)
        test_view = widget._view
        mock_handle = Mock()
        collections = test_view._collections.values()
        for coll in collections:
            if coll and coll.children:
                for name, conn in coll.children.items():
                    if test_view._is_placeholder(conn):
                        mock_handle()
        mock_handle.assert_not_called()
        widget.destroy()
        carb.settings.get_settings().set("exts/omni.kit.window.filepicker/show_add_new_connection", old_setting_value)
        await ui_test.human_delay(30)
