<template>
  <div class="itf-board">

    <div ref="container" class="itf-board-titles">
      <div v-for="(column, index) of orderColumnsInBoard"
           :data-column="index"
           @mouseover="columnHighlight(index, 'enter')"
           @mouseout="columnHighlight(index, 'leave')"
           :key="index" :class="`itf-board-column itf-board-column__title p-2 ${columnEdit ? 'itf-board-column__move' : ''}`">
        <div accept-group="board-columns"
             class="itf-board-header-space"
             @enter="columnHighlight(index, 'enter', 'drop')"
             @leave="columnHighlight(index, 'leave', 'drop')"
             v-dropzone="{ payload: { index, column } }">
          <div class="itf-board-header-dropzone"></div>
        </div>
        <itf-edit-button
          accept-group="board-columns"
          :can-edit="columnEdit"
          @enter="columnHighlight(index, 'enter')"
          @leave="columnHighlight(index, 'leave')"
          @click="$emit('update:columnSettings', column)"
          v-dropzone="{ payload: { index, column } }"
          class="d-flex align-items-center">
          <div
            group="board-columns"
            @drop="reorderColumns"
            v-draggable="{ handle: true, payload: { index, column }, mirror: { yAxis:false } }"
            class="flex-grow-1 d-flex align-items-center itf-board-column__header justify-content-between">
            <div>
              <slot name="columnMarkup" :column="column" />
              <slot name="header" :column="column">{{ column[columnNameKey] }}</slot>
            </div>
            <div class="text-muted me-1"><slot name="header-addon" :column="column" :items="groupedItems[column.Id]">{{ (groupedItems[column.Id] || []).length }}</slot></div>
          </div>
        </itf-edit-button>
        <div v-if="index === columns.length - 1"
             @enter="columnHighlightLast('enter', 'drop')"
             @leave="columnHighlightLast('leave', 'drop')"
             accept-group="board-columns"
             class="itf-board-header-space right"
             v-dropzone="{ payload:{ last: true } }">
          <div class="itf-board-header-dropzone"></div>
        </div>
      </div>
      <div v-if="columnEdit" class="align-items-end d-flex pb-1">
        <slot name="add-column">
          <itf-button icon small @click="$emit('add-сolumn')">
            <itf-icon name="plus" />
          </itf-button>
        </slot>
      </div>
    </div>

    <div class="itf-board-columns flex-grow-1">
      <div v-for="(column, index) of orderColumnsInBoard"
           :key="`column-${index}`"
           :data-column="index"
           class="itf-board-column px-2 pb-2 pt-2" :class="{'empty': !groupedItems[column.Id]}">
        <slot name="column" :column="column" :index="index" :items="groupedItems[column.Id] || []"></slot>
      </div>
    </div>
  </div>
</template>
<script>
import './styles.scss';
import { Component, Prop, Vue } from 'vue-property-decorator';
import itfIcon from '../icon/Icon';
import itfButton from '../button/Button';
import loading from '../../directives/loading';
import itfForm from '../form/Form';
import itfEditButton from '../editable/EditButton.vue';
import createDraggable from '../sortable/draggable';
import { debounce } from '../../helpers/debounce';

const { Node, ...draggableDirectives } = createDraggable();

export default @Component({
  components: {
    itfIcon,
    itfEditButton,
    itfForm,
    itfButton
  },
  directives: {
    ...draggableDirectives,
    loading
  },
  provide() {
    return { board: this }; // do not use Provide from vue-property-decorator
  }
})
class Board extends Vue {
  @Prop(Array) columns;
  @Prop(Array) items;
  @Prop(Array) boardColumnsOrder
  @Prop(Array) columnOrders;
  @Prop({ type: Function, default: (item) => item.Status?.Id }) groupFunc;
  @Prop({ type: Function, default: (item, value) => { item.Status = value; } }) updateFunc;
  @Prop({ type: String, default: 'Name' }) columnNameKey;
  @Prop(Boolean) columnSorting;
  @Prop({ type: Boolean, default: true }) columnEdit;

  beforeDestroy() {
    if (this.columnSorting) {
      Node.removeContainer(this.$refs.container);
    }
  }

  mounted() {
    this.itemStatusChanged = debounce(this.emitItemStatusChanged, 10);
    if (this.columnSorting) {
      Node.addContainer(this.$refs.container);
    }
  }

  get groupedItems() {
    const grouped = {};
    for (const column of this.columns) {
      grouped[column.Id] = this.orderItemsInColumn(column);
    }
    return grouped;
  }

  get orderColumnsInBoard() {
    return this.sortByOrdering(this.columns, this.boardColumnsOrder || []);
  }

  orderItemsInColumn(column) {
    const columnOrdering = (this.columnOrders.find((c) => c.Id === column.Id) || { ItemIds: [] }).ItemIds;
    let items = this.items.filter((item) => column.Id === this.groupFunc(item));
    return this.sortByOrdering(items, columnOrdering);
  }

  sortByOrdering (arr, ordering) {
    const arrCopy = arr.slice();
    return arrCopy.sort((a, b) => {
      const aIndex = ordering.findIndex((id) => id === a.Id);
      const bIndex = ordering.findIndex((id) => id === b.Id);
      if (aIndex === -1 && bIndex === -1) {
        return 0;
      }
      if (aIndex === -1) {
        return 1;
      }
      if (bIndex === -1) {
        return -1;
      }
      return aIndex - bIndex;
    });
  }

  columnHighlight(index, state, className = 'over') {
    Array.from(this.$el.querySelectorAll(`[data-column="${index}"]`)).forEach(t=>{
      state === 'enter' ? t.classList.add(className) : t.classList.remove(className)
    });
  }
  columnHighlightLast(state, className = 'over') {
    Array.from(this.$el.querySelectorAll(`[data-column="${this.columns.length - 1}"]`)).forEach(t=>{
      if (state === "enter") {
        t.classList.add(className);
        t.classList.add('right');
      } else {
        t.classList.remove(className);
        t.classList.remove('right');
      }
    });
  }

  reorderColumns({ detail }) {
    const { index: fromIndex } = detail.draggablePayload;
    const { index: toIndex, last } = detail.dropzonePayload;
    const newValue = [...this.orderColumnsInBoard];
    const [removed] = newValue.splice(fromIndex, 1);

    if (last) {
      newValue.push(removed);
    } else {
      newValue.splice((fromIndex < toIndex) ? toIndex - 1 : toIndex, 0, removed);
    }
    this.$emit('update:columns', newValue);
    if (last) {
      this.columnHighlightLast('leave');
      this.columnHighlightLast('leave', 'drop');
    } else {
      this.columnHighlight(toIndex, 'leave');
      this.columnHighlight(toIndex, 'leave', 'drop');
    }
    // for (const dropdown of this.$refs.dropdown) {
    //   dropdown.hide();
    // }
  }

  reorderCards({ detail }) {
    const { item, column: fromColumn } = detail.draggablePayload;
    const { index: toIndex, last, column } = detail.dropzonePayload;

    const items = [...this.items];
    const itemIndex = items.findIndex(t => t === item);

    const newItem = { ...items[itemIndex] };
    this.updateFunc(newItem, column);

    items[itemIndex] = newItem;

    this.lastUpdatedIndex = typeof this.lastUpdatedIndex === 'undefined' ? toIndex : this.lastUpdatedIndex; // запамятовуємо індекс, бо виклики може бути два, коли колонка і карточка обробляє
    this.itemStatusChanged(items[itemIndex], column, items, () => {
      const toIndex = this.lastUpdatedIndex; // беремо індекс з пам'яті, бо викликів може бути два, але індекс тільки в картці, якщо нема, то це колонка
      this.lastUpdatedIndex = undefined; // обнуляємо, щоб при наступному перетягувані вже не брало його
      const newOrders = [...this.columnOrders];
      let sorting = newOrders.find((c) => c.Id === column.Id);
      if (!sorting) {
        sorting = { Id: column.Id, ItemIds: [] };
        newOrders.push(sorting)
      }
      let fromSorting = newOrders.find((c) => c.Id === fromColumn.Id);
      if (!fromSorting) {
        fromSorting = { Id: fromColumn.Id, ItemIds: [] };
        newOrders.push(fromSorting)
      }
      // update from
      fromSorting.ItemIds = this.groupedItems[fromColumn.Id].map((i) => i.Id).filter((t) => t !== newItem.Id);
      // update to
      const items = [...this.groupedItems[column.Id]];
      let placeIndex = last ? items.length : toIndex;
      if (typeof placeIndex === 'undefined') { // ідекса може не бути, якщо на колонку переносять, тоді в кінець
        placeIndex = items.length;
      }
      const itemsInColumn = items.map((t) => t.Id).filter((t) => t !== newItem.Id);
      itemsInColumn.splice(placeIndex, 0, newItem.Id);
      sorting.ItemIds = itemsInColumn;
      this.$emit('update:columnOrders', newOrders);
    });
  }

  emitItemStatusChanged(item, column, items, updateOrder) {
    updateOrder();

    this.$emit('update:item', [item, column]);
    this.$emit('update:items', items);
  }
}
</script>
