# ------------------------------------------------------------------------------
# Class that scans a <table> element
# ------------------------------------------------------------------------------

from robot.api import logger as robotlogger
from accelarobotkeywords.framework.selenium.table.tabledetails import TableDetails


class TableScanner:

    @staticmethod
    def get_table_details(driver, locator):
        """
        Scans the table element and completes the TableDetails object
        :param driver: Seleenium webdriver
        :param locator: the locator for the table element
        """
        robotlogger.info("Scanning html table element")
        result = TableDetails()
        try:
            # get the table element
            table_element = driver.find_element(locator.locator_type, locator.locator_value)
            # check element is really a table
            tag_name = table_element.tag_name.lower()
            if tag_name != 'table':
                raise Exception('Specified element is not a table element')
            # scan column names
            column_names = TableScanner.__scan_columns(table_element)
            if len(column_names) == 0:
                robotlogger.warn('No column names found in table')
            for counter in range(0, len(column_names)):
                result.add_column_name(counter, column_names[counter])
            # scan number of rows
            row_count = TableScanner.__scan_rows(table_element)
            result.set_row_count(row_count)
            # finish
            return result
        except Exception as e:
            robotlogger.error("Error scanning html table: %s" % str(e))
            raise

    @staticmethod
    def search_column_for_value(driver, locator, column_index, search_text, start_row, max_rows):
        """
        Searches a column in the table for the specified search text
        :param driver: Selenium webdriver
        :param locator: Locator of the table element
        :param column_index: column index to search
        :param search_text: text to search in specified column
        :param max_rows: Max number of rows ro search on
        :param start_row: Starting row number
        :return: Row number where earch text was found
        """
        robotlogger.debug("Scanning html table, column %s for value %s" % (str(column_index), search_text))
        try:
            # get the table element
            table_element = driver.find_element(locator.locator_type, locator.locator_value)
            # check element is really a table
            tag_name = table_element.tag_name.lower()
            if tag_name != 'table':
                raise Exception('Specified element is not a table element')
            # perform scan
            row_index = TableScanner.__scan_column_for_value(table_element, column_index, search_text, start_row, max_rows)
            robotlogger.debug('Search text found in row: %s' % row_index)
            return row_index
        except Exception as e:
            robotlogger.error('Error scanning table: %s' % str(e))
            raise

    @staticmethod
    def __scan_columns(table_element):
        """
        Scans the table for columns
        :return Array of column names in order found (their index)
        """
        try:
            # scan for child <th> elements and get their text
            column_names = []
            head_elements = table_element.find_elements(by='xpath', value='descendant::th')
            for head_element in head_elements:
                column_name = head_element.text
                column_names.append(column_name)
            robotlogger.debug("Found table column names: %s" % ' '.join(column_names))
            return column_names
        except Exception as e:
            robotlogger.error("Error scanning column names: %s" % str(e))
            raise

    @staticmethod
    def __scan_rows(table_element):
        """
        Scans the number of rows in the table
        :return: Number of rows found
        """
        try:
            row_elements = table_element.find_elements(by='xpath', value='descendant::tr')
            robotlogger.debug("%s rows found in table" % str(len(row_elements)))
            return len(row_elements)
        except Exception as e:
            robotlogger.error("Error scanning number of rows: %s" % str(e))
            raise

    @staticmethod
    def __scan_column_for_value(table_element, column_index, search_text, start_row, max_rows):
        """
        Scans a table looking for text value in specified column
        :param table_element: WebElement for the the <table> element
        :param column_index: column to search value for
        :param search_text: expected text to search for
        :param max_rows: number of rows to scan or max rows in table
        :param start_row: row number to start from
        :return: row number where cell value found
        """
        try:
            for row_counter in range(start_row, max_rows+1):
                xpath = 'descendant::tr[%s]' % row_counter + '/descendant::td[%s]' % column_index
                cell_element = table_element.find_element(by='xpath', value=xpath)
                cell_text = cell_element.text.strip().lower()
                if cell_text == search_text.lower():
                    robotlogger.debug("Cell value %s found at row %s" % (search_text, str(row_counter)))
                    return row_counter
            raise Exception('Cell value not found')
        except Exception as e:
            robotlogger.error("Error scanning column %s for value %s" % (str(column_index), search_text))
            robotlogger.error(str(e))
            raise
