<template>
  <div class="qf-table" :class="[{
      'qf-table-fit': fit,
      'qf-table-border': border || isGroup,
      'qf-table-group': isGroup,
      'qf-table-fluid-height': maxHeight,
      'qf-table-scrollable-x': layout.scrollX,
      'qf-table-scrollable-y': layout.scrollY,
      'qf-table-enable-row-hover': !store.isComplex,
      'qf-table-enable-row-transition': (store.data || []).length !== 0 && (store.data || []).length < 100
    }]">
    <div class="hidden-columns" ref="hiddenColumns">
      <slot></slot>
    </div>
    <div v-if="showHeader" class="qf-table-header-wrapper" ref="headerWrapper">
      <table-header ref="tableHeader" :border="border" :default-sort="defaultSort" :style="{
          width: layout.bodyWidth ? layout.bodyWidth + 'px' : ''
        }">
      </table-header>
    </div>
    <div class="qf-table-body-wrapper" ref="bodyWrapper" :class="[layout.scrollX ? `is-scrolling-${scrollPosition}` : 'is-scrolling-none']" :style="[bodyHeight]">
      <table-body :context="context" :style="{
           width: bodyWidth
        }" :highlight="highlightCurrentRow">
      </table-body>
      <div v-if="!data || data.length === 0" class="qf-table-empty-block" ref="emptyBlock" :style="{
          width: bodyWidth
        }">
        <span class="qf-table-empty-text">
          <slot name="empty">{{ emptyText || t('qf.table.emptyText') }}</slot>
        </span>
      </div>
      <div v-if="$slots.append" class="qf-table-append-wrapper" ref="appendWrapper">
        <slot name="append"></slot>
      </div>
    </div>
    <div v-if="showSummary" v-show="data && data.length > 0" class="qf-table-footer-wrapper" ref="footerWrapper">
      <table-footer :store="store" :border="border" :sum-text="sumText || t('qf.table.sumText')" :summary-method="summaryMethod" :default-sort="defaultSort" :style="{
          width: layout.bodyWidth ? layout.bodyWidth + 'px' : ''
        }">
      </table-footer>
    </div>
    <div v-if="fixedColumns.length > 0" class="qf-table-fixed" ref="fixedWrapper" :style="[{width: layout.fixedWidth ? layout.fixedWidth + 'px' : ''},fixedHeight]">
      <div v-if="showHeader" class="qf-table-fixed-header-wrapper" ref="fixedHeaderWrapper">
        <table-header ref="fixedTableHeader" fixed="left" :border="border" :style="{width: bodyWidth}"></table-header>
      </div>
      <div class="qf-table-fixed-body-wrapper" ref="fixedBodyWrapper" :style="[{top: layout.headerHeight + 'px'},fixedBodyHeight]">
        <table-body fixed="left" :style="{width: bodyWidth}" :highlight="highlightCurrentRow">
        </table-body>
        <div v-if="$slots.append" class="qf-table-append-gutter" :style="{height: layout.appendHeight + 'px'}"></div>
      </div>
      <div v-if="showSummary" v-show="data && data.length > 0" class="qf-table-fixed-footer-wrapper" ref="fixedFooterWrapper">
        <table-footer fixed="left" :border="border" :sum-text="sumText || t('qf.table.sumText')" :summary-method="summaryMethod" :style="{ width: bodyWidth}"></table-footer>
      </div>
    </div>
    <div v-if="rightFixedColumns.length > 0" class="qf-table-fixed-right" ref="rightFixedWrapper" :style="[{
        width: layout.rightFixedWidth ? layout.rightFixedWidth + 'px' : '',
        right: layout.scrollY ? (border ? layout.gutterWidth : (layout.gutterWidth || 0)) + 'px' : ''
    },fixedHeight]">
      <div v-if="showHeader" class="qf-table-fixed-header-wrapper" ref="rightFixedHeaderWrapper">
        <table-header ref="rightFixedTableHeader" fixed="right" :border="border" :style="{width: bodyWidth}"></table-header>
      </div>
      <div class="qf-table-fixed-body-wrapper" ref="rightFixedBodyWrapper" :style="[{top: layout.headerHeight + 'px'},fixedBodyHeight]">
        <table-body fixed="right" :style="{width: bodyWidth}" :highlight="highlightCurrentRow">
        </table-body>
      </div>
      <div v-if="showSummary" v-show="data && data.length > 0" class="qf-table-fixed-footer-wrapper" ref="rightFixedFooterWrapper">
        <table-footer fixed="right" :border="border" :sum-text="sumText || t('qf.table.sumText')" :summary-method="summaryMethod" :style="{width: bodyWidth}"></table-footer>
      </div>
    </div>
    <div class="qf-table-column-resize" ref="resizeProxy" v-show="resizeProxyVisible"></div>
  </div>
</template>

<script>
import TableStore from './table-store.js';
import TableHeader from './table-header.vue';
import TableFooter from './table-footer.vue';
import TableBody from './table-body.vue';
import TableColumn from './table-layout';
import LocaleMixin from '../locale/mixin';
import { addResizeListener, removeResizeListener } from '../util/resize-event';

let tableIdSeed = 1;

export default {
  name: 'QfTable',
  mixins: [LocaleMixin],
  components: {
    TableBody,
    TableHeader,
    TableFooter
  },
  props: {
    height: [String, Number],
    maxHeight: [String, Number],
    border: Boolean,
    spanMethod: Function,
    headerCellClassName: [String, Function],
    headerCellStyle: [Object, Function],
    cellClassName: [String, Function],
    cellStyle: [Object, Function],
    defaultSort: Object,
    highlightCurrentRow: Boolean,
    currentRowKey: [String, Number],
    defaultExpandAll: Boolean,
    rowKey: [String, Function],
    selectOnIndeterminate: {
      type: Boolean,
      default: true
    },
    rowClassName: [String, Function],
    showHeader: {
      type: Boolean,
      default: true
    },
    showSummary: Boolean,
    sumText: String,
    summaryMethod: Function,
    emptyText: String,
    context: {},
    fit: {
      type: Boolean,
      default: true
    },
    data: {
      type: Array,
      default: function () {
        return [];
      }
    }
  },
  provide() {
    return {
      store: this.store
    };
  },
  data() {
    let store = new TableStore(this, {
      rowKey: this.rowKey,
      defaultExpandAll: this.defaultExpandAll,
      selectOnIndeterminate: this.selectOnIndeterminate
    });
    return {
      store: store,
      layout: new TableColumn({
        store,
        table: this,
        fit: this.fit,
        showHeader: this.showHeader
      }),
      resizeState: {
        width: null,
        height: null
      },
      isGroup: false,
      scrollPosition: 'left',
      resizeProxyVisible: false,
      renderExpanded: null
    };
  },
  created() {
    this.tableId = 'qf-table_' + tableIdSeed++;
  },
  mounted() {
    this.bindEvents();
    this.store.updateColumns();
    this.doLayout();
    this.resizeState = {
      width: this.$el.offsetWidth,
      height: this.$el.offsetHeight
    };
    this.$ready = true;
  },
  destroyed() {
    removeResizeListener(this.$el, this.resizeListener);
  },
  watch: {
    height: {
      immediate: true,
      handler(value) {
        this.layout.setHeight(value);
      }
    },
    maxHeight: {
      immediate: true,
      handler(value) {
        this.layout.setMaxHeight(value);
      }
    },
    currentRowKey(newVal) {
      this.store.setCurrentRowKey(newVal);
    },
    data: {
      immediate: true,
      handler(value) {
        this.store.setData(value);
        if (this.$ready) {
          this.$nextTick(() => {
            this.doLayout();
          });
        }
      }
    }
  },
  computed: {
    columns() {
      return this.store.columns;
    },

    fixedColumns() {
      return this.store.fixedColumns;
    },
    rightFixedColumns() {
      return this.store.rightFixedColumns;
    },

    shouldUpdateHeight() {
      return this.height ||
        this.maxHeight ||
        this.fixedColumns.length > 0 ||
        this.rightFixedColumns.length > 0;
    },

    bodyWrapper() {
      return this.$refs.bodyWrapper;
    },

    bodyWidth() {
      const { bodyWidth, scrollY, gutterWidth } = this.layout;
      return bodyWidth ? bodyWidth - (scrollY ? gutterWidth : 0) + 'px' : '';
    },

    bodyHeight() {
      if (this.height) {
        return {
          height: this.layout.bodyHeight ? this.layout.bodyHeight + 'px' : ''
        };
      } else if (this.maxHeight) {
        return {
          'max-height': (this.showHeader
            ? this.maxHeight - this.layout.headerHeight - this.layout.footerHeight
            : this.maxHeight - this.layout.footerHeight) + 'px'
        };
      }
      return {};
    },

    fixedBodyHeight() {
      if (this.height) {
        return {
          height: this.layout.fixedBodyHeight ? this.layout.fixedBodyHeight + 'px' : ''
        };
      } else if (this.maxHeight) {
        let maxHeight = this.layout.scrollX ? this.maxHeight - this.layout.gutterWidth : this.maxHeight;
        if (this.showHeader) {
          maxHeight -= this.layout.headerHeight;
        }
        maxHeight -= this.layout.footerHeight;
        return {
          'max-height': maxHeight + 'px'
        };
      }
      return {};
    },

    fixedHeight() {
      if (this.maxHeight) {
        if (this.showSummary) {
          return {
            bottom: 0
          };
        }
        return {
          bottom: (this.layout.scrollX && this.data.length) ? this.layout.gutterWidth + 'px' : ''
        };
      } else {
        if (this.showSummary) {
          return {
            height: this.layout.tableHeight ? this.layout.tableHeight + 'px' : ''
          };
        }
        return {
          height: this.layout.viewportHeight ? this.layout.viewportHeight + 'px' : ''
        };
      }
    }
  },
  methods: {
    resizeListener() {
      if (!this.$ready) return;
      let shouldUpdateLayout = false;
      const el = this.$el;
      const { width: oldWidth, height: oldHeight } = this.resizeState;
      const width = el.offsetWidth;
      if (oldWidth !== width) {
        shouldUpdateLayout = true;
      }
      const height = el.offsetHeight;
      if ((this.height || this.shouldUpdateHeight) && oldHeight !== height) {
        shouldUpdateLayout = true;
      }
      if (shouldUpdateLayout) {
        this.resizeState.width = width;
        this.resizeState.height = height;
        this.doLayout();
      }
    },

    doLayout() {
      this.layout.updateColumnsWidth();
      if (this.shouldUpdateHeight) {
        this.layout.updateElsHeight();
      }
    },

    updateScrollY() {
      this.layout.updateScrollY();
    },

    handleFixedMousewheel(event, data) {
      const bodyWrapper = this.bodyWrapper;
      if (Math.abs(data.spinY) > 0) {
        const currentScrollTop = bodyWrapper.scrollTop;
        if (data.pixelY < 0 && currentScrollTop !== 0) {
          event.preventDefault();
        }
        if (data.pixelY > 0 && bodyWrapper.scrollHeight - bodyWrapper.clientHeight > currentScrollTop) {
          event.preventDefault();
        }
        bodyWrapper.scrollTop += Math.ceil(data.pixelY / 5);
      } else {
        bodyWrapper.scrollLeft += Math.ceil(data.pixelX / 5);
      }
    },

    handleHeaderFooterMousewheel(event, data) {
      const { pixelX, pixelY } = data;
      if (Math.abs(pixelX) >= Math.abs(pixelY)) {
        event.preventDefault();
        this.bodyWrapper.scrollLeft += data.pixelX / 5;
      }
    },

    bindEvents() {
      const { headerWrapper, footerWrapper } = this.$refs;
      const refs = this.$refs;
      let self = this;
      this.bodyWrapper.addEventListener('scroll', function () {
        if (headerWrapper) headerWrapper.scrollLeft = this.scrollLeft;
        if (footerWrapper) footerWrapper.scrollLeft = this.scrollLeft;
        if (refs.fixedBodyWrapper) refs.fixedBodyWrapper.scrollTop = this.scrollTop;
        if (refs.rightFixedBodyWrapper) refs.rightFixedBodyWrapper.scrollTop = this.scrollTop;
        const maxScrollLeftPosition = this.scrollWidth - this.offsetWidth - 1;
        const scrollLeft = this.scrollLeft;
        if (scrollLeft >= maxScrollLeftPosition) {
          self.scrollPosition = 'right';
        } else if (scrollLeft === 0) {
          self.scrollPosition = 'left';
        } else {
          self.scrollPosition = 'middle';
        }
      });
      if (this.fit) {
        addResizeListener(this.$el, this.resizeListener);
      }
    },

    toggleRowSelection(row, selected) {
      this.store.fireRowSelection(row, selected);
      this.store.updateAllSelected();
    },

    clearSelection() {
      this.store.clearSelection();
    },

    toggleRowExpansion(row, expanded) {
      this.store.rowExpandedChanged(row, expanded);
    },

    getSelection() {
      return this.store.selection;
    }
  }
};
</script>
