<script>
import QfCheckbox from '../checkbox';
import LayoutObserver from './layout-observer';
import { hasClass, addClass, removeClass } from '../util/dom';
import { getCell, getColumnByCell, getRowIdentity } from './util';

export default {
  inject: ['store'],
  mixins: [LayoutObserver],
  components: {
    QfCheckbox
  },
  props: {
    fixed: String,
    stripe: Boolean,
    context: {},
    highlight: Boolean
  },

  computed: {
    rows() {
      return this.store.data;
    },
    columns() {
      return this.store.columns;
    },
    columnsCount() {
      return this.store.columns.length;
    },

    leftFixedLeafCount() {
      return this.store.fixedLeafColumnsLength;
    },

    rightFixedLeafCount() {
      return this.store.rightFixedLeafColumnsLength;
    },

    leftFixedCount() {
      return this.store.fixedColumns.length;
    },

    rightFixedCount() {
      return this.store.rightFixedColumns.length;
    },

    table() {
      return this.$parent;
    }
  },

  methods: {
    getKeyOfRow(row, index) {
      const rowKey = this.table.rowKey;
      if (rowKey) {
        return getRowIdentity(row, rowKey);
      }
      return index;
    },

    isColumnHidden(index) {
      if (this.fixed === true || this.fixed === 'left') {
        return index >= this.leftFixedLeafCount;
      } else if (this.fixed === 'right') {
        return index < this.columnsCount - this.rightFixedLeafCount;
      } else {
        return (index < this.leftFixedLeafCount) || (index >= this.columnsCount - this.rightFixedLeafCount);
      }
    },

    getSpan(row, column, rowIndex, columnIndex) {
      let rowspan = 1;
      let colspan = 1;

      const fn = this.table.spanMethod;
      if (typeof fn === 'function') {
        const result = fn({
          row,
          column,
          rowIndex,
          columnIndex
        });

        if (Array.isArray(result)) {
          rowspan = result[0];
          colspan = result[1];
        } else if (typeof result === 'object') {
          rowspan = result.rowspan;
          colspan = result.colspan;
        }
      }

      return {
        rowspan,
        colspan
      };
    },

    getCellStyle(rowIndex, columnIndex, row, column) {
      const cellStyle = this.table.cellStyle;
      if (typeof cellStyle === 'function') {
        return cellStyle(
          rowIndex,
          columnIndex,
          row,
          column
        );
      }
      return cellStyle;
    },

    getCellClass(rowIndex, columnIndex, row, column) {
      const classes = [column.id, column.align, column.className];

      if (this.isColumnHidden(columnIndex)) {
        classes.push('is-hidden');
      }

      const cellClassName = this.table.cellClassName;
      if (typeof cellClassName === 'string') {
        classes.push(cellClassName);
      } else if (typeof cellClassName === 'function') {
        classes.push(cellClassName(
          rowIndex,
          columnIndex,
          row,
          column
        ));
      }

      return classes.join(' ');
    },

    handleDoubleClick(event, row) {
      this.handleEvent(event, row, 'dblclick');
    },

    handleClick(event, row) {
      this.store.setCurrentRow(row);
      this.handleEvent(event, row, 'click');
    },

    handleEvent(event, row, name) {
      const table = this.table;
      const cell = getCell(event);
      let column;
      if (cell) {
        column = getColumnByCell(table, cell);
        if (column) {
          table.$emit(`cell-${name}`, row, column, cell, event);
        }
      }
      table.$emit(`row-${name}`, row, event, column);
    },

    handleMouseEnter(index) {
      this.store.setHoverRow(index);
      this.table.$emit('cell-mouse-enter', index);
    },

    handleMouseLeave() {
      this.store.setHoverRow(null);
    },

    handleExpandClick(row, e) {
      e.stopPropagation();
      this.store.rowExpandedChanged(row);
    },

    handleKeydown(event, colIndex, cellIndex, row, column) {
      this.table.$emit('table-keydown', event, colIndex, cellIndex, row, column);
    },

    getRowClass(row, rowIndex) {
      const classes = ['qf-table-row'];

      if (this.stripe && rowIndex % 2 === 1) {
        classes.push('qf-table-row-striped');
      }
      const rowClassName = this.table.rowClassName;
      if (typeof rowClassName === 'string') {
        classes.push(rowClassName);
      } else if (typeof rowClassName === 'function') {
        classes.push(rowClassName(
          row,
          rowIndex
        ));
      }

      if (this.store.expandRows.indexOf(row) > -1) {
        classes.push('expanded');
      }

      return classes.join(' ');
    }
  },

  watch: {
    'store.hoverRow'(newVal, oldVal) {
      if (!this.store.isComplex) return;
      const el = this.$el;
      if (!el) return;
      const tr = el.querySelector('tbody').children;
      const rows = [].filter.call(tr, row => hasClass(row, 'qf-table-row'));
      const oldRow = rows[oldVal];
      const newRow = rows[newVal];
      if (oldRow) {
        removeClass(oldRow, 'hover-row');
      }
      if (newRow) {
        addClass(newRow, 'hover-row');
      }
    },
    'store.currentRow'(newVal, oldVal) {
      if (!this.highlight) return;
      const el = this.$el;
      if (!el) return;
      const data = this.store.data;
      const tr = el.querySelector('tbody').children;
      const rows = [].filter.call(tr, row => hasClass(row, 'qf-table-row'));
      const oldRow = rows[data.indexOf(oldVal)];
      const newRow = rows[data.indexOf(newVal)];
      if (oldRow) {
        removeClass(oldRow, 'current-row');
      } else {
        [].forEach.call(rows, row => removeClass(row, 'current-row'));
      }
      if (newRow) {
        addClass(newRow, 'current-row');
      }
    }
  },

  render(h) {
    const columnsHidden = this.columns.map((column, index) => this.isColumnHidden(index));

    return (
      <table
        class="qf-table-body"
        cellspacing="0"
        cellpadding="0"
        border="0">
        <colgroup>
          {
            this._l(this.columns, column => <col name={column.id} />)
          }
        </colgroup>
        <tbody>
          {
            this._l(this.rows, (row, rowIndex) =>
              [<tr
                on-mouseenter={_ => this.handleMouseEnter(rowIndex)}
                on-mouseleave={_ => this.handleMouseLeave()}
                on-click={($event) => this.handleClick($event, row)}
                on-dblclick={($event) => this.handleDoubleClick($event, row)}
                class={[this.getRowClass(row, rowIndex)]}
                key={this.table.rowKey ? this.getKeyOfRow(row, rowIndex) : rowIndex}>
                {
                  this._l(this.columns, (column, cellIndex) => {
                    const { rowspan, colspan } = this.getSpan(row, column, rowIndex, cellIndex);
                    if (!rowspan || !colspan) {
                      return '';
                    } else {
                      if (rowspan === 1 && colspan === 1) {
                        return (
                          <td
                            style={this.getCellStyle(rowIndex, cellIndex, row, column)}
                            on-keydown={($event) => this.handleKeydown($event, rowIndex, cellIndex, row, column)}
                            class={this.getCellClass(rowIndex, cellIndex, row, column)}>
                            {
                              column.renderCell.call(
                                this._renderProxy,
                                h,
                                {
                                  row,
                                  column,
                                  rowIndex,
                                  store: this.store,
                                  _self: this.context || this.table.$vnode.context
                                },
                                columnsHidden[cellIndex]
                              )
                            }
                          </td>
                        );
                      } else {
                        return (
                          <td
                            style={this.getCellStyle(rowIndex, cellIndex, row, column)}
                            class={this.getCellClass(rowIndex, cellIndex, row, column)}
                            rowspan={rowspan}
                            colspan={colspan}>
                            {
                              column.renderCell.call(
                                this._renderProxy,
                                h,
                                {
                                  row,
                                  column,
                                  rowIndex,
                                  store: this.store,
                                  _self: this.context || this.table.$vnode.context
                                },
                                columnsHidden[cellIndex]
                              )
                            }
                          </td>
                        );
                      }
                    }
                  })
                }
              </tr>,
              this.store.isRowExpanded(row)
                ? (<tr>
                  <td colspan={this.columns.length} class="qf-table-expand">
                    {this.table.renderExpanded ? this.table.renderExpanded(h, {
                      row,
                      rowIndex,
                      store: this.store
                    }) : ''}
                  </td>
                </tr>)
                : ''
              ]
            )
          }
        </tbody>
      </table>
    );
  }
};
</script>
