<template>
  <div class="data-list task-list">
    <div
      ref="body"
      class="datatable-wrapper"
    >
      <table class="datatable">
        <thead class="datatable-head">
          <tr>
            <th
              ref="th-prod"
              scope="col"
              class="production datatable-row-header datatable-row-header--nobd"
            >
              {{ $t('tasks.fields.production') }}
            </th>
            <th
              ref="th-type"
              scope="col"
              class="type datatable-row-header datatable-row-header--nobd"
              :style="{ left: colTypePosX }"
            >
              {{ $t('tasks.fields.task_type') }}
            </th>
            <th
              ref="th-name"
              scope="col"
              class="name datatable-row-header"
              :style="{ left: colNamePosX }"
            >
              {{ $t('tasks.fields.entity') }}
            </th>
            <th
              v-if="isDescriptionPresent"
              class="description"
              scope="col"
            >
              {{ $t('assets.fields.description') }}
            </th>
            <th
              scope="col"
              class="estimation"
              :title="$t('main.estimation')"
            >
              {{ $t('main.estimation_short') }}
            </th>
            <th
              scope="col"
              class="estimation"
            >
              {{ $t('tasks.fields.duration').substring(0, 3) }}.
            </th>
            <th
              scope="col"
              class="start-date"
            >
              {{ $t('tasks.fields.start_date_short') }}
            </th>
            <th
              scope="col"
              class="due-date"
            >
              {{ $t('tasks.fields.due_date') }}
            </th>
            <metadata-header
              v-for="field_name in Object.keys(metadataDescriptorsMap)"
              :key="'desc-header' + field_name"
              :descriptor="
                mergeMetadataDescriptors(metadataDescriptorsMap[field_name])
              "
              :no-menu="true"
            />
            <th
              scope="col"
              class="status"
            >
              {{ $t('tasks.fields.task_status') }}
            </th>
            <th
              v-if="!done"
              scope="col"
              class="last-comment"
            >
              {{ $t('tasks.fields.last_comment') }}
            </th>
            <th
              v-else
              scope="col"
              class="end-date"
            >
              {{ $t('tasks.fields.end_date') }}
            </th>
          </tr>
        </thead>
        <tbody
          v-if="tasks.length > 0"
          class="datatable-body"
        >
          <tr
            v-for="(entry, i) in displayedTasks"
            :key="entry + '-' + i"
            :class="{
              'datatable-row': true,
              'datatable-row--selectable': true,
              selected:
                selectionGrid && selectionGrid[i] ? selectionGrid[i][0] : false
            }"
            @click="onLineClicked(i, $event)"
          >
            <td
              class="production datatable-row-header datatable-row-header--nobd"
              scope="row"
            >
              <production-name-cell
                :is-tooltip="true"
                :entry="productionMap.get(entry.project_id)"
                :only-avatar="true"
                :is-link="false"
              />
            </td>
            <task-type-cell
              class="type datatable-row-header datatable-row-header--nobd"
              :production-id="entry.project_id"
              :task-type="getTaskType(entry)"
              :style="{ left: colTypePosX }"
            />
            <td
              class="name datatable-row-header"
              :style="{ left: colNamePosX }"
            >
              <div class="flexrow">
                <entity-thumbnail
                  :empty-width="60"
                  :empty-height="40"
                  :width="60"
                  :height="40"
                  :entity="{ preview_file_id: entry.entity_preview_file_id }"
                  :with-link="false"
                />
                {{ entry.full_entity_name }}
              </div>
            </td>
            <description-cell
              v-if="isDescriptionPresent"
              class="description"
              :entry="{ description: entry.entity_description }"
            />
            <td class="estimation">
              {{ formatDuration(entry.estimation) }}
            </td>
            <td class="estimation">
              {{ formatDuration(entry.duration) }}
            </td>
            <td class="start-date">
              {{ formatDate(entry.start_date) }}
            </td>
            <td class="due-date">
              {{ formatDate(entry.due_date) }}
            </td>
            <td
              v-for="fieldName in Object.keys(metadataDescriptorsMap)"
              :key="'desc-' + entry.id + '-' + fieldName"
              class="metadata-descriptor"
            >
              <div
                v-if="
                  entry.entity_data && getMetadataDescriptor(fieldName, entry)
                "
              >
                <div
                  v-if="
                    getDescriptorChecklistValues(
                      getMetadataDescriptor(fieldName, entry)
                    ).length > 0
                  "
                >
                  <p
                    v-for="(option, i) in getDescriptorChecklistValues(
                      getMetadataDescriptor(fieldName, entry)
                    )"
                    :key="`${entry.id}-
                    ${getMetadataDescriptor(fieldName, entry).id}
                    -${i}-${option.text}-div`"
                  >
                    <input
                      :id="`${entry.id}
                      -${getMetadataDescriptor(fieldName, entry).id}
                      -${i}-${option.text}-input`"
                      type="checkbox"
                      disabled
                      :checked="
                        getMetadataChecklistValues(
                          getMetadataDescriptor(fieldName, entry),
                          entry
                        )[option.text]
                      "
                    >
                    <label
                      style="cursor: pointer"
                      :for="`${entry.id}
                      -${getMetadataDescriptor(fieldName, entry).id}
                      -${i}-${option.text}-input`"
                    >
                      {{ option.text }}
                    </label>
                  </p>
                </div>
                <p v-else>
                  {{
                    getMetadataFieldValue(
                      getMetadataDescriptor(fieldName, entry),
                      entry
                    )
                  }}
                </p>
              </div>
            </td>
            <validation-cell
              :ref="'validation-' + i + '-0'"
              class="status unselectable"
              :task-test="entry"
              :is-border="false"
              :is-assignees="false"
              :selectable="!done"
              :clickable="false"
              :selected="
                selectionGrid && selectionGrid[i] ? selectionGrid[i][0] : false
              "
              :rowX="i"
              :columnY="0"
              :column="entry.taskStatus"
              @select="onTaskSelected"
              @unselect="onTaskUnselected"
            />
            <last-comment-cell
              v-if="!done"
              class="last-comment"
              :task="entry"
            />
            <td
              v-else
              class="end-date"
            >
              {{ formatDate(entry.end_date) }}
            </td>
          </tr>
        </tbody>
      </table>
    </div>

    <table-info
      :is-loading="isLoading"
      :is-error="isError"
    />

    <div
      v-if="tasks.length === 0 && !isLoading"
      class="has-text-centered empty-list"
    >
      <p>
        <img src="@/assets/illustrations/empty_todo.png">
      </p>
      <p>
        {{ $t('people.no_task_assigned') }}
      </p>
    </div>

    <p
      v-if="tasks.length && !isLoading"
      class="has-text-centered footer-info"
    >
      {{ tasks.length }} {{ $tc('tasks.tasks', tasks.length) }}
    </p>
  </div>
</template>

<script>
import { mapGetters, mapActions } from 'vuex'

import { selectionListMixin } from '@/components/mixins/selection'
import { formatListMixin } from '@/components/mixins/format'
import { descriptorMixin } from '@/components/mixins/descriptors'
import { PAGE_SIZE } from '@/lib/pagination'
import { formatSimpleDate } from '@/lib/time'

import EntityThumbnail from '@/components/widgets/EntityThumbnail'
import DescriptionCell from '@/components/cells/DescriptionCell'
import LastCommentCell from '@/components/cells/LastCommentCell'
import ProductionNameCell from '@/components/cells/ProductionNameCell'
import TaskTypeCell from '@/components/cells/TaskTypeName'
import TableInfo from '@/components/widgets/TableInfo'
import ValidationCell from '@/components/cells/ValidationCell'
import MetadataHeader from '@/components/cells/MetadataHeader'

export default {
  name: 'TodosList',
  mixins: [formatListMixin, selectionListMixin, descriptorMixin],

  components: {
    EntityThumbnail,
    DescriptionCell,
    LastCommentCell,
    ProductionNameCell,
    TableInfo,
    TaskTypeCell,
    ValidationCell,
    MetadataHeader
  },

  props: ['done', 'tasks', 'isLoading', 'isError', 'selectionGrid'],

  data() {
    return {
      page: 1,
      colTypePosX: '',
      colNamePosX: ''
    }
  },

  mounted() {
    this.page = 1
    this.resizeHeaders()
    window.addEventListener('keydown', this.onKeyDown, false)
    this.colTypePosX = this.$refs['th-prod'].offsetWidth + 'px'
    this.colNamePosX =
      this.$refs['th-prod'].offsetWidth +
      this.$refs['th-type'].offsetWidth +
      'px'
  },

  beforeUnmount() {
    window.removeEventListener('keydown', this.onKeyDown)
  },

  computed: {
    ...mapGetters([
      'nbSelectedTasks',
      'taskTypeMap',
      'productionMap',
      'openProductions',
      'user'
    ]),

    displayedTasks() {
      return this.tasks.slice(0, this.page * PAGE_SIZE)
    },

    isDescriptionPresent() {
      return this.tasks.some((task) => {
        return task.entity_description && task.entity_description.length > 0
      })
    },

    metadataDescriptorsMap() {
      const metadataDescriptorsMap = {}
      this.openProductions.forEach((project) => {
        project.descriptors.forEach((descriptor) => {
          const isUserDepartment = this.user.departments.some((department) =>
            descriptor.departments.includes(department)
          )
          if (isUserDepartment) {
            // group them by field_name if they have the same field_name
            if (!(descriptor.field_name in metadataDescriptorsMap)) {
              metadataDescriptorsMap[descriptor.field_name] = {}
            }
            const descriptorFieldNameEntry =
              metadataDescriptorsMap[descriptor.field_name]
            // group them by entity_type if the have the same entity_type
            if (!(descriptor.entity_type in descriptorFieldNameEntry)) {
              descriptorFieldNameEntry[descriptor.entity_type] = {}
            }
            descriptorFieldNameEntry[descriptor.entity_type][project.id] =
              descriptor
          }
        })
      })
      return metadataDescriptorsMap
    }
  },

  methods: {
    ...mapActions([]),

    setScrollPosition(scrollPosition) {
      if (this.$refs.body) {
        this.$refs.body.scrollTop = scrollPosition
      }
    },

    formatDate(date) {
      return date ? formatSimpleDate(date) : ''
    },

    onBodyScroll(event, position) {
      this.$emit('scroll', position.scrollTop)
      const maxHeight =
        this.$refs.body.scrollHeight - this.$refs.body.offsetHeight
      if (maxHeight < position.scrollTop + 100) {
        this.page++
      }
    },

    onLineClicked(i, event) {
      const ref = 'validation-' + i + '-0'
      const validationCell = this.$refs[ref][0]
      if (validationCell) {
        validationCell.select(event)
      }
    },

    onTaskSelected(validationInfo) {
      if (validationInfo.isShiftKey) {
        if (this.lastSelection) {
          let startX = this.lastSelection.x
          let endX = validationInfo.x
          if (validationInfo.x < this.lastSelection.x) {
            startX = validationInfo.x
            endX = this.lastSelection.x
          }

          for (let i = startX; i <= endX; i++) {
            const ref = 'validation-' + i + '-' + 0
            const validationCell = this.$refs[ref][0]
            if (!this.selectionGrid[i][0]) {
              validationCell.select({ ctrlKey: true, isUserClick: false })
            }
          }
        }
      } else if (!validationInfo.isCtrlKey) {
        this.$store.commit('CLEAR_SELECTED_TASKS')
      }
      this.$store.commit('ADD_SELECTED_TASK', validationInfo)

      if (!validationInfo.isShiftKey && validationInfo.isUserClick) {
        const x = validationInfo.x
        const y = 0
        this.lastSelection = { x, y }
        const ref = 'validation-' + x + '-' + y
        const validationCell = this.$refs[ref][0]
        this.$nextTick(() => {
          this.scrollToValidationCell(validationCell)
        })
      }
    },

    onTaskUnselected(validationInfo) {
      if (!validationInfo.isCtrlKey) {
        if (this.nbSelectedTasks === 1) {
          this.$store.commit('REMOVE_SELECTED_TASK', validationInfo)
        } else {
          this.$store.commit('CLEAR_SELECTED_TASKS')
          this.$store.commit('ADD_SELECTED_TASK', validationInfo)
        }
      } else {
        this.$store.commit('REMOVE_SELECTED_TASK', validationInfo)
      }
    },

    getTaskType(entry) {
      const taskType = this.taskTypeMap.get(entry.task_type_id)
      const production = this.productionMap.get(entry.project_id)
      this.$store.commit('SET_EPISODE_ON_TASK_TYPE', {
        taskType,
        episodeId: entry.episode_id
      })
      if (
        production &&
        production.production_type === 'tvshow' &&
        !entry.episode_id
      ) {
        this.$store.commit('SET_EPISODE_ON_TASK_TYPE', {
          taskType,
          episodeId: production.first_episode_id
        })
      }
      return taskType
    },

    entityPath(entity) {
      const entityType = entity.sequence_name ? 'shot' : 'asset'
      const route = {
        name: entityType,
        params: {
          production_id: entity.project_id
        }
      }

      if (entityType === 'asset') {
        route.params.asset_id = entity.entity_id
      } else {
        route.params.shot_id = entity.entity_id
      }

      const production = this.productionMap.get(entity.project_id)
      let episodeId = entity.episode_id
      if (production && production.production_type === 'tvshow' && !episodeId) {
        if (entityType === 'shot') {
          episodeId = production.first_episode_id
        } else {
          episodeId = 'main'
        }
      }

      if (episodeId) {
        route.name = `episode-${entityType}`
        route.params.episode_id = episodeId
      }

      return route
    },

    onKeyDown(event) {
      const lastSelection = this.lastSelection
        ? this.lastSelection
        : { x: 0, y: 0 }
      const i = lastSelection.x
      const j = lastSelection.y
      let validationCell = null
      if (event.ctrlKey || event.metaKey) {
        if (event.keyCode === 37) {
          validationCell = this.select(i, j - 1)
        } else if (event.keyCode === 38) {
          validationCell = this.select(i - 1, j)
        } else if (event.keyCode === 39) {
          validationCell = this.select(i, j + 1)
        } else if (event.keyCode === 40) {
          validationCell = this.select(i + 1, j)
        }
        this.scrollToValidationCell(validationCell)
      }
    },

    scrollToValidationCell(validationCell) {
      if (validationCell) {
        const margin = 20
        const rect = validationCell.$el.getBoundingClientRect()
        const listRect = this.$refs.body.getBoundingClientRect()
        const isBelow = rect.bottom > listRect.bottom - margin
        const isAbove = rect.top < listRect.top + margin
        const isRight = rect.right > listRect.right - margin
        const isLeft = rect.left < listRect.left + margin

        if (isBelow) {
          const scrollingRequired = rect.bottom - listRect.bottom + margin
          this.setScrollPosition(this.$refs.body.scrollTop + scrollingRequired)
        } else if (isAbove) {
          const scrollingRequired = listRect.top - rect.top + margin
          this.setScrollPosition(this.$refs.body.scrollTop - scrollingRequired)
        }

        if (isRight) {
          const scrollingRequired = rect.right - listRect.right + margin
          this.setScrollLeftPosition(
            this.$refs.body.scrollLeft + scrollingRequired
          )
        } else if (isLeft) {
          const scrollingRequired = listRect.left - rect.left + margin
          this.setScrollLeftPosition(
            this.$refs.body.scrollLeft - scrollingRequired
          )
        }
      }
    },

    select(i, j) {
      const ref = 'validation-' + i + '-' + j
      const validationCell = this.$refs[ref]
      if (validationCell) validationCell[0].$el.click()
      return validationCell ? validationCell[0] : 0
    },

    resizeHeaders() {
      const tableBody = this.$refs['body-tbody']
      const isTableBodyContainLines = tableBody && tableBody.children
      if (isTableBodyContainLines) {
        const bodyElement = tableBody.children[0]
        const columnDescriptors = [
          { index: 1, name: 'type' },
          { index: 3, name: 'name' }
        ]
        columnDescriptors.forEach((desc) => {
          const width = Math.max(
            bodyElement.children[desc.index].offsetWidth,
            100
          )
          this.$refs['th-' + desc.name].style['min-width'] = `${width}px`
        })
      }
    },

    mergeMetadataDescriptors(descriptors) {
      const firstKeyEntityType = Object.keys(descriptors)[0]
      const firstKeyProjectId = Object.keys(descriptors[firstKeyEntityType])[0]
      const mergedDescriptors = {
        departments: [],
        field_name:
          descriptors[firstKeyEntityType][firstKeyProjectId].field_name,
        name: descriptors[firstKeyEntityType][firstKeyProjectId].name
      }
      // merge departments
      Object.keys(descriptors).forEach((entityType) =>
        Object.keys(descriptors[entityType]).forEach((projectId) => {
          mergedDescriptors.departments = [
            ...new Set([
              ...descriptors[entityType][projectId].departments,
              ...mergedDescriptors.departments
            ])
          ]
        })
      )
      return mergedDescriptors
    },

    getMetadataDescriptor(fieldName, entry) {
      const entityType = entry.task_type_for_entity
      const projectId = entry.project_id
      return this.metadataDescriptorsMap[fieldName] &&
        this.metadataDescriptorsMap[fieldName][entityType] &&
        this.metadataDescriptorsMap[fieldName][entityType][projectId]
        ? this.metadataDescriptorsMap[fieldName][entityType][projectId]
        : null
    }
  }
}
</script>

<style lang="scss" scoped>
.datatable-body tr:first-child th,
.datatable-body tr:first-child td {
  border-top: 0;
}

.datatable .datatable-row {
  cursor: pointer;
}

.name {
  width: 300px;
  min-width: 300px;
}

.description {
  width: 200px;
  min-width: 200px;
}

.description li {
  list-style-type: disc;
  margin-left: 2em;
}

.name a {
  color: inherit;
}

.production {
  width: 70px;
  min-width: 70px;
  max-width: 70px;
}

.type {
  width: 130px;
  min-width: 130px;
}

.status {
  width: 100px;
  min-width: 100px;
}

.estimation {
  width: 60px;
  min-width: 60px;
}

td.estimation {
  text-align: center;
}

.start-date,
.due-date {
  width: 110px;
  min-width: 110px;
}

td.due-date {
  border-right: 1px solid var(--border-alt);
}

th.last-comment {
  max-width: 100%;
  width: 100%;
}

td.last-comment {
  min-width: 250px;
}

td.end-date {
  width: 100%;
  min-width: 150px;
  color: $grey;
}

.thumbnail {
  min-width: 60px;
  max-width: 60px;
  width: 60px;
  padding: 0;
}

.empty-list img {
  max-width: 80vh;
}

.entity-name {
  color: var(--text);
  font-weight: bold;
}
</style>
