<template>
    <tbody :ref="bodyRef" class="p-datatable-tbody" role="rowgroup" :style="bodyStyle"
            @keydown="onBodyKeyDown($event)" :tabindex=" frozenRow ? -1: 0 "> <!--  必须要设置 tabindex，才能响应keyodown事件  -->

        <template v-if="!empty">
            <template v-for="(rowData, index) of value">
                <tr
                    v-if="templates['groupheader'] && rowGroupMode === 'subheader' && shouldRenderRowGroupHeader(value, rowData, getRowIndex(index))"
                    :key="getRowKey(rowData, getRowIndex(index)) + '_subheader'"
                    class="p-rowgroup-header"
                    :style="rowGroupHeaderStyle"
                  >
                    <td :colspan="columnsLength ">
                        <button v-if="expandableRowGroups" class="p-row-toggler p-link" @click="onRowGroupToggle($event, rowData)" type="button">
                            <component v-if="templates['rowgrouptogglericon']" :is="templates['rowgrouptogglericon']" :expanded="isRowGroupExpanded(rowData)" />
                            <template v-else>
                                <span v-if="isRowGroupExpanded(rowData) && expandedRowIcon" :class="['p-row-toggler-icon', expandedRowIcon]" />
                                <ChevronDownIcon v-else-if="isRowGroupExpanded(rowData) && !expandedRowIcon" class="p-row-toggler-icon" />
                                <span v-else-if="!isRowGroupExpanded(rowData) && collapsedRowIcon" :class="['p-row-toggler-icon', collapsedRowIcon]" />
                                <ChevronRightIcon v-else-if="!isRowGroupExpanded(rowData) && !collapsedRowIcon" class="p-row-toggler-icon" />
                            </template>
                        </button>
                        <component :is="templates['groupheader']" :data="rowData" :index="getRowIndex(index)" />
                    </td>
                </tr>

                <!--
                      把行的keydown 消息统一 转到body里来进行处理
                      @keydown="onRowKeyDown($event, rowData, getRowIndex(index))" 
                      :tabindex="setRowTabindex(index)"
                -->
                
                <tr
                    v-if="expandableRowGroups ? isRowGroupExpanded(rowData) : true"
                    :key="getRowKey(rowData, getRowIndex(index))"
                    :class="getRowClass(rowData,getRowIndex(index) )"
                    :style="getRowStyle(rowData)"
                    @click="onRowClick($event, rowData, getRowIndex(index))"
                    @dblclick="onRowDblClick($event, rowData, getRowIndex(index))"
                    @contextmenu="onRowRightClick($event, rowData, getRowIndex(index))"
                    @touchend="onRowTouchEnd($event)"
                    @mousedown="onRowMouseDown($event)"
                    @dragstart="onRowDragStart($event, getRowIndex(index))"
                    @dragover="onRowDragOver($event, getRowIndex(index))"
                    @dragleave="onRowDragLeave($event)"
                    @dragend="onRowDragEnd($event)"
                    @drop="onRowDrop($event)"
                >
                    <template v-for="(col, i) of columns">
                        <DTBodyCell
                            v-if="shouldRenderBodyCell(value, col, getRowIndex(index))"
                            :key="columnProp(col, 'columnKey') || columnProp(col, 'field') || i"
                            :rowData="rowData"
                            :column="col"
                            :rowIndex="getRowIndex(index)"
                            :index="i"
                            :selected="isSelected(rowData)"
                            :frozenRow="frozenRow"
                            :rowspan="rowGroupMode === 'rowspan' ? calculateRowGroupSize(value, col, getRowIndex(index)) : null"
                            :responsiveLayout="responsiveLayout"
                            :virtualScrollerContentProps="virtualScrollerContentProps"
                            :ariaControls="expandedRowId + '_' + index + '_expansion'"
                            :name="nameAttributeSelector"
                            :isRowExpanded="isRowExpanded(rowData)"
                            :expandedRowIcon="expandedRowIcon"
                            :collapsedRowIcon="collapsedRowIcon"
                            :disabled="isDisabled(rowData,col)"
                            :readonly="isReadonly(rowData,col)"
                            @radio-change="onRadioChange($event)"
                            @checkbox-change="onCheckboxChange($event)"
                            @row-toggle="onRowToggle($event)"
                            @cell-edit-init="onCellEditInit($event)"
                            @cell-edit-complete="onCellEditComplete($event)"
                            @cell-edit-cancel="onCellEditCancel($event)"
                            @item-change="onItemChange($event)"
                        />
                    </template>
                </tr>
                <tr v-if="templates['expansion'] && expandedRows && isRowExpanded(rowData)" :key="getRowKey(rowData, getRowIndex(index)) + '_expansion'" :id="expandedRowId + '_' + index + '_expansion'" class="p-datatable-row-expansion" >
                    <td :colspan="columnsLength">
                        <component :is="templates['expansion']" :data="rowData" :index="getRowIndex(index)" />
                    </td>
                </tr>
                <tr v-if="templates['groupfooter'] && rowGroupMode === 'subheader' && shouldRenderRowGroupFooter(value, rowData, getRowIndex(index))" :key="getRowKey(rowData, getRowIndex(index)) + '_subfooter'" class="p-rowgroup-footer" >
                    <td :colspan="columnsLength ">
                        <component :is="templates['groupfooter']" :data="rowData" :index="getRowIndex(index)" />
                    </td>
                </tr>
            </template>
        </template>
        <tr v-else class="p-datatable-emptymessage" role="row">
            <td :colspan="columnsLength">
               <component v-if="templates.empty" :is="templates.empty" />
               <div v-else>暂无数据</div>
            </td>
        </tr>
    </tbody>
</template>

<script>
import ChevronDownIcon from 'primevue/icons/chevrondown';
import ChevronRightIcon from 'primevue/icons/chevronright';
import { DomHandler, ObjectUtils, RowStatus, UniqueComponentId } from 'primevue/utils';
import BodyCell from './BodyCell.vue';

export default {
    name: 'TableBody',
    emits: [
        'rowgroup-toggle',
        'row-click',
        'row-dblclick',
        'row-rightclick',
        'row-touchend',
        'row-keydown',
        'row-mousedown',
        'row-dragstart',
        'row-dragover',
        'row-dragleave',
        'row-dragend',
        'row-drop',
        'row-toggle',
        'radio-change',
        'checkbox-change',
        'cell-edit-init',
        'cell-edit-complete',
        'cell-edit-cancel',
        'item-change',
        'body-keydown'
    ],
    props: {
        value: {
            type: Array,
            default: null
        },
        columns: {
            type: null,
            default: null
        },
        frozenRow: {
            type: Boolean,
            default: false
        },
        empty: {
            type: Boolean,
            default: false
        },
        rowGroupMode: {
            type: String,
            default: null
        },
        groupRowsBy: {
            type: [Array, String, Function],
            default: null
        },
        expandableRowGroups: {
            type: Boolean,
            default: false
        },
        expandedRowGroups: {
            type: Array,
            default: null
        },

        currentRow: {
            type: Number,
            default: 0
        },

        first: {
            type: Number,
            default: 0
        },
        dataKey: {
            type: String,
            default: null
        },
        expandedRowIcon: {
            type: String,
            default: null
        },
        collapsedRowIcon: {
            type: String,
            default: null
        },
        expandedRows: {
            type: Array,
            default: null
        },
        expandedRowKeys: {
            type: null,
            default: null
        },

        selectionMode: {
            type: String,
            default: null
        },
        contextMenu: {
            type: Boolean,
            default: false
        },
        contextMenuSelection: {
            type: Object,
            default: null
        },
        rowClass: {
            type: null,
            default: null
        },
        rowStyle: {
            type: null,
            default: null
        },
      
        compareSelectionBy: {
            type: String,
            default: 'deepEquals'
        },
        templates: {
            type: null,
            default: null
        },
        scrollable: {
            type: Boolean,
            default: false
        },
        responsiveLayout: {
            type: String,
            default: 'stack'
        },
        virtualScrollerContentProps: {
            type: Object,
            default: null
        },
        isVirtualScrollerDisabled: {
            type: Boolean,
            default: false
        },
        currentRowHighlight:{
            type: Boolean,
            default: true
        },

        disabled:{
             type:Boolean,
             default:null
         },
        readonly:{
            type:Boolean,
            default:false
        }
    },
    
    inject:{
         dataTable:"DataTable"
     },

    data() {
        return {
            rowGroupHeaderStyleObject: {},
            tabindexArray: [],
            isARowSelected: false
        };
    },
    mounted() {
        if (this.frozenRow) {
            this.updateFrozenRowStickyPosition();
        }

        if (this.scrollable && this.rowGroupMode === 'subheader') {
            this.updateFrozenRowGroupHeaderStickyPosition();
        }
    },
    updated() {
        if (this.frozenRow) {
            this.updateFrozenRowStickyPosition();
        }

        if (this.scrollable && this.rowGroupMode === 'subheader') {
            this.updateFrozenRowGroupHeaderStickyPosition();
        }
    },

    methods: {
        columnProp(col, prop) {
            return ObjectUtils.getVNodeProp(col, prop);
        },
        shouldRenderRowGroupHeader(value, rowData, i) {
            let currentRowFieldData = ObjectUtils.resolveFieldData(rowData, this.groupRowsBy);
            let prevRowData = value[i - 1];

            if (prevRowData) {
                let previousRowFieldData = ObjectUtils.resolveFieldData(prevRowData, this.groupRowsBy);

                return currentRowFieldData !== previousRowFieldData;
            } else {
                return true;
            }
        },

        getRowKey(rowData, index) {
           if(rowData && ('rowId' in rowData)){
               return rowData["rowId"];
           }

           return this.dataKey ? ObjectUtils.resolveFieldData(rowData, this.dataKey) : this.getRowIndex(index);
        },

        getRowIndex(index) {
            const getItemOptions = this.getVirtualScrollerProp('getItemOptions');

            //改为数据显示在table中的行序号，而不是在数据表的行数据index
            //return getItemOptions ? getItemOptions(index).index : this.first +index

             return getItemOptions ? getItemOptions(index).index : index;
        },

        getRowStyle(rowData) {
            if (this.rowStyle) {
                return this.rowStyle(rowData);
            }
        },
        getRowClass(rowData, index ) {
            let rowStyleClass = [];

            if(!this.disabled){
                if( this.currentRowHighlight && index === this.currentRow ){
                    if(this.selectionMode)   //如果支持行选择，则用 p-currentRow 的主题颜色
                         rowStyleClass.push('p-currentRow');
                    else
                         rowStyleClass.push('p-highlight');
                }

                rowStyleClass.push({
                    'p-highlight': this.isSelected(rowData) 
                });
            
                if (this.contextMenuSelection) {
                    rowStyleClass.push({
                        'p-highlight-contextmenu': this.isSelectedWithContextMenu(rowData)
                    });
                } 
            }

            if (this.rowClass) {
                let rowClassValue = this.rowClass(rowData);

                if (rowClassValue) {
                    rowStyleClass.push(rowClassValue);
                }
            }

            return rowStyleClass;
        },
        shouldRenderRowGroupFooter(value, rowData, i) {
            if (this.expandableRowGroups && !this.isRowGroupExpanded(rowData)) {
                return false;
            } else {
                let currentRowFieldData = ObjectUtils.resolveFieldData(rowData, this.groupRowsBy);
                let nextRowData = value[i + 1];

                if (nextRowData) {
                    let nextRowFieldData = ObjectUtils.resolveFieldData(nextRowData, this.groupRowsBy);

                    return currentRowFieldData !== nextRowFieldData;
                } else {
                    return true;
                }
            }
        },
        shouldRenderBodyCell(value, column, i) {
            if (this.rowGroupMode) {
                if (this.rowGroupMode === 'subheader') {
                    return this.groupRowsBy !== this.columnProp(column, 'field');
                } else if (this.rowGroupMode === 'rowspan') {
                    if (this.isGrouped(column)) {
                        let prevRowData = value[i - 1];

                        if (prevRowData) {
                            let currentRowFieldData = ObjectUtils.resolveFieldData(value[i], this.columnProp(column, 'field'));
                            let previousRowFieldData = ObjectUtils.resolveFieldData(prevRowData, this.columnProp(column, 'field'));

                            return currentRowFieldData !== previousRowFieldData;
                        } else {
                            return true;
                        }
                    } else {
                        return true;
                    }
                }
            } else {
                return !this.columnProp(column, 'hidden');
            }
        },
        calculateRowGroupSize(value, column, index) {
            if (this.isGrouped(column)) {
                let currentRowFieldData = ObjectUtils.resolveFieldData(value[index], this.columnProp(column, 'field'));
                let nextRowFieldData = currentRowFieldData;
                let groupRowSpan = 0;

                while (currentRowFieldData === nextRowFieldData) {
                    groupRowSpan++;
                    let nextRowData = value[++index];

                    if (nextRowData) {
                        nextRowFieldData = ObjectUtils.resolveFieldData(nextRowData, this.columnProp(column, 'field'));
                    } else {
                        break;
                    }
                }

                return groupRowSpan === 1 ? null : groupRowSpan;
            } else {
                return null;
            }
        },
        isGrouped(column) {
            if (this.groupRowsBy && this.columnProp(column, 'field')) {
                if (Array.isArray(this.groupRowsBy)) return this.groupRowsBy.indexOf(column.props.field) > -1;
                else return this.groupRowsBy === column.props.field;
            } else {
                return false;
            }
        },
        isRowEditing(rowData) {
            return false;
        },
        isRowExpanded(rowData) {
            if (rowData && this.expandedRows) {
                if (this.dataKey) return this.expandedRowKeys ? this.expandedRowKeys[ObjectUtils.resolveFieldData(rowData, this.dataKey)] !== undefined : false;
                else return this.findIndex(rowData, this.expandedRows) > -1;
            }

            return false;
        },
        isRowGroupExpanded(rowData) {
            if (this.expandableRowGroups && this.expandedRowGroups) {
                let groupFieldValue = ObjectUtils.resolveFieldData(rowData, this.groupRowsBy);

                return this.expandedRowGroups.indexOf(groupFieldValue) > -1;
            }

            return false;
        },
        isSelected(rowData) {
           if(rowData && ('isSelected' in rowData)){
               return rowData["isSelected"];
           }

           return false;
        },
        
        isSelectedWithContextMenu(rowData) {
            if (rowData && this.contextMenuSelection) {
                return this.equals(rowData, this.contextMenuSelection, this.dataKey);
            }

            return false;
        },

        isDisabled(rowData, column){
            if(!rowData ||! column)
                return false;

            if(this.disabled)
                return true;

            const propValue = this.columnProp(column,"disabled") ;

            if(propValue===null || propValue===undefined || propValue==='' )
                return false;
            else if( ( typeof propValue) ==='boolean')
                return propValue;
            else if( ( typeof propValue)==='function'){
               return  propValue( rowData, this.columnProp(column,"field"));
            }
            else if(propValue==='notnew'){
                const status=rowData['rowStatus'];

                return  ( status ===RowStatus.New || status===RowStatus.NewModified)? false : true;
            }

            return  (propValue==='1'  ) ? true: false;
        },  

        isReadonly(rowData, column){
            if(!rowData ||! column)
                return false;

            if(this.readonly)
                return true;

            const propValue = this.columnProp(column,"readonly") ;

            if(propValue===null || propValue===undefined || propValue==='' )
                return false;
            else if( ( typeof propValue) ==='boolean')
                return propValue;
            else if( ( typeof propValue)==='function'){
               return  propValue( rowData, this.columnProp(column,"field"));
            }
            else if(propValue==='notnew'){
                const status=rowData['rowStatus'];

                return  ( status ===RowStatus.New || status===RowStatus.NewModified)? false : true;
            }

            return  (propValue==='1'  ) ? true: false;
        },  

        findIndex(rowData, collection) {
            let index = -1;

            if (collection && collection.length) {
                for (let i = 0; i < collection.length; i++) {
                    if (this.equals(rowData, collection[i])) {
                        index = i;
                        break;
                    }
                }
            }

            return index;
        },
        equals(data1, data2) {
            return this.compareSelectionBy === 'equals' ? data1 === data2 : ObjectUtils.equals(data1, data2, this.dataKey);
        },
        onRowGroupToggle(event, data) {
            this.$emit('rowgroup-toggle', { originalEvent: event, data: data });
        },
        onRowClick(event, rowData, rowIndex) {
            this.$emit('row-click', { originalEvent: event, data: rowData, index: rowIndex });
        },
        onRowDblClick(event, rowData, rowIndex) {
            this.$emit('row-dblclick', { originalEvent: event, data: rowData, index: rowIndex });
        },
        onRowRightClick(event, rowData, rowIndex) {
            this.$emit('row-rightclick', { originalEvent: event, data: rowData, index: rowIndex });
        },
        
        onRowTouchEnd(event) {
            this.$emit('row-touchend', event);
        },
        onRowKeyDown(event, rowData, rowIndex) {
           this.$emit('row-keydown', { originalEvent: event, data: rowData, index: rowIndex });
        },
        onRowMouseDown(event) {
            this.$emit('row-mousedown', event);
        },
        onRowDragStart(event, rowIndex) {
            this.$emit('row-dragstart', { originalEvent: event, index: rowIndex });
        },
        onRowDragOver(event, rowIndex) {
            this.$emit('row-dragover', { originalEvent: event, index: rowIndex });
        },
        onRowDragLeave(event) {
            this.$emit('row-dragleave', event);
        },
        onRowDragEnd(event) {
            this.$emit('row-dragend', event);
        },
        onRowDrop(event) {
            this.$emit('row-drop', event);
        },
        onRowToggle(event) {
            this.$emit('row-toggle', event);
        },
        onRadioChange(event) {
            this.$emit('radio-change', event);
        },
        onCheckboxChange(event) {
            this.$emit('checkbox-change', event);
        },
        onCellEditInit(event) {
            this.$emit('cell-edit-init', event);
        },
        onCellEditComplete(event) {
            this.$emit('cell-edit-complete', event);
        },
        onCellEditCancel(event) {
            this.$emit('cell-edit-cancel', event);
        },
        onItemChange(event){
            this.$emit('item-change', event);
        },
               
        updateFrozenRowStickyPosition() {
            this.$el.style.top = DomHandler.getOuterHeight(this.$el.previousElementSibling) + 'px';
        },
        updateFrozenRowGroupHeaderStickyPosition() {
            let tableHeaderHeight = DomHandler.getOuterHeight(this.$el.previousElementSibling);

            this.rowGroupHeaderStyleObject.top = tableHeaderHeight + 'px';
        },
        getVirtualScrollerProp(option, options) {
            options = options || this.virtualScrollerContentProps;

            return options ? options[option] : null;
        },
        bodyRef(el) {
            // For VirtualScroller
            const contentRef = this.getVirtualScrollerProp('contentRef');

            contentRef && contentRef(el);
        },
        setRowTabindex(index) {
            // if (this.selection === null && (this.selectionMode === 'single' || this.selectionMode === 'multiple')) {
            //     return index === 0 ? 0 : -1;
            // }

            return -1;
        },
        
        onBodyKeyDown(event){
            this.$emit('body-keydown',  { originalEvent: event, frozenRow: this.frozenRow  }  );
        }
    },

    computed: {
        columnsLength() {
            let hiddenColLength = 0;

            this.columns.forEach((column) => {
                //if (this.columnProp(column, 'selectionMode') === 'single') hiddenColLength--;
                if (this.columnProp(column, 'hidden')) hiddenColLength++;
            });

            return this.columns ? this.columns.length - hiddenColLength : 0;
        },
        rowGroupHeaderStyle() {
            if (this.scrollable) {
                return { top: this.rowGroupHeaderStyleObject.top };
            }

            return null;
        },
        bodyStyle() {
            return this.getVirtualScrollerProp('contentStyle');
        },
        expandedRowId() {
            return UniqueComponentId();
        },
        nameAttributeSelector() {
            return UniqueComponentId();
        }
    },
    components: {
        DTBodyCell: BodyCell,
        ChevronDownIcon: ChevronDownIcon,
        ChevronRightIcon: ChevronRightIcon
    }
};
</script>
