import time
import robot
import robot.utils
from .keywordgroup import KeywordGroup


class Waiting(KeywordGroup):

    # def wait_until_page_contains_element(self, locator, timeout=30):
    #     """
    #     Let the script wait until the page contain the expected element.
    #     The possible locator could be
    #     id, xpath, link text, partial link text, name, tag name, class name, css selector
    #     Example:
    #     | wait until page contains element | id=kw | 30 |
    #     """
    #     (prefix, criteria) = self._parse_locator(locator)
    #     self._safe_find(by=prefix, value=criteria, timeout=timeout)
    #
    # def wait_until_page_not_contains_element(self, locator, timeout=30):
    #     """
    #     Let the script wait until the page not contain the expected element.
    #     The possible locator could be
    #     id, xpath, link text, partial link text, name, tag name, class name, css selector
    #     Example:
    #     | wait until page not contains element | id=kw | 30 |
    #     """
    #     WebDriverWait(self._get_current_browser(), timeout, poll_frequency=0.1) \
    #         .until(ec.invisibility_of_element_located(locator=locator))

    def wait_until_element_is_visible(self, locator, timeout=None, error=None):
        """Waits until element specified with `locator` is visible.
        Fails if `timeout` expires before the element is visible. See
        `introduction` for more information about `timeout` and its
        default value.
        `error` can be used to override the default error message.
        See also `Wait Until Page Contains`, `Wait Until Page Contains
        Element`, `Wait For Condition` and BuiltIn keyword `Wait Until Keyword
        Succeeds`.
        """
        def check_visibility():
            visible = self._is_visible(locator)
            if visible:
                return
            elif visible is None:
                return error or "Element locator '%s' did not match any elements after %s" % (locator, self._format_timeout(timeout))
            else:
                return error or "Element '%s' was not visible in %s" % (locator, self._format_timeout(timeout))
        self._wait_until_no_error(timeout, check_visibility)

    def wait_until_page_contains(self, text, timeout=None, error=None):
        """Waits until `text` appears on current page.
        Fails if `timeout` expires before the text appears. See
        `introduction` for more information about `timeout` and its
        default value.
        `error` can be used to override the default error message.
        See also `Wait Until Page Does Not Contain`,
        `Wait Until Page Contains Element`,
        `Wait Until Page Does Not Contain Element` and
        BuiltIn keyword `Wait Until Keyword Succeeds`.
        """
        if not error:
            error = "Text '%s' did not appear in <TIMEOUT>" % text
        self._wait_until(timeout, error, self._is_text_present, text)

    def wait_until_page_does_not_contain(self, text, timeout=None, error=None):
        """Waits until `text` disappears from current page.
        Fails if `timeout` expires before the `text` disappears. See
        `introduction` for more information about `timeout` and its
        default value.
        `error` can be used to override the default error message.
        See also `Wait Until Page Contains`,
        `Wait Until Page Contains Element`,
        `Wait Until Page Does Not Contain Element` and
        BuiltIn keyword `Wait Until Keyword Succeeds`.
        """

        def check_present():
            present = self._is_text_present(text)
            if not present:
                return
            else:
                return error or "Text '%s' did not disappear in %s" % (text, self._format_timeout(timeout))

        self._wait_until_no_error(timeout, check_present)

    def wait_until_page_contains_element(self, locator, timeout=None, error=None):
        """Waits until element specified with `locator` appears on current page.
        Fails if `timeout` expires before the element appears. See
        `introduction` for more information about `timeout` and its
        default value.
        `error` can be used to override the default error message.
        See also `Wait Until Page Contains`,
        `Wait Until Page Does Not Contain`
        `Wait Until Page Does Not Contain Element`
        and BuiltIn keyword `Wait Until Keyword Succeeds`.
        """
        if not error:
            error = "Element '%s' did not appear in <TIMEOUT>" % locator
        self._wait_until(timeout, error, self._is_element_present, locator)

    def wait_until_page_does_not_contain_element(self, locator, timeout=None, error=None):
        """Waits until element specified with `locator` disappears from current page.
        Fails if `timeout` expires before the element disappears. See
        `introduction` for more information about `timeout` and its
        default value.
        `error` can be used to override the default error message.
        See also `Wait Until Page Contains`,
        `Wait Until Page Does Not Contain`,
        `Wait Until Page Contains Element` and
        BuiltIn keyword `Wait Until Keyword Succeeds`.
        """

        def check_present():
            present = self._is_element_present(locator)
            if not present:
                return
            else:
                return error or "Element '%s' did not disappear in %s" % (locator, self._format_timeout(timeout))

        self._wait_until_no_error(timeout, check_present)

    # Private

    def _wait_until(self, timeout, error, function, *args):
        error = error.replace('<TIMEOUT>', self._format_timeout(timeout))

        def wait_func():
            return None if function(*args) else error

        self._wait_until_no_error(timeout, wait_func)

    def _wait_until_no_error(self, timeout, wait_func, *args):
        timeout = robot.utils.timestr_to_secs(timeout) if timeout is not None else self._timeout_in_secs
        maxtime = time.time() + timeout
        while True:
            timeout_error = wait_func(*args)
            if not timeout_error:
                return
            if time.time() > maxtime:
                self.log_source()
                raise AssertionError(timeout_error)
            time.sleep(0.2)

    def _format_timeout(self, timeout):
        timeout = robot.utils.timestr_to_secs(timeout) if timeout is not None else self._timeout_in_secs
        return robot.utils.secs_to_timestr(timeout)