class TestlioAutomationTest(unittest.TestCase):
    log = None
    name = None
    driver = None
    caps = {}
    default_implicit_wait = 20
    IS_IOS = False
    IS_ANDROID = False
    capabilities = {}
    passed = False

    def parse_test_script_dir_and_filename(self, filename):
        # used in each test script to get its own path
        pth = os.path.dirname(os.path.abspath(filename))
        pth = os.path.basename(os.path.normpath(pth))
        ndx = str.index(filename, '.py')
        filename = filename[:ndx]
        filename = filename.split('/')[-1]
        return pth, filename

    @classmethod
    def setUpClass(cls):
        platform = os.getenv('PLATFORM') or (
            'android' if os.getenv('ANDROID_HOME') else 'ios')
        if platform == 'ios' and 'TESTDROID_SERVER_URL' in os.environ or 'VIRTUAL_ENV' in os.environ:
            cls.capabilities['appium-version'] = os.getenv('APPIUM_VERSION')
            cls.capabilities['platformName'] = os.getenv('PLATFORM') or (
                'android' if os.getenv('ANDROID_HOME') else 'ios')
            cls.capabilities['deviceName'] = os.getenv('DEVICE') or os.getenv(
                'DEVICE_DISPLAY_NAME')
            cls.capabilities['app'] = os.getenv('APP') or os.getenv(
                'APPIUM_APPFILE')
            cls.capabilities['newCommandTimeout'] = os.getenv(
                'NEW_COMMAND_TIMEOUT')
            cls.capabilities['fullReset'] = 'true'

            # iOS 10, XCode8 support
            if os.getenv('AUTOMATION_NAME'):
                cls.capabilities["automationName"] = os.getenv(
                    'AUTOMATION_NAME')
            if os.getenv('UDID'):
                cls.capabilities["udid"] = os.getenv('UDID')

            executor = os.getenv('EXECUTOR', 'http://*****:*****@text,"ABCDEFGHIJKLMNOPQRSTUVWXYZ", "abcdefghijklmnopqrstuvwxyz"),"{0}") or contains(translate(@content-desc,"ABCDEFGHIJKLMNOPQRSTUVWXYZ", "abcdefghijklmnopqrstuvwxyz"),"{0}")]'
                        .format(str(kwargs['name']).lower()))))
            elif kwargs.has_key('class_name'):
                return wait.until(
                    EC.element_to_be_clickable(
                        (By.CLASS_NAME, kwargs['class_name'])))
            elif kwargs.has_key('id'):
                return wait.until(
                    EC.element_to_be_clickable((By.ID, kwargs['id'])))
            elif kwargs.has_key('accessibility_id'):
                return wait.until(
                    EC.element_to_be_clickable(
                        (By.ID, kwargs['accessibility_id'])))
            elif kwargs.has_key('xpath'):
                return wait.until(
                    EC.element_to_be_clickable((By.XPATH, kwargs['xpath'])))
            else:
                raise TypeError('Element is not found')
        except:
            return False

    def get_element(self, **kwargs):
        # self.dismiss_update_popup()
        self.set_implicit_wait(1)
        if kwargs.has_key('timeout'):
            timeout = kwargs['timeout']
        else:
            timeout = 10
        wait = WebDriverWait(self.driver,
                             timeout,
                             poll_frequency=0.5,
                             ignored_exceptions=[
                                 ElementNotVisibleException,
                                 ElementNotSelectableException,
                                 StaleElementReferenceException,
                                 TimeoutException, WebDriverException
                             ])
        try:
            if kwargs.has_key('name'):
                return wait.until(
                    EC.presence_of_element_located((
                        By.XPATH,
                        '//*[contains(translate(@text,"ABCDEFGHIJKLMNOPQRSTUVWXYZ", "abcdefghijklmnopqrstuvwxyz"),"{0}") or contains(translate(@content-desc,"ABCDEFGHIJKLMNOPQRSTUVWXYZ", "abcdefghijklmnopqrstuvwxyz"),"{0}")]'
                        .format(str(kwargs['name']).lower()))))
            elif kwargs.has_key('class_name'):
                return wait.until(
                    EC.presence_of_element_located(
                        (By.CLASS_NAME, kwargs['class_name'])))
            elif kwargs.has_key('id'):
                return wait.until(
                    EC.presence_of_element_located((By.ID, kwargs['id'])))
            elif kwargs.has_key('accessibility_id'):
                return wait.until(
                    EC.presence_of_element_located(
                        (By.ID, kwargs['accessibility_id'])))
            elif kwargs.has_key('xpath'):
                return wait.until(
                    EC.presence_of_element_located(
                        (By.XPATH, kwargs['xpath'])))
            else:
                raise TypeError('Element is not found')
        except:
            return False

    def get_visible_element(self, **kwargs):
        # self.dismiss_update_popup()
        self.set_implicit_wait(1)
        if kwargs.has_key('timeout'):
            timeout = kwargs['timeout']
        else:
            timeout = 10
        wait = WebDriverWait(self.driver,
                             timeout,
                             poll_frequency=0.5,
                             ignored_exceptions=[
                                 ElementNotVisibleException,
                                 ElementNotSelectableException,
                                 StaleElementReferenceException,
                                 TimeoutException, WebDriverException
                             ])
        try:
            if kwargs.has_key('name'):
                return wait.until(
                    EC.visibility_of_element_located((
                        By.XPATH,
                        '//*[contains(translate(@text,"ABCDEFGHIJKLMNOPQRSTUVWXYZ", "abcdefghijklmnopqrstuvwxyz"),"{0}") or contains(translate(@content-desc,"ABCDEFGHIJKLMNOPQRSTUVWXYZ", "abcdefghijklmnopqrstuvwxyz"),"{0}")]'
                        .format(str(kwargs['name']).lower()))))
            elif kwargs.has_key('class_name'):
                return wait.until(
                    EC.visibility_of_element_located(
                        (By.CLASS_NAME, kwargs['class_name'])))
            elif kwargs.has_key('id'):
                return wait.until(
                    EC.visibility_of_element_located((By.ID, kwargs['id'])))
            elif kwargs.has_key('accessibility_id'):
                return wait.until(
                    EC.visibility_of_element_located(
                        (By.ID, kwargs['accessibility_id'])))
            elif kwargs.has_key('xpath'):
                return wait.until(
                    EC.visibility_of_element_located(
                        (By.XPATH, kwargs['xpath'])))
            else:
                raise TypeError('Element is not found')
        except:
            return False

    def get_elements(self, **kwargs):
        # self.dismiss_update_popup()
        self.set_implicit_wait(1)
        if kwargs.has_key('timeout'):
            timeout = kwargs['timeout']
        else:
            timeout = 10
        wait = WebDriverWait(self.driver,
                             timeout,
                             poll_frequency=0.5,
                             ignored_exceptions=[
                                 ElementNotVisibleException,
                                 ElementNotSelectableException,
                                 StaleElementReferenceException,
                                 TimeoutException, WebDriverException
                             ])
        try:
            if kwargs.has_key('name'):
                return wait.until(
                    EC.presence_of_all_elements_located((
                        By.XPATH,
                        '//*[contains(translate(@text,"ABCDEFGHIJKLMNOPQRSTUVWXYZ", "abcdefghijklmnopqrstuvwxyz"),"{0}") or contains(translate(@content-desc,"ABCDEFGHIJKLMNOPQRSTUVWXYZ", "abcdefghijklmnopqrstuvwxyz"),"{0}")]'
                        .format(str(kwargs['name']).lower()))))
            elif kwargs.has_key('class_name'):
                return wait.until(
                    EC.presence_of_all_elements_located(
                        (By.CLASS_NAME, kwargs['class_name'])))
            elif kwargs.has_key('id'):
                return wait.until(
                    EC.presence_of_all_elements_located((By.ID, kwargs['id'])))
            elif kwargs.has_key('accessibility_id'):
                return wait.until(
                    EC.presence_of_all_elements_located(
                        (By.ID, kwargs['accessibility_id'])))
            elif kwargs.has_key('xpath'):
                return wait.until(
                    EC.presence_of_all_elements_located(
                        (By.XPATH, kwargs['xpath'])))
            else:
                raise TypeError('Elements are not found')
        except:
            return []

    def is_element_on_screen_area(self, element):
        if element:
            element_x = element.location['x']
            element_y = element.location['y']

            element_w = element.size['width']
            element_h = element.size['height']

            display_w = self.driver.get_window_size()['width']
            display_h = self.driver.get_window_size()['height']

            return (element_x > 0 and (
                (element_x + element_w) <= display_w)) and (element_y > 0 and (
                    (element_y + element_h) <= display_h))
        return False

    def is_element_visible(self, element):
        if element:
            if self.IS_IOS:
                return (element.location['x'] > 0 or
                        element.location['y'] > 0) and element.is_displayed()
            else:
                return element.is_displayed()
        return False

    def dismiss_update_popup(self):
        try:
            if "update your os" in str(self.driver.page_source).lower():
                self.driver.back()
        except:
            pass

    def set_implicit_wait(self, wait_time=-1):
        """
        Wrapper that sets implicit wait, defaults to self.default_implicit_wait
        """
        if wait_time == -1:
            wait_time = self.default_implicit_wait

        try:
            self.driver.implicitly_wait(wait_time)
        except:
            pass

    def screenshot(self):
        import time
        import subprocess
        if str(self.capabilities['platformName']).lower() == 'ios':
            time.sleep(
                1
            )  # wait for animations to complete before taking a screenshot

            try:
                path = "{dir}/{name}-{time}.png".format(dir=SCREENSHOTS_DIR,
                                                        name=self.name,
                                                        time=time.mktime(
                                                            time.gmtime()))

                if not os.environ['IOS_UDID'] and not os.environ['UDID']:
                    raise Exception('screenshot failed. IOS_UDID not provided')

                if os.environ['IOS_UDID']:
                    subprocess.call("echo $IOS_UDID &> consoleoutput.txt",
                                    shell=True)
                    subprocess.call("idevicescreenshot -u $IOS_UDID \"" +
                                    path + "\" &> consoleoutput2.txt",
                                    shell=True)
                else:
                    subprocess.call("echo $UDID &> consoleoutput.txt",
                                    shell=True)
                    subprocess.call("idevicescreenshot -u $UDID \"" + path +
                                    "\" &> consoleoutput2.txt",
                                    shell=True)

                return path
            except:
                return False
        elif str(self.capabilities['platformName']).lower() == 'android':
            time.sleep(
                1
            )  # wait for animations to complete before taking a screenshot

            if not os.path.exists(SCREENSHOTS_DIR):
                os.makedirs(SCREENSHOTS_DIR)

            path = "{dir}/{name}-{time}.png".format(dir=SCREENSHOTS_DIR,
                                                    name=self.name,
                                                    time=time.mktime(
                                                        time.gmtime()))
            try:
                self.driver.save_screenshot(path)
                return path
            except:
                subprocess.call(
                    "adb shell screencap -p | perl -pe 's/\x0D\x0A/\x0A/g' > "
                    + path,
                    shell=True)
                return path

    def validate_tcp(self,
                     host,
                     from_timestamp=None,
                     to_timestamp=None,
                     uri_contains=None,
                     body_contains=None,
                     screenshot=None,
                     request_present=None):
        """Save TCP validation data for post processing"""

        screenshot = self.screenshot() if screenshot else None
        self.event.validate_tcp(host, from_timestamp, to_timestamp,
                                uri_contains, body_contains, screenshot,
                                request_present)

    def click(self, element=None, screenshot=False, **kwargs):
        """
        Perform click on element. If element is not provided try to search
        by paramaters in kwargs.
        """
        def _click(element):
            try:
                if element:
                    try:
                        readable_name = element.text or \
                               element.get_attribute('name') or \
                               element.get_attribute('resourceId') or \
                               element.get_attribute('content-desc') or \
                               element.get_attribute('value') or \
                               element.tag_name
                        kwargs['Element'] = str(readable_name).replace(
                            ": u'", ": '")
                    except:
                        pass
                    element.click()
                else:
                    # self.event._log_info(self.event._event_data("*** WARNING ***  Element is absent"))
                    self.event.error()
                screenshot_path = self.screenshot() if screenshot else None
                self.event.click(screenshot=screenshot_path,
                                 **self._format_element_data(**kwargs))
            except:
                self.event.error()
                raise

        return self._element_action(_click, element, **kwargs)

    def send_keys(self, data, element=None, screenshot=False, **kwargs):
        """
        Send keys to an element. If element is not provided try to search
        by parameters in kwargs.
        """
        def _send_keys(element):
            try:
                element.send_keys(data)
                screenshot_path = self.screenshot() if screenshot else None
                self.event.send_keys(data,
                                     screenshot=screenshot_path,
                                     **self._format_element_data(**kwargs))
            except:
                self.event.error()
                raise

        return self._element_action(_send_keys, element, **kwargs)

    def send_text(self, data, element=None, screenshot=False, **kwargs):
        """
        Set text to an element. If element is not provided try to search
        by parameters in kwargs.
        This method is using methods provided by Appium, not native send_keys
        """
        def _send_keys(element):
            try:
                if str(self.caps['platformName']).lower() == 'android':
                    element.send_text(data)
                elif str(self.caps['platformName']).lower() == 'ios':
                    element.set_value(data.replace('\n', '', 1))
                screenshot_path = self.screenshot() if screenshot else None
                self.event.send_keys(data,
                                     screenshot=screenshot_path,
                                     **self._format_element_data(**kwargs))
            except:
                self.event.error()
                raise

        return self._element_action(_send_keys, element, **kwargs)

    def wait_and_accept_alert(self, timeout=20):
        """Wait for alert and accept"""
        def accept_alert():
            try:
                self.driver.switch_to_alert().accept()
                self.event.accept_alert()
            except:
                self.event.error()
                raise

        self._alert_action(timeout, accept_alert)

    def wait_and_dismiss_alert(self, timeout=20):
        """Wait for alert and dismiss"""
        def dismiss_alert():
            try:
                self.driver.switch_to_alert().dismiss()
                self.event.dismiss_alert()
            except:
                self.event.error()
                raise

        self._alert_action(timeout, dismiss_alert)

    def assertTrue(self, *args, **kwargs):
        try:
            super(TestlioAutomationTest, self).assertTrue(*args, **kwargs)
        except:
            self.event.error()
            raise

    # Message is a requirement, then the client is able to verify if we are making the right checks
    def assertEqualWithScreenShot(self,
                                  expected,
                                  actual,
                                  screenshot=False,
                                  msg=None):
        screenshot = self.screenshot() if screenshot else None
        self.event.assertion(msg, screenshot=screenshot)
        if expected != actual:
            if msg is None:
                self.assertTrue(False)
            else:
                self.assertTrue(False, msg)
            self.event.error()
        else:
            self.assertTrue(True)

    def assertTrueWithScreenShot(self, condition, screenshot=False, msg=None):
        screenshot = self.screenshot() if screenshot else None
        self.event.assertion(msg, screenshot=screenshot)
        if msg is None:
            self.assertTrue(condition)
        else:
            self.assertTrue(condition, msg)

    def exists(self, **kwargs):
        """
        Finds element by name or xpath
        advanced:
            call using an element:
            my_layout = self.get_element(class_name='android.widget.LinearLayout')
            self.exists(name='Submit', driver=my_layout)
        """
        if kwargs.has_key('element'):
            try:
                return kwargs['element']
            except:
                return False
        else:
            try:
                return self.get_element(**kwargs)
            except NoSuchElementException:
                return False
                # finally:
                #     self.driver.implicitly_wait(self.default_implicit_wait)

    def not_exists(self, **kwargs):
        """
        Waits until element does not exist.  Waits up to <implicit_wait> seconds.
        Optional parameter: timeout=3 if you only want to wait 3 seconds.  Default=30
        Return: True or False
        """
        if 'timeout' in kwargs:
            timeout = (kwargs['timeout'])
        else:
            timeout = 30

        start_time = time()

        kwargs['timeout'] = 0  # we want exists to return immediately
        while True:
            elem = self.exists(**kwargs)
            if not elem:
                return True

            if time() - start_time > timeout:
                return False

    def verify_blind(self,
                     list_of_text_keys,
                     case_sensitive=True,
                     strict=False,
                     screenshot=True,
                     with_timeout=5):
        sleep(with_timeout)
        page_source = self.driver.page_source
        if screenshot:
            self.event.assertion(data="*** BLIND VERIFICATION ***",
                                 screenshot=self.screenshot())
        for key in list_of_text_keys:
            if not case_sensitive:
                key = str(key).lower()
                page_source = str(page_source).lower()

            if strict:
                self.assertTrueWithScreenShot(
                    key in page_source,
                    screenshot=False,
                    msg="Element '%s' is expected to be existed on the page" %
                    key)
            else:
                if key not in page_source:
                    errors = os.environ[SOFT_ASSERTIONS_FAILURES]

                    self.event.assertion(
                        data="*** FAILURE *** Element is missing: '%s'" % key,
                        screenshot=self.screenshot())

                    errors += "\nElement is missing: '%s'" % key
                    os.environ[SOFT_ASSERTIONS_FAILURES] = errors
                    os.environ[FAILURES_FOUND] = "true"
                else:
                    self.event._log_info(
                        self.event._event_data(
                            "*** SUCCESS *** Element is presented: '%s'" %
                            key))

    def verify_exists(self, strict=False, **kwargs):
        screenshot = False
        if kwargs.has_key('screenshot') and kwargs['screenshot']:
            screenshot = True

        if kwargs.has_key('readable_name'):
            selector = kwargs['readable_name']
        elif kwargs.has_key('name'):
            selector = kwargs['name']
        elif kwargs.has_key('accessibility_id'):
            selector = kwargs['accessibility_id']
        elif kwargs.has_key('class_name'):
            selector = kwargs['class_name']
        elif kwargs.has_key('id'):
            selector = kwargs['id']
        elif kwargs.has_key('xpath'):
            selector = kwargs['xpath']
        elif kwargs.has_key('element'):
            selector = str(kwargs['element'])
        else:
            selector = 'Element not found'

        if strict:
            self.assertTrueWithScreenShot(
                self.exists(**kwargs),
                screenshot=screenshot,
                msg="Should see element with text or selector: '%s'" %
                selector)
        else:
            if not self.exists(**kwargs):
                errors = os.environ[SOFT_ASSERTIONS_FAILURES]

                self.event.assertion(
                    data="*** FAILURE *** Element is missing: '%s'" % selector,
                    screenshot=self.screenshot())

                errors += "\nElement is missing: '%s'" % selector
                os.environ[SOFT_ASSERTIONS_FAILURES] = errors
                os.environ[FAILURES_FOUND] = "true"
            else:
                self.event._log_info(
                    self.event._event_data(
                        "*** SUCCESS *** Element is presented: '%s'" %
                        selector))

    def verify_not_exists(self, strict=False, **kwargs):
        screenshot = False
        if kwargs.has_key('screenshot') and kwargs['screenshot']:
            screenshot = True

        if kwargs.has_key('name'):
            selector = kwargs['name']
        elif kwargs.has_key('accessibility_id'):
            selector = kwargs['accessibility_id']
        elif kwargs.has_key('class_name'):
            selector = kwargs['class_name']
        elif kwargs.has_key('id'):
            selector = kwargs['id']
        elif kwargs.has_key('xpath'):
            selector = kwargs['xpath']
        elif kwargs.has_key('element'):
            selector = str(kwargs['element'])
        else:
            selector = 'Element not found'

        if strict:
            self.assertTrueWithScreenShot(
                not self.exists(**kwargs),
                screenshot=screenshot,
                msg="Should NOT see element with text or selector: '%s'" %
                selector)
        else:
            if self.exists(**kwargs):
                errors = os.environ[SOFT_ASSERTIONS_FAILURES]
                self.event.assertion(
                    data=
                    "*** FAILURE *** Element is presented but should not be: '%s'"
                    % selector,
                    screenshot=self.screenshot())
                errors += "\nElement is presented but should not be: '%s'" % selector
                os.environ[SOFT_ASSERTIONS_FAILURES] = errors
                os.environ[FAILURES_FOUND] = "true"
            else:
                self.event._log_info(
                    self.event._event_data(
                        "*** SUCCESS *** Element missing: '%s'" % selector))

    def verify_not_equal(self, obj1, obj2, screenshot=False):
        self.assertTrueWithScreenShot(obj1 != obj2,
                                      screenshot=screenshot,
                                      msg="'%s' should NOT equal '%s'" %
                                      (obj1, obj2))

    def verify_equal(self, obj1, obj2, screenshot=False):
        self.assertTrueWithScreenShot(obj1 == obj2,
                                      screenshot=screenshot,
                                      msg="'%s' should EQUAL '%s'" %
                                      (obj1, obj2))

    def _element_action(self, action, element=None, **kwargs):
        """Find element if not supplied and send to action delegate"""

        element = element if element else self._find_element(**kwargs)

        action(element)
        return element

    def _find_element(self, **kwargs):
        """
        Finds element by name or xpath
        """

        if kwargs.has_key('timeout'):
            self.set_implicit_wait(int(kwargs['timeout']))

        if kwargs.has_key('name'):
            return self._find_element_by_xpath(
                '//*[@text="{0}" or @content-desc="{1}"]'.format(
                    kwargs['name'], kwargs['name']))
        elif kwargs.has_key('class_name'):
            return self._find_element_by_class_name(kwargs['class_name'])
        elif kwargs.has_key('id'):
            return self._find_element_by_id(kwargs['id'])
        elif kwargs.has_key('accessibility_id'):
            return self._find_element_by_accessibility_id(
                kwargs['accessibility_id'])
        elif kwargs.has_key('xpath'):
            return self._find_element_by_xpath(kwargs['xpath'])
        else:
            raise TypeError('Neither element `name` or `xpath` provided')

    def _find_element_by_name(self, name):
        try:
            return self.driver.find_element_by_name(name)
        except:
            self.event.error(element_name=name)
            raise

    def _find_element_by_xpath(self, xpath):
        try:
            return self.driver.find_element_by_xpath(xpath)
        except:
            self.event.error(element_xpath=xpath)
            raise

    def _find_element_by_class_name(self, class_name):
        try:
            return self.driver.find_element_by_class_name(class_name)
        except:
            self.event.error(element_name=class_name)
            raise

    def _find_element_by_id(self, element_id):
        try:
            return self.driver.find_element_by_id(element_id)
        except:
            self.event.error(id=element_id)
            raise

    def _find_element_by_accessibility_id(self, element_accessibility_id):
        try:
            return self.driver.find_element_by_accessibility_id(
                element_accessibility_id)
        except:
            self.event.error(id=element_accessibility_id)
            raise

    def _alert_is_present(self):
        """Check if alert message is present"""

        try:
            self.driver.switch_to_alert().text
            return True
        except Exception, e:
            return False