'use strict';

Object.defineProperty(exports, "__esModule", {
  value: true
});

var _slicedToArray = function () { function sliceIterator(arr, i) { var _arr = []; var _n = true; var _d = false; var _e = undefined; try { for (var _i = arr[Symbol.iterator](), _s; !(_n = (_s = _i.next()).done); _n = true) { _arr.push(_s.value); if (i && _arr.length === i) break; } } catch (err) { _d = true; _e = err; } finally { try { if (!_n && _i["return"]) _i["return"](); } finally { if (_d) throw _e; } } return _arr; } return function (arr, i) { if (Array.isArray(arr)) { return arr; } else if (Symbol.iterator in Object(arr)) { return sliceIterator(arr, i); } else { throw new TypeError("Invalid attempt to destructure non-iterable instance"); } }; }();

var _extends = Object.assign || function (target) { for (var i = 1; i < arguments.length; i++) { var source = arguments[i]; for (var key in source) { if (Object.prototype.hasOwnProperty.call(source, key)) { target[key] = source[key]; } } } return target; };

var _react = require('react');

var _react2 = _interopRequireDefault(_react);

var _propTypes = require('prop-types');

var _propTypes2 = _interopRequireDefault(_propTypes);

var _immutable = require('immutable');

var _immutable2 = _interopRequireDefault(_immutable);

var _reactIntl = require('react-intl');

var _reactRouterDom = require('react-router-dom');

var _get = require('lodash/get');

var _get2 = _interopRequireDefault(_get);

var _cspaceLayout = require('cspace-layout');

var _dimensions = require('../../../styles/dimensions.css');

var _dimensions2 = _interopRequireDefault(_dimensions);

var _SearchResultTable = require('../../../styles/cspace-ui/SearchResultTable.css');

var _SearchResultTable2 = _interopRequireDefault(_SearchResultTable);

var _SearchResultEmpty = require('../../../styles/cspace-ui/SearchResultEmpty.css');

var _SearchResultEmpty2 = _interopRequireDefault(_SearchResultEmpty);

function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }

const rowHeight = parseInt(_dimensions2.default.inputHeight, 10);

const messages = (0, _reactIntl.defineMessages)({
  searchPending: {
    'id': 'searchResultTable.searchPending',
    'defaultMessage': '\u22EF'
  }
});

/**
 * Determines if a column is sortable for a given search. A column is sortable if sortBy is truthy,
 * and the search is not constrained by a related record, or if it is, the field to sort by is not
 * complex. This is here to deal with CSPACE-5366 (searches with related record constraints are
 * done using CMIS, which can't see into complex fields). If that bug is ever fixed, then it will
 * suffice just to check sortBy.
 */
const isSortable = (column, searchDescriptor) => {
  const sortBy = column.sortBy;


  return sortBy && (!searchDescriptor.getIn(['searchQuery', 'rel']) || sortBy.indexOf('/0/') === -1);
};

const rowRenderer = (params, location) => {
  // This is a fork of react-virtualized's default row renderer:
  // https://github.com/bvaughn/react-virtualized/blob/master/source/Table/defaultRowRenderer.js

  const className = params.className,
        columns = params.columns,
        index = params.index,
        key = params.key,
        onRowClick = params.onRowClick,
        rowData = params.rowData,
        style = params.style;


  const a11yProps = {};

  if (onRowClick // ||
  // onRowDoubleClick ||
  // onRowMouseOut ||
  // onRowMouseOver ||
  // onRowRightClick
  ) {
      a11yProps['aria-label'] = 'row';
      a11yProps.tabIndex = 0;

      if (onRowClick) {
        a11yProps.onClick = event => onRowClick({ event, index, rowData });
      }

      // if (onRowDoubleClick) {
      //   a11yProps.onDoubleClick = event =>
      //     onRowDoubleClick({ event, index, rowData });
      // }

      // if (onRowMouseOut) {
      //   a11yProps.onMouseOut = event => onRowMouseOut({ event, index, rowData });
      // }

      // if (onRowMouseOver) {
      //   a11yProps.onMouseOver = event => onRowMouseOver({ event, index, rowData });
      // }

      // if (onRowRightClick) {
      //   a11yProps.onContextMenu = event =>
      //     onRowRightClick({ event, index, rowData });
      // }
    }

  if (location) {
    return _react2.default.createElement(
      _reactRouterDom.Link,
      _extends({}, a11yProps, {
        className: className,
        key: key,
        role: 'row',
        style: style,
        to: location
      }),
      columns
    );
  }

  return _react2.default.createElement(
    'div',
    _extends({}, a11yProps, {
      className: className,
      'data-index': index,
      key: key,
      role: 'row',
      style: style
    }),
    columns
  );
};

const propTypes = {
  columnSetName: _propTypes2.default.string,
  config: _propTypes2.default.object.isRequired,
  formatCellData: _propTypes2.default.func,
  formatColumnLabel: _propTypes2.default.func,
  isSearchPending: _propTypes2.default.bool,
  linkItems: _propTypes2.default.bool,
  listType: _propTypes2.default.string,
  perms: _propTypes2.default.instanceOf(_immutable2.default.Map),
  searchDescriptor: _propTypes2.default.instanceOf(_immutable2.default.Map),
  searchError: _propTypes2.default.instanceOf(_immutable2.default.Map),
  searchResult: _propTypes2.default.instanceOf(_immutable2.default.Map),
  showCheckboxColumn: _propTypes2.default.bool,
  renderCheckbox: _propTypes2.default.func,
  renderHeader: _propTypes2.default.func,
  renderFooter: _propTypes2.default.func,
  renderSelectBar: _propTypes2.default.func,
  getItemLocation: _propTypes2.default.func,
  onItemClick: _propTypes2.default.func,
  onSortChange: _propTypes2.default.func
};

const defaultProps = {
  columnSetName: 'default',
  formatCellData: (column, data) => data,
  formatColumnLabel: column => (0, _get2.default)(column, ['messages', 'label', 'defaultMessage']),
  linkItems: true,
  listType: 'common',
  renderHeader: () => null,
  renderFooter: () => null,
  renderSelectBar: () => null
};

class SearchResultTable extends _react.Component {
  constructor() {
    super();

    this.getItemLocation = this.getItemLocation.bind(this);
    this.handleKeyDown = this.handleKeyDown.bind(this);
    this.handleRowClick = this.handleRowClick.bind(this);
    this.renderNoItems = this.renderNoItems.bind(this);
    this.renderRow = this.renderRow.bind(this);
    this.sort = this.sort.bind(this);
  }

  getItemLocation(item) {
    var _props = this.props;
    const config = _props.config,
          listType = _props.listType,
          perms = _props.perms,
          searchDescriptor = _props.searchDescriptor;


    const getItemLocationPath = (0, _get2.default)(config, ['listTypes', listType, 'getItemLocationPath']);

    if (!getItemLocationPath) {
      return undefined;
    }

    const itemContext = { config, perms, searchDescriptor };
    const itemLocationPath = getItemLocationPath(item, itemContext);

    if (!itemLocationPath) {
      return undefined;
    }

    // Create a location with the item location path, along with enough state to reproduce this
    // search. The search descriptor is converted to an object in order to reliably store it in
    // location state.

    return {
      pathname: itemLocationPath,
      state: {
        searchDescriptor: searchDescriptor.toJS(),
        // The search traverser on records will always link to the search result page, so use
        // its search name.
        searchName: 'searchResultPage'
      }
    };
  }

  handleKeyDown(event) {
    if (event.key === 'Enter') {
      const index = (0, _get2.default)(event, ['target', 'dataset', 'index']);

      if (typeof index !== 'undefined') {
        this.handleRowClick(event.target.dataset.index);
      }
    }
  }

  handleRowClick(index) {
    var _props2 = this.props;
    const config = _props2.config,
          listType = _props2.listType,
          searchResult = _props2.searchResult,
          onItemClick = _props2.onItemClick;


    if (onItemClick) {
      const listTypeConfig = config.listTypes[listType];
      const listNodeName = listTypeConfig.listNodeName,
            itemNodeName = listTypeConfig.itemNodeName;


      const items = searchResult.getIn([listNodeName, itemNodeName]);
      const item = _immutable2.default.List.isList(items) ? items.get(index) : items;

      onItemClick(item);
    }
  }

  sort({ sortBy, sortDirection }) {
    const onSortChange = this.props.onSortChange;


    if (onSortChange) {
      onSortChange(sortBy + (sortDirection === _cspaceLayout.Table.SortDirection.DESC ? ' desc' : ''));
    }
  }

  renderNoItems() {
    const isSearchPending = this.props.isSearchPending;


    const message = isSearchPending ? _react2.default.createElement(_reactIntl.FormattedMessage, messages.searchPending) : null;

    return _react2.default.createElement(
      'div',
      { className: _SearchResultEmpty2.default.common },
      message
    );
  }

  renderRow(params) {
    var _props3 = this.props;
    const getItemLocation = _props3.getItemLocation,
          linkItems = _props3.linkItems;
    const rowData = params.rowData;


    let location;

    if (linkItems) {
      const locationGetter = getItemLocation || this.getItemLocation;

      location = locationGetter(rowData);
    }

    return rowRenderer(params, location);
  }

  renderTable() {
    var _props4 = this.props;
    const columnSetName = _props4.columnSetName,
          config = _props4.config,
          formatCellData = _props4.formatCellData,
          formatColumnLabel = _props4.formatColumnLabel,
          listType = _props4.listType,
          searchDescriptor = _props4.searchDescriptor,
          searchResult = _props4.searchResult,
          showCheckboxColumn = _props4.showCheckboxColumn,
          renderCheckbox = _props4.renderCheckbox;


    if (searchResult) {
      const recordType = searchDescriptor.get('recordType');
      const subresource = searchDescriptor.get('subresource');
      const searchQuery = searchDescriptor.get('searchQuery');

      const listTypeConfig = config.listTypes[listType];
      const listNodeName = listTypeConfig.listNodeName,
            itemNodeName = listTypeConfig.itemNodeName;


      let sortColumnName = null;
      let sortDir = null;

      const sortSpec = searchQuery.get('sort');

      if (sortSpec) {
        var _sortSpec$split = sortSpec.split(' ');

        var _sortSpec$split2 = _slicedToArray(_sortSpec$split, 2);

        sortColumnName = _sortSpec$split2[0];
        sortDir = _sortSpec$split2[1];
      }

      const list = searchResult.get(listNodeName);
      const pageSize = parseInt(list.get('pageSize'), 10);
      const totalItems = parseInt(list.get('totalItems'), 10);
      const itemsInPage = parseInt(list.get('itemsInPage'), 10);

      let items = list.get(itemNodeName);

      if (!items) {
        items = _immutable2.default.List();
      }

      if (!_immutable2.default.List.isList(items)) {
        // If there's only one result, it won't be returned as a list.
        items = _immutable2.default.List.of(items);
      }

      const columnConfigurer = subresource ? config.subresources[subresource] : config.recordTypes[recordType];

      let columnConfig = (0, _get2.default)(columnConfigurer, ['columns', columnSetName]);

      if (!columnConfig && columnSetName !== defaultProps.columnSetName) {
        // Fall back to the default column set if the named one doesn't exist.

        columnConfig = (0, _get2.default)(columnConfigurer, ['columns', defaultProps.columnSetName]);
      }

      if (!columnConfig) {
        columnConfig = [];
      }

      const columns = Object.keys(columnConfig).filter(name => !columnConfig[name].disabled).sort((nameA, nameB) => {
        const orderA = columnConfig[nameA].order;
        const orderB = columnConfig[nameB].order;

        return orderA - orderB;
      }).map(name => {
        const column = columnConfig[name];

        return {
          cellDataGetter: ({ dataKey, rowData }) => {
            let data = null;

            if (rowData) {
              const keys = dataKey.split('|');

              for (let i = 0; i < keys.length; i += 1) {
                const candidateValue = rowData.get(keys[i]);

                if (candidateValue) {
                  data = candidateValue;
                  break;
                }
              }
            }

            return formatCellData(column, data, rowData);
          },
          disableSort: !isSortable(column, searchDescriptor),
          flexGrow: column.flexGrow,
          flexShrink: column.flexShrink,
          label: formatColumnLabel(column),
          dataKey: column.dataKey || name,
          width: column.width
        };
      });

      let heightBasis;

      if (isNaN(totalItems)) {
        // We don't yet know how many items are found by the search. Set the height to one item, so
        // an ellipsis (or other calculating indicator) can be shown.

        heightBasis = 1;
      } else {
        // If all of the search results fit on one page, shrink the table to fit the number of
        // results. Otherwise, size the table to fit the desired page size, even if there aren't
        // that many results on this page. This keeps the pager from jumping up on the last page
        // and while page/sorting changes are in progress.

        heightBasis = totalItems <= pageSize && !isNaN(itemsInPage) ? itemsInPage : pageSize;

        if (heightBasis === 0) {
          // If there are no items, set the height to one, because it looks weird when the footer
          // is mashed up against the header. This also leaves room to display a "no records found"
          // message if desired.

          heightBasis = 1;
        }
      }

      const height = heightBasis * rowHeight + rowHeight;

      return _react2.default.createElement(
        'div',
        { style: { height } },
        _react2.default.createElement(_cspaceLayout.Table, {
          columns: columns,
          rowCount: items.size,
          rowGetter: ({ index }) => items.get(index),
          showCheckboxColumn: showCheckboxColumn,
          onRowClick: this.handleRowClick,
          renderCheckbox: renderCheckbox,
          sort: this.sort,
          sortBy: sortColumnName,
          sortDirection: sortDir === 'desc' ? _cspaceLayout.Table.SortDirection.DESC : _cspaceLayout.Table.SortDirection.ASC,
          noRowsRenderer: this.renderNoItems,
          rowRenderer: this.renderRow
        })
      );
    }

    return null;
  }

  render() {
    var _props5 = this.props;
    const isSearchPending = _props5.isSearchPending,
          searchError = _props5.searchError,
          searchResult = _props5.searchResult,
          renderHeader = _props5.renderHeader,
          renderSelectBar = _props5.renderSelectBar,
          renderFooter = _props5.renderFooter;


    return (
      // eslint-disable-next-line jsx-a11y/no-static-element-interactions
      _react2.default.createElement(
        'div',
        {
          className: _SearchResultTable2.default.common,
          role: 'presentation',
          onKeyDown: this.handleKeyDown
        },
        renderHeader({ isSearchPending, searchError, searchResult }),
        renderSelectBar(),
        this.renderTable(),
        renderFooter({ isSearchPending, searchError, searchResult })
      )
    );
  }
}

exports.default = SearchResultTable;
SearchResultTable.propTypes = propTypes;
SearchResultTable.defaultProps = defaultProps;