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

export default {
  inject: ['store'],
  mixins: [LayoutObserver],
  components: {
    QfCheckbox
  },
  props: {
    fixed: String,
    border: Boolean,
    defaultSort: {
      type: Object,
      default() {
        return {
          prop: '',
          order: ''
        };
      }
    }
  },
  data() {
    return {
      draggingColumn: null,
      dragging: false,
      dragState: {}
    };
  },
  computed: {
    table() {
      return this.$parent;
    },

    rows() {
      return this.store.getHeaderRows();
    },

    columns() {
      return this.store.columns;
    },

    isAllSelected() {
      return this.store.isAllSelected;
    },

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

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

    columnsCount() {
      return this.store.columns.length;
    },

    hasGutter() {
      return !this.fixed && this.tableLayout.gutterWidth;
    }
  },

  mounted() {
    if (this.defaultSort.prop) {
      this.store.sortProp = this.defaultSort.prop;
      this.store.sortOrder = this.defaultSort.order || 'ascending';
      this.$nextTick(_ => {
        for (let i = 0, length = this.columns.length; i < length; i++) {
          let column = this.columns[i];
          if (column.property === this.store.sortProp) {
            column.order = this.store.sortOrder;
            this.store.sortingColumn = column;
            break;
          }
        }

        if (this.store.sortingColumn) {
          this.store.changeSortCondition();
        }
      });
    }
  },

  methods: {
    toggleAllSelection() {
      this.store.toggleAllSelection();
    },

    isCellHidden(index, columns) {
      let start = 0;
      for (let i = 0; i < index; i++) {
        start += columns[i].colSpan;
      }
      const after = start + columns[index].colSpan - 1;
      if (this.fixed === true || this.fixed === 'left') {
        return after >= this.leftFixedLeafCount;
      } else if (this.fixed === 'right') {
        return start < this.columnsCount - this.rightFixedLeafCount;
      } else {
        return (after < this.leftFixedLeafCount) || (start >= this.columnsCount - this.rightFixedLeafCount);
      }
    },

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

    getHeaderCellClass(rowIndex, columnIndex, row, column) {
      const classes = [column.id, column.order, column.headerAlign, column.className, column.labelClassName];

      if (rowIndex === 0 && this.isCellHidden(columnIndex, row)) {
        classes.push('is-hidden');
      }

      if (!column.children) {
        classes.push('is-leaf');
      }

      if (column.sortable) {
        classes.push('is-sortable');
      }

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

      return classes.join(' ');
    },

    toggleOrder(order) {
      return !order ? 'ascending' : order === 'ascending' ? 'descending' : null;
    },

    handleHeaderClick(event, column) {
      if (column.sortable) {
        this.handleSortClick(event, column);
      }

      this.$parent.$emit('header-click', column, event);
    },

    handleSortClick(event, column, givenOrder) {
      event.stopPropagation();
      let order = givenOrder || this.toggleOrder(column.order);

      let target = event.target;
      while (target && target.tagName !== 'TH') {
        target = target.parentNode;
      }

      if (target && target.tagName === 'TH') {
        if (hasClass(target, 'noclick')) {
          removeClass(target, 'noclick');
          return;
        }
      }

      if (!column.sortable) return;

      const states = this.store;
      let sortProp = states.sortProp;
      let sortOrder;
      const sortingColumn = states.sortingColumn;

      if (sortingColumn !== column || (sortingColumn === column && sortingColumn.order === null)) {
        if (sortingColumn) {
          sortingColumn.order = null;
        }
        states.sortingColumn = column;
        sortProp = column.property;
      }

      if (!order) {
        sortOrder = column.order = null;
        states.sortingColumn = null;
        sortProp = null;
      } else {
        sortOrder = column.order = order;
      }

      states.sortProp = sortProp;
      states.sortOrder = sortOrder;

      this.store.changeSortCondition();
    },

    handleMouseDown(event, column) {
      if (column.children && column.children.length > 0) return;
      if (this.draggingColumn && this.border) {
        this.dragging = true;

        this.$parent.resizeProxyVisible = true;

        const table = this.$parent;
        const tableEl = table.$el;
        const tableLeft = tableEl.getBoundingClientRect().left;
        const columnEl = this.$el.querySelector(`th.${column.id}`);
        const columnRect = columnEl.getBoundingClientRect();
        const minLeft = columnRect.left - tableLeft + 30;

        addClass(columnEl, 'noclick');

        this.dragState = {
          startMouseLeft: event.clientX,
          startLeft: columnRect.right - tableLeft,
          startColumnLeft: columnRect.left - tableLeft,
          tableLeft
        };

        const resizeProxy = table.$refs.resizeProxy;
        resizeProxy.style.left = this.dragState.startLeft + 'px';

        document.onselectstart = function () {
          return false;
        };
        document.ondragstart = function () {
          return false;
        };

        const handleMouseMove = (event) => {
          const deltaLeft = event.clientX - this.dragState.startMouseLeft;
          const proxyLeft = this.dragState.startLeft + deltaLeft;

          resizeProxy.style.left = Math.max(minLeft, proxyLeft) + 'px';
        };

        const handleMouseUp = () => {
          if (this.dragging) {
            const {
              startColumnLeft,
              startLeft
            } = this.dragState;
            const finalLeft = parseInt(resizeProxy.style.left, 10);
            const columnWidth = finalLeft - startColumnLeft;
            column.width = column.realWidth = columnWidth;
            table.$emit('header-dragend', column.width, startLeft - startColumnLeft, column, event);

            document.body.style.cursor = '';
            this.dragging = false;
            this.draggingColumn = null;
            this.dragState = {};

            table.resizeProxyVisible = false;
          }

          document.removeEventListener('mousemove', handleMouseMove);
          document.removeEventListener('mouseup', handleMouseUp);
          document.onselectstart = null;
          document.ondragstart = null;

          setTimeout(function () {
            removeClass(columnEl, 'noclick');
          }, 0);
        };

        document.addEventListener('mousemove', handleMouseMove);
        document.addEventListener('mouseup', handleMouseUp);
      }
    },

    handleMouseMove(event, column) {
      if (column.children && column.children.length > 0) return;
      let target = event.target;
      while (target && target.tagName !== 'TH') {
        target = target.parentNode;
      }

      if (!column || !column.resizable) return;

      if (!this.dragging && this.border) {
        let rect = target.getBoundingClientRect();

        const bodyStyle = document.body.style;
        if (rect.width > 12 && rect.right - event.pageX < 8) {
          bodyStyle.cursor = 'col-resize';
          if (hasClass(target, 'is-sortable')) {
            target.style.cursor = 'col-resize';
          }
          this.draggingColumn = column;
        } else if (!this.dragging) {
          bodyStyle.cursor = '';
          if (hasClass(target, 'is-sortable')) {
            target.style.cursor = 'pointer';
          }
          this.draggingColumn = null;
        }
      }
    },

    handleMouseOut() {
      document.body.style.cursor = '';
    }
  },

  render(h) {
    const columnRows = this.rows;
    // 是否拥有多级表头
    const isGroup = columnRows.length > 1;
    if (isGroup) this.$parent.isGroup = true;
    return (
      <table
        class="qf-table-header"
        cellspacing="0"
        cellpadding="0"
        border="0">
        <colgroup>
          {
            this._l(this.columns, column => <col name={column.id}/>)
          }
          {
            this.hasGutter ? <col name="gutter"/> : ''
          }
        </colgroup>
        <thead class={[{ 'is-group': isGroup, 'has-gutter': this.hasGutter }]}>
          {
            this._l(columnRows, (columns, rowIndex) =>
              <tr>
                {
                  this._l(columns, (column, cellIndex) =>
                    <th
                      colspan={column.colSpan}
                      rowspan={column.rowSpan}
                      on-mousemove={($event) => this.handleMouseMove($event, column)}
                      on-mouseout={this.handleMouseOut}
                      on-mousedown={($event) => this.handleMouseDown($event, column)}
                      on-click={($event) => this.handleHeaderClick($event, column)}
                      style={this.getHeaderCellStyle(rowIndex, cellIndex, columns, column)}
                      class={this.getHeaderCellClass(rowIndex, cellIndex, columns, column)}>
                      <div>
                        {
                          column.renderHeader
                            ? column.renderHeader.call(this._renderProxy, h, {
                              column,
                              $index: cellIndex,
                              store: this.store,
                              _self: this.$parent.$vnode.context
                            })
                            : column.label
                        }
                        {
                          column.sortable
                            ? <span class="caret-wrapper" on-click={($event) => this.handleSortClick($event, column)}>
                              <i class="sort-caret ascending"
                                on-click={($event) => this.handleSortClick($event, column, 'ascending')}>
                              </i>
                              <i class="sort-caret descending"
                                on-click={($event) => this.handleSortClick($event, column, 'descending')}>
                              </i>
                            </span>
                            : ''
                        }
                      </div>
                    </th>
                  )
                }
                {
                  this.hasGutter ? <th class="gutter"></th> : ''
                }
              </tr>
            )
          }
        </thead>
      </table>
    );
  }
};
</script>
