#    Licensed under the Apache License, Version 2.0 (the "License"); you may
#    not use this file except in compliance with the License. You may obtain
#    a copy of the License at
#
#         http://www.apache.org/licenses/LICENSE-2.0
#
#    Unless required by applicable law or agreed to in writing, software
#    distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
#    WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
#    License for the specific language governing permissions and limitations
#    under the License.
from selenium.common import exceptions
from selenium.webdriver.common import by

from openstack_dashboard.test.integration_tests.regions import baseregion


class CannotClickMenuItemException(Exception):
    pass


class NavigationAccordionRegion(baseregion.BaseRegion):
    """Navigation menu located in the left."""
    _project_access_security_locator = (
        by.By.CSS_SELECTOR, 'a[href*="/project/access_and_security/"]')
    _settings_change_password_locator = (
        by.By.CSS_SELECTOR, 'a[href*="/settings/password//"]')
    _project_bar_locator = (by.By.XPATH,
                            ".//*[@id='main_content']//div[contains(text(),"
                            "'Project')]")

    MAX_MENU_ITEM_CLICK_TRIES = 100

    @property
    def project_bar(self):
        return self._get_element(*self._project_bar_locator)

    _first_level_item_selected_locator = (
        by.By.CSS_SELECTOR, 'li.openstack-dashboard.selenium-active > a')
    _second_level_item_selected_locator = (
        by.By.CSS_SELECTOR, 'li.nav-header.selenium-active > a')

    _first_level_item_xpath_template = (
        "//li[contains(concat('', @class, ''), 'openstack-dashboard') "
        "and contains(., '%s')]/a")
    _second_level_item_xpath_template = (
        "//li[contains(concat('', @class, ''), 'nav-header') "
        "and contains(., '%s')]/a")
    _third_level_item_xpath_template = (
        "//li[contains(concat('', @class, ''), 'openstack-panel') and "
        "contains(., '%s')]/a")

    def _get_first_level_item_locator(self, text):
        return (by.By.XPATH,
                self._first_level_item_xpath_template % text)

    def _get_second_level_item_locator(self, text):
        return (by.By.XPATH,
                self._second_level_item_xpath_template % text)

    def _get_third_level_item_locator(self, text):
        return (by.By.XPATH,
                self._third_level_item_xpath_template % text)

    def get_first_level_selected_item(self):
        if self._is_element_present(*self._first_level_item_selected_locator):
            return self._get_element(*self._first_level_item_selected_locator)
        else:
            return None

    def get_second_level_selected_item(self):
        if self._is_element_present(*self._second_level_item_selected_locator):
            return self._get_element(*self._second_level_item_selected_locator)
        else:
            return None

    @property
    def access_security(self):
        return self._get_element(*self._project_access_security_locator)

    @property
    def change_password(self):
        return self._get_element(*self._settings_change_password_locator)

    def _click_menu_item(self, text, loc_craft_func, get_selected_func=None):
        """Click on menu item if not selected.

        Menu animation that visualize transition from one selection to
        another take some time - if clicked on item during this animation
        nothing happens, therefore it is necessary to do this in a loop.
        """

        if not get_selected_func:
            self._click_item(text, loc_craft_func)
        else:
            for _ in range(self.MAX_MENU_ITEM_CLICK_TRIES):
                selected_item = get_selected_func()
                if selected_item and text == selected_item.text:
                    break

                # In case different item was chosen previously scroll it,
                # because otherwise selenium will complain with
                # MoveTargetOutOfBoundsException
                if selected_item:
                    selected_item.click()
                self._click_item(text, loc_craft_func)
            else:

                # One should never get in here,
                # this suggest that something is wrong
                raise CannotClickMenuItemException()

    def _click_item(self, text, loc_craft_func):
        """Click on item."""
        item_locator = loc_craft_func(text)
        item = self._get_element(*item_locator)
        item.click()

    def click_on_menu_items(self, first_level=None,
                            second_level=None,
                            third_level=None):
        if first_level:
            self._click_menu_item(first_level,
                                  self._get_first_level_item_locator,
                                  self.get_first_level_selected_item)
        if second_level:
            self._click_menu_item(second_level,
                                  self._get_second_level_item_locator,
                                  self.get_second_level_selected_item)

        # it is not checked that third level item is clicked because behaviour
        # of the third menu layer is buggy => always click
        if third_level:
            self._click_menu_item(third_level,
                                  self._get_third_level_item_locator)


class DropDownMenuRegion(baseregion.BaseRegion):
    """Drop down menu region."""

    _menu_items_locator = (by.By.CSS_SELECTOR,
                           'ul.dropdown-menu > li > *')
    _menu_first_child_locator = (by.By.CSS_SELECTOR,
                                 'a[data-toggle="dropdown"]')

    @property
    def menu_items(self):
        self.open()
        menu_items = self._get_elements(*self._menu_items_locator)
        return menu_items

    def is_open(self):
        """Returns True if drop down menu is open, otherwise False."""
        return "open" in self.src_elem.get_attribute('class')

    def open(self):
        """Opens menu by clicking on the first child of the source element."""
        if self.is_open() is False:
            self._get_element(*self._menu_first_child_locator).click()


class UserDropDownMenuRegion(DropDownMenuRegion):
    """Drop down menu located in the right side of the topbar,
    contains links to settings and help.
    """
    _menu_first_child_locator = (by.By.CSS_SELECTOR, '*')
    _settings_link_locator = (by.By.CSS_SELECTOR,
                              'a[href*="/settings/"]')
    _help_link_locator = (by.By.CSS_SELECTOR,
                          'ul#editor_list li:nth-of-type(2) > a')
    _logout_link_locator = (by.By.CSS_SELECTOR,
                            'a[href*="/auth/logout/"]')

    @property
    def settings_link(self):
        return self._get_element(*self._settings_link_locator)

    @property
    def help_link(self):
        return self._get_element(*self._help_link_locator)

    @property
    def logout_link(self):
        return self._get_element(*self._logout_link_locator)

    def click_on_settings(self):
        self.open()
        self.settings_link.click()

    def click_on_help(self):
        self.open()
        self.help_link.click()

    def click_on_logout(self):
        self.open()
        self.logout_link.click()


class TabbedMenuRegion(baseregion.BaseRegion):

    _tab_locator = (by.By.CSS_SELECTOR, 'a')
    _default_src_locator = (by.By.CSS_SELECTOR, 'ul.nav.nav-tabs')

    def switch_to(self, index=0):
        self._get_elements(*self._tab_locator)[index].click()


class ProjectDropDownRegion(DropDownMenuRegion):

    _menu_first_child_locator = (by.By.CSS_SELECTOR, '*')
    _menu_items_locator = (by.By.CSS_SELECTOR,
                           'div.context-lists a')

    def click_on_project(self, name):
        for item in self.menu_items:
            if item.text == name:
                item.click()
                break
        else:
            raise exceptions.NoSuchElementException(
                "Not found element with text: %s" % name)
