import getRoleType from '../aria/get-role-type';
import getExplicitRole from '../aria/get-explicit-role';
import isFocusable from '../dom/is-focusable';
import findUp from '../dom/find-up';
import getElementCoordinates from '../dom/get-element-coordinates';
import getViewportSize from '../dom/get-viewport-size';

/**
 * Determines whether a table is a data table
 * @method isDataTable
 * @memberof axe.commons.table
 * @instance
 * @param  {HTMLTableElement} node The table to test
 * @return {Boolean}
 * @see http://asurkov.blogspot.co.uk/2011/10/data-vs-layout-table.html
 */
function isDataTable(node) {
  const role = getExplicitRole(node);

  // The element is not focusable and has role=presentation
  if ((role === 'presentation' || role === 'none') && !isFocusable(node)) {
    return false;
  }

  // Table inside editable area is data table always since the table structure is crucial for table editing
  if (
    node.getAttribute('contenteditable') === 'true' ||
    findUp(node, '[contenteditable="true"]')
  ) {
    return true;
  }

  // Table having ARIA table related role is data table
  if (role === 'grid' || role === 'treegrid' || role === 'table') {
    return true;
  }

  // Table having ARIA landmark role is data table
  if (getRoleType(role) === 'landmark') {
    return true;
  }

  // Table having datatable="0" attribute is layout table
  if (node.getAttribute('datatable') === '0') {
    return false;
  }

  // Table having summary attribute is data table
  if (node.getAttribute('summary')) {
    return true;
  }

  // Table having legitimate data table structures is data table
  if (node.tHead || node.tFoot || node.caption) {
    return true;
  }
  // colgroup / col - colgroup is magically generated
  for (
    let childIndex = 0, childLength = node.children.length;
    childIndex < childLength;
    childIndex++
  ) {
    if (node.children[childIndex].nodeName.toUpperCase() === 'COLGROUP') {
      return true;
    }
  }

  let cells = 0;
  const rowLength = node.rows.length;
  let row, cell;
  let hasBorder = false;
  for (let rowIndex = 0; rowIndex < rowLength; rowIndex++) {
    row = node.rows[rowIndex];
    for (
      let cellIndex = 0, cellLength = row.cells.length;
      cellIndex < cellLength;
      cellIndex++
    ) {
      cell = row.cells[cellIndex];
      if (cell.nodeName.toUpperCase() === 'TH') {
        return true;
      }
      if (
        !hasBorder &&
        (cell.offsetWidth !== cell.clientWidth ||
          cell.offsetHeight !== cell.clientHeight)
      ) {
        hasBorder = true;
      }
      if (
        cell.getAttribute('scope') ||
        cell.getAttribute('headers') ||
        cell.getAttribute('abbr')
      ) {
        return true;
      }
      if (['columnheader', 'rowheader'].includes(getExplicitRole(cell))) {
        return true;
      }
      // abbr element as a single child element of table cell
      if (
        cell.children.length === 1 &&
        cell.children[0].nodeName.toUpperCase() === 'ABBR'
      ) {
        return true;
      }
      cells++;
    }
  }

  // Table having nested table is layout table
  if (node.getElementsByTagName('table').length) {
    return false;
  }

  // Table having only one row or column is layout table (row)
  if (rowLength < 2) {
    return false;
  }

  // Table having only one row or column is layout table (column)
  const sampleRow = node.rows[Math.ceil(rowLength / 2)];
  if (sampleRow.cells.length === 1 && sampleRow.cells[0].colSpan === 1) {
    return false;
  }

  // Table having many columns (>= 5) is data table
  if (sampleRow.cells.length >= 5) {
    return true;
  }

  // Table having borders around cells is data table
  if (hasBorder) {
    return true;
  }

  // Table having differently colored rows is data table
  let bgColor, bgImage;
  for (let rowIndex = 0; rowIndex < rowLength; rowIndex++) {
    row = node.rows[rowIndex];
    if (
      bgColor &&
      bgColor !==
        window.getComputedStyle(row).getPropertyValue('background-color')
    ) {
      return true;
    } else {
      bgColor = window
        .getComputedStyle(row)
        .getPropertyValue('background-color');
    }
    if (
      bgImage &&
      bgImage !==
        window.getComputedStyle(row).getPropertyValue('background-image')
    ) {
      return true;
    } else {
      bgImage = window
        .getComputedStyle(row)
        .getPropertyValue('background-image');
    }
  }

  // Table having many rows (>= 20) is data table
  if (rowLength >= 20) {
    return true;
  }

  // Wide table (more than 95% of the document width) is layout table
  if (
    getElementCoordinates(node).width >
    getViewportSize(window).width * 0.95
  ) {
    return false;
  }

  // Table having small amount of cells (<= 10) is layout table
  if (cells < 10) {
    return false;
  }

  // Table containing embed, object, applet of iframe elements (typical advertisements elements) is layout table
  if (node.querySelector('object, embed, iframe, applet')) {
    return false;
  }

  // Otherwise it's data table
  return true;
}

export default isDataTable;
