<template>
  <div
    ref="cell"
    class="grid-view__cell grid-field-file__cell"
    :class="{ active: selected }"
    @drop.prevent="onDrop($event)"
    @dragover.prevent
    @dragenter.prevent="dragEnter($event)"
    @dragleave="dragLeave($event)"
  >
    <div v-show="dragging" class="grid-field-file__dragging">
      <div>
        <i class="grid-field-file__drop-icon iconoir-cloud-upload"></i>
        {{ $t('gridViewFieldFile.dropHere') }}
      </div>
    </div>
    <ul v-if="Array.isArray(value)" class="grid-field-file__list">
      <li
        v-for="(file, index) in value"
        :key="file.name + index"
        class="grid-field-file__item"
      >
        <a
          v-tooltip="file.visible_name"
          class="grid-field-file__link"
          @click.prevent="showFileModal(index)"
        >
          <img
            v-if="file.is_image"
            class="grid-field-file__image"
            :src="file.thumbnails.tiny.url"
          />
          <i
            v-else
            class="grid-field-file__icon"
            :class="getIconClass(file.mime_type)"
          ></i>
        </a>
      </li>
      <li
        v-for="loading in loadings"
        :key="loading.id"
        class="grid-field-file__item"
      >
        <div class="grid-field-file__loading"></div>
      </li>
      <li v-if="!readOnly" v-show="selected" class="grid-field-file__item">
        <a class="grid-field-file__item-add" @click.prevent="showUploadModal()">
          <i class="iconoir-plus"></i>
        </a>
        <div v-if="value.length == 0" class="grid-field-file__drop">
          <i class="grid-field-file__drop-icon iconoir-cloud-upload"></i>
          {{ $t('gridViewFieldFile.dropFileHere') }}
        </div>
      </li>
    </ul>
    <UserFilesModal
      v-if="Array.isArray(value) && !readOnly"
      ref="uploadModal"
      @uploaded="addFiles(value, $event)"
      @hidden="hideModal"
    ></UserFilesModal>
    <FileFieldModal
      v-if="Array.isArray(value)"
      ref="fileModal"
      :files="value"
      :read-only="readOnly"
      @hidden="hideModal"
      @removed="removeFile(value, $event)"
      @renamed="renameFile(value, $event.index, $event.value)"
    ></FileFieldModal>
  </div>
</template>

<script>
import { uuid } from '@baserow/modules/core/utils/string'
import { isElement } from '@baserow/modules/core/utils/dom'
import { notifyIf } from '@baserow/modules/core/utils/error'
import UserFilesModal from '@baserow/modules/core/components/files/UserFilesModal'
import { UploadFileUserFileUploadType } from '@baserow/modules/core/userFileUploadTypes'
import UserFileService from '@baserow/modules/core/services/userFile'
import FileFieldModal from '@baserow/modules/database/components/field/FileFieldModal'
import gridField from '@baserow/modules/database/mixins/gridField'
import fileField from '@baserow/modules/database/mixins/fileField'

export default {
  name: 'GridViewFieldFile',
  components: { UserFilesModal, FileFieldModal },
  mixins: [gridField, fileField],
  data() {
    return {
      modalOpen: false,
      dragging: false,
      loadings: [],
      dragTarget: null,
    }
  },
  methods: {
    /**
     * Method is called when the user drops his files into the field. The files should
     * automatically be uploaded to the user files and added to the field after that.
     */
    async onDrop(event) {
      const files = [...event.dataTransfer.items].map((item) =>
        item.getAsFile()
      )
      await this.uploadFiles(files)
    },
    async uploadFiles(fileArray) {
      if (this.readOnly) {
        return
      }

      this.dragging = false

      // Indicates that this component must not be destroyed even though the user might
      // select another cell.
      this.$emit('add-keep-alive')

      const files = fileArray.map((file) => {
        return {
          id: uuid(),
          file,
        }
      })

      if (files === null) {
        return
      }

      this.$emit('select')

      // First add the file ids to the loading list so the user sees a visual loading
      // indication for each file.
      files.forEach((file) => {
        this.loadings.push({ id: file.id })
      })

      // Now upload the files one by one to not overload the backend. When finished,
      // regardless of is has succeeded, the loading state for that file can be removed
      // because it has already been added as a file.
      for (let i = 0; i < files.length; i++) {
        const id = files[i].id
        const file = files[i].file

        try {
          const { data } = await UserFileService(this.$client).uploadFile(file)
          this.addFiles(this.value, [data])
        } catch (error) {
          notifyIf(error, 'userFile')
        }

        const index = this.loadings.findIndex((l) => l.id === id)
        this.loadings.splice(index, 1)
      }

      // Indicates that this component can be destroyed if it is not selected.
      this.$emit('remove-keep-alive')
    },
    select() {
      // While the field is selected we want to open the select row toast by pressing
      // the enter key.
      this.$el.keydownEvent = (event) => {
        if (event.key === 'Enter' && !this.modalOpen) {
          this.showUploadModal()
        }
      }
      document.body.addEventListener('keydown', this.$el.keydownEvent)
    },
    beforeUnSelect() {
      document.body.removeEventListener('keydown', this.$el.keydownEvent)
    },
    /**
     * If the user clicks inside the select row modal we do not want to unselect the
     * field. The modal lives in the root of the body element and not inside the cell,
     * so the system naturally wants to unselect when the user clicks inside one of
     * these contexts.
     */
    canUnselectByClickingOutside(event) {
      return (
        (!this.$refs.uploadModal ||
          !isElement(this.$refs.uploadModal.$el, event.target)) &&
        !isElement(this.$refs.fileModal.$el, event.target)
      )
    },
    /**
     * Prevent unselecting the field cell by changing the event. Because the deleted
     * item is not going to be part of the dom anymore after deleting it will get
     * noticed as if the user clicked outside the cell which wasn't the case.
     */
    removeFile(event, index) {
      event.preventFieldCellUnselect = true
      return fileField.methods.removeFile.call(this, event, index)
    },
    showUploadModal() {
      if (this.readOnly) {
        return
      }

      this.modalOpen = true
      this.$refs.uploadModal.show(UploadFileUserFileUploadType.getType())
    },
    showFileModal(index) {
      this.modalOpen = true
      this.$refs.fileModal.show(index)
    },
    hideModal() {
      this.modalOpen = false
    },
    /**
     * While the modal is open, all key combinations related to the field must be
     * ignored.
     */
    canSelectNext() {
      return !this.modalOpen
    },
    canKeyDown() {
      return !this.modalOpen
    },
    canKeyboardShortcut() {
      return !this.modalOpen
    },
    dragEnter(event) {
      if (this.readOnly) {
        return
      }

      this.dragging = true
      this.dragTarget = event.target
    },
    dragLeave(event) {
      if (this.dragTarget === event.target && !this.readOnly) {
        event.stopPropagation()
        event.preventDefault()
        this.dragging = false
        this.dragTarget = null
      }
    },
    onPaste(event) {
      if (
        !event.clipboardData.types.includes('text/plain') ||
        event.clipboardData.getData('text/plain').startsWith('file:///')
      ) {
        const { items } = event.clipboardData
        for (let i = 0; i < items.length; i++) {
          const item = items[i]
          if (item.type.includes('image')) {
            const file = item.getAsFile()
            this.uploadFiles([file])
            return true
          }
        }
      }
      return false
    },
  },
}
</script>