<!--
Program: Multi File Uploader
Author: SDL
Description: Multi File Common Component
-->
<template>
  <div class="ui--file-attachment" :class="{ active: modifyFlag && !itemsAdded, drag: modifyFlag && itemsAdded && dragging }">
    <p class="message"><i class="bi bi-cloud-upload"></i><span v-html="initMsg"></span></p>
    <input
      type="file"
      :id="`fileAttach${downloadType}`"
      name="items[]"
      multiple
      @change="onChange"
      class="addfile"
      @dragover="ignoreDrag"
      @dragenter="ignoreDrag"
      @drop="onChange"
      @dragend="dragging = false"
      @dragleave="dragging = false"
      :style="(modifyFlag && itemsAdded && dragging) || (modifyFlag && !itemsAdded) ? 'z-index:1001;position:absolute;top:0;bottom:0;left:0;right:0;width:100%;height:100%;' : 'width:0%;height:0%;z-index:-1;'"
    />
    <div class="ui--file-list" :class="{ loaderArea: isLoaderVisible }" @dragenter="dragging = true">
      <!-- file-button -->
      <div class="ui--file-button" v-if="modifyFlag">
        <input type="checkbox" @click.stop v-model="selectAll" @change="toggleSelectAll" />
        <button type="button" class="btn ui--btn-icon-sm" @click="moveUp">
          <span class="ui--blind">{{ $t('sdl.common.label.moveup') }}</span
          ><i class="bi bi-caret-up-fill"></i>
        </button>
        <button type="button" class="btn ui--btn-icon-sm" @click="moveDown">
          <span class="ui--blind">{{ $t('sdl.common.label.movedown') }}</span
          ><i class="bi bi-caret-down-fill"></i>
        </button>
        <button type="button" class="btn ui--btn-icon-sm" @click="selectRemoveItem">
          <span class="ui--blind">{{ $t('sdl.common.label.delete') }}</span
          ><i class="bi bi-x"></i>
        </button>
        <span class="ms-2 me-2 ui--slide">|</span>
        <span
          >{{ $t('sdl.common.label.total') }}<strong class="ms-1 me-2">{{ itemsAdded }}</strong
          >{{ itemsTotalSize }} / {{ getItemsMaxTotalSize }}</span
        >
        <label class="btn btn-outline-secondary ms-3 ui--btn-xs" style="margin-bottom: 0px" :for="`fileAttach${downloadType}`">
          {{ $t('sdl.common.label.fileAddition') }}
        </label>
      </div>
      <div class="ui--file-button" v-else>
        <input type="checkbox" @click.stop v-model="selectAll" @change="toggleSelectAll" />
        <span
          >{{ $t('sdl.common.label.total') }}<strong class="ms-1 me-2">{{ itemsAdded }}</strong
          >({{ itemsTotalSize }})</span
        >
        <!--        If you want to download compressed into a zip file -->
        <!--        <button type="button" @click="checkedDownload" class="btn btn-outline-secondary ms-3 ui&#45;&#45;btn-xs">-->
        <button type="button" @click="checkedDownloadNonZip" class="btn btn-outline-secondary ms-3 ui--btn-xs">
          {{ $t('sdl.common.label.alldownload') }}
        </button>
      </div>
      <!-- // file-button -->
      <!-- file-list -->
      <ul>
        <li v-for="item in itemsNames" :key="item.fileName">
          <div class="form-check">
            <input type="checkbox" class="form-check-input" v-model="item.selected" @change="selectRow(item)" />
            <label class="form-check-label" @click="download(item)" :style="!modifyFlag ? 'cursor:pointer;' : ''">
              {{ item.fileName }}
              <small>
                <small>({{ bytesToSize(item.fileSize) }})</small>
              </small>
            </label>
          </div>
          <button type="button" class="btn ui--btn-icon-sm" v-show="modifyFlag" @click="removeItem(item)">
            <span class="ui--blind">{{ $t('sdl.common.label.delete') }}</span
            ><i class="bi bi-x"></i>
          </button>
        </li>
      </ul>
      <div class="loader" :class="{ loaderArea: isLoaderVisible }" v-show="false">
        <div class="loaderImg"></div>
      </div>
      <!-- // file-list -->
    </div>
  </div>
</template>

<script>
import axios from 'axios';
import SDLUtil from '@/utils/SDLUtil';

export default {
  props: {
    modifyFlag: {
      type: Boolean,
      default: true,
    },
    uploadURL: {
      type: String,
      required: false,
      default: `${SDLUtil.API_URL}/resource/attachments/multifile-upload`,
    },
    multiFileNm: {
      type: String,
      default: '',
    },
    downloadType: {
      type: String,
      required: true,
    },
    fileList: {
      type: Array,
    },
    useExtList: {
      type: String,
      default: 'zip|xlsx|xls|ppt|pptx',
    },
    maxItems: {
      type: Number,
      default: 10,
    },
    method: {
      type: String,
      default: 'post',
    },
    // postMeta: {
    //   type: [String, Array, Object],
    //   default: '',
    // },
    // postData: {
    //   type: [Object],
    //   default: () => {},
    // },
    postHeader: {
      type: [Object],
      default: () => {},
    },
    httpMethodErrorMessage: {
      type: String,
      default: "This HTTP method is not allowed. Please use either 'put' or 'post' methods.",
    },
    maxTotalSize: {
      type: Number,
      default: 1073741824,
    },
  },

  /*
   * The component's data.
   */
  data() {
    return {
      dragging: false,
      items: [],
      itemsAdded: '',
      itemsNames: [],
      itemsTotalSize: 0,
      itemsMaxTotalSize: 0,
      formData: '',
      successMsg: '',
      errorMsg: '',
      isLoaderVisible: false,
      fileSizes: 0,
      selectAll: false,
      docId: '',
    };
  },
  computed: {
    /**
     * param:
     * return:
     * exception:
     * Description : get maximum attachment capacity
     */
    getItemsMaxTotalSize() {
      return this.bytesToSize(this.maxTotalSize);
    },
    initMsg() {
      return `${this.$t('sdl.common.message.uploadTheAttachedFileHere')}(${this.getItemsMaxTotalSize})`;
    },
  },
  created() {
    this.init();
  },
  methods: {
    // http://scratch99.com/web-development/javascript/convert-bytes-to-mb-kb/
    /**
     * param: bytes -> String
     * return: String
     * exception:
     * Description : convert bytes to size
     */
    bytesToSize(bytes) {
      const sizes = ['Bytes', 'KB', 'MB', 'GB', 'TB'];
      if (bytes === 0) return '0.00KB';
      const i = parseInt(Math.floor(Math.log(bytes) / Math.log(1024)), 10);
      if (i === 0) return `${bytes} ${sizes[i]}`;
      // eslint-disable-next-line no-restricted-properties
      return `${(bytes / Math.pow(1024, i)).toFixed(2)} ${sizes[i]}`;
    },
    /**
     * param:
     * return:
     * exception:
     * Description : select attch file
     */
    selectRow(item) {
      if (!item.selected) {
        if (this.selectAll) {
          this.selectAll = false;
        }
      }
    },
    /**
     * param:
     * return:
     * exception:
     * Description : If you select all
     */
    toggleSelectAll() {
      if (this.selectAll) {
        for (let i = 0; i < this.itemsNames.length; i += 1) {
          const selectItem = this.itemsNames[i];
          selectItem.selected = true;
          this.itemsNames.splice(i, 1, selectItem);
        }
      } else {
        for (let i = 0; i < this.itemsNames.length; i += 1) {
          const selectItem = this.itemsNames[i];
          selectItem.selected = false;
          this.itemsNames.splice(i, 1, selectItem);
        }
      }
    },
    /**
     * param:
     * return:
     * exception:
     * Description : move up
     */
    moveUp() {
      for (let i = 0; i < this.itemsNames.length; i += 1) {
        if (this.itemsNames[i].selected) {
          if (i === 0) {
            return;
          }
          const tmpItem = this.itemsNames[i];
          this.itemsNames.splice(i, 1, this.itemsNames[i - 1]);
          this.itemsNames.splice(i - 1, 1, tmpItem);
        }
      }
    },
    /**
     * param:
     * return:
     * exception:
     * Description : move down
     */
    moveDown() {
      for (let i = this.itemsNames.length - 1; i >= 0; i -= 1) {
        if (this.itemsNames[i].selected) {
          if (i === this.itemsNames.length - 1) {
            return;
          }
          const tmpItem = this.itemsNames[i];
          this.itemsNames.splice(i, 1, this.itemsNames[i + 1]);
          this.itemsNames.splice(i + 1, 1, tmpItem);
        }
      }
    },
    /**
     * param:
     * return:
     * exception:
     * Description : Delete selected files
     */
    selectRemoveItem() {
      let flag = true;
      const selectedItemList = [];
      this.itemsNames.forEach(item => {
        if (item.selected) selectedItemList.push(item);
      });
      selectedItemList.forEach(selectedItem => {
        this.removeItem(selectedItem);
        flag = false;
      });
      if (flag) {
        SDLUtil.alert('sdl.common.message.selectRow');
      }
    },
    /**
     * param:
     * return:
     * exception:
     * Description : Download selected files
     */
    checkedDownload() {
      let flag = true;
      const items = [];
      let fileName = '';
      for (let i = 0; i < this.itemsNames.length; i += 1) {
        if (this.itemsNames[i].selected) {
          if (fileName === '') {
            fileName = this.itemsNames[i].fileName.substring(fileName.lastIndexOf('.'));
          }
          items.push(this.itemsNames[i].fileId);
          flag = false;
        }
      }
      if (flag) {
        SDLUtil.alert('sdl.common.message.selectTheFileToDownload');
      } else if (items.length > 1) {
        this.download({ fileIds: items, fileName });
      } else {
        this.download({ fileId: items[0], fileName });
      }
    },
    async checkedDownloadNonZip() {
      let flag = true;
      const items = [];
      let fileName = '';
      for (let i = 0; i < this.itemsNames.length; i += 1) {
        if (this.itemsNames[i].selected) {
          if (fileName === '') {
            fileName = this.itemsNames[i].fileName.substring(fileName.lastIndexOf('.'));
          }
          items.push(this.itemsNames[i].fileId);
          flag = false;
        }
      }
      if (flag) {
        SDLUtil.alert('sdl.common.message.selectTheFileToDownload');
      } else if (items.length > 1) {
        for (let id of items) {
          await this.asyncDownload({ fileId: id });
        }
      } else {
        await this.asyncDownload({ fileId: items[0], fileName });
      }
    },
    /**
     * param:
     * return:
     * exception:
     * Description : download files
     */
    download(params) {
      if (this.modifyFlag) return;
      let path = '';
      if (typeof params.fileIds !== 'undefined') {
        if (this.multiFileNm !== '') {
          params.fileName = this.multiFileNm;
        }
        if (this.docId) {
          // case approval
          path = `${SDLUtil.API_URL}/approval/approval-documents/${this.docId}/attachments?fileIds=${params.fileIds}&zipFileName=${params.fileName}`;
        } else {
          path = `${SDLUtil.API_URL}/resource/attachments/multifile-download?fileIds=${params.fileIds}&downloadType=${this.downloadType}&zipFileName=${params.fileName}`;
        }
      } else if (this.docId) {
        // case approval
        path = `${SDLUtil.API_URL}/approval/approval-documents/${this.docId}/attachments/${params.fileId}`;
      } else {
        path = `${SDLUtil.API_URL}/resource/attachments/file-download/${params.fileId}?downloadType=${this.downloadType}`;
      }

      const getFileName = contentDisposition => {
        const fileName = contentDisposition
          .split(';')
          .filter(ele => ele.indexOf('filename') > -1)
          .map(ele => ele.replace(/"/g, '').split('=')[1]);
        return fileName[0] ? fileName[0] : null;
      };

      SDLUtil.showLoadingBar(true);
      axios({
        url: path,
        responseType: 'arraybuffer',
        method: 'get',
      })
        .then(response => {
          try {
            if (response.headers['content-type'] === 'application/json') {
              response.data = JSON.parse(String.fromCharCode.apply(null, Array.from(new Uint8Array(response.data))));
              SDLUtil.showLoadingBar(false);
              SDLUtil.errorAlert(response);
              return;
            }
            const blob = new Blob([response.data], { type: response.headers['content-type'] });

            let fileName = '';
            if (response.headers['content-disposition']) {
              fileName = getFileName(response.headers['content-disposition']);
            } else {
              // eslint-disable-next-line prefer-destructuring
              fileName = params.fileName;
            }

            fileName = decodeURI(fileName);

            if (window.navigator.msSaveOrOpenBlob) {
              // IE 10+
              window.navigator.msSaveOrOpenBlob(blob, fileName);
            } else {
              // not IE
              const link = document.createElement('a');
              link.href = window.URL.createObjectURL(blob);
              link.target = '_self';
              if (fileName) link.download = fileName;
              link.click();
            }
            SDLUtil.showLoadingBar(false);
            // return response.data;
          } catch (e) {
            SDLUtil.showLoadingBar(false);
            console.error(e);
          }
        })
        .catch(response => {
          SDLUtil.showLoadingBar(false);
          SDLUtil.alert(response.data);
        });
    },
    async asyncDownload(params) {
      if (this.modifyFlag) return;
      let path = '';
      if (typeof params.fileIds !== 'undefined') {
        if (this.multiFileNm !== '') {
          params.fileName = this.multiFileNm;
        }
        if (this.docId) {
          // case approval
          path = `${SDLUtil.API_URL}/approval/approval-documents/${this.docId}/attachments?fileIds=${params.fileIds}&zipFileName=${params.fileName}`;
        } else {
          path = `${SDLUtil.API_URL}/resource/attachments/multifile-download?fileIds=${params.fileIds}&downloadType=${this.downloadType}&zipFileName=${params.fileName}`;
        }
      } else if (this.docId) {
        // case approval
        path = `${SDLUtil.API_URL}/approval/approval-documents/${this.docId}/attachments/${params.fileId}`;
      } else {
        path = `${SDLUtil.API_URL}/resource/attachments/file-download/${params.fileId}?downloadType=${this.downloadType}`;
      }

      const getFileName = contentDisposition => {
        const fileName = contentDisposition
          .split(';')
          .filter(ele => ele.indexOf('filename') > -1)
          .map(ele => ele.replace(/"/g, '').split('=')[1]);
        return fileName[0] ? fileName[0] : null;
      };

      SDLUtil.showLoadingBar(true);
      await axios({
        url: path,
        responseType: 'arraybuffer',
        method: 'get',
      })
        .then(response => {
          try {
            if (response.headers['content-type'] === 'application/json') {
              response.data = JSON.parse(String.fromCharCode.apply(null, Array.from(new Uint8Array(response.data))));
              SDLUtil.showLoadingBar(false);
              SDLUtil.errorAlert(response);
              return;
            }
            const blob = new Blob([response.data], { type: response.headers['content-type'] });

            let fileName = '';
            if (response.headers['content-disposition']) {
              fileName = getFileName(response.headers['content-disposition']);
            } else {
              // eslint-disable-next-line prefer-destructuring
              fileName = params.fileName;
            }

            fileName = decodeURI(fileName);

            if (window.navigator.msSaveOrOpenBlob) {
              // IE 10+
              window.navigator.msSaveOrOpenBlob(blob, fileName);
            } else {
              // not IE
              const link = document.createElement('a');
              link.href = window.URL.createObjectURL(blob);
              link.target = '_self';
              if (fileName) link.download = fileName;
              link.click();
            }
            SDLUtil.showLoadingBar(false);
            // return response.data;
          } catch (e) {
            SDLUtil.showLoadingBar(false);
            console.error(e);
          }
        })
        .catch(response => {
          SDLUtil.showLoadingBar(false);
          SDLUtil.alert(response.data);
        });
    },
    /**
     * param:
     * return:
     * exception:
     * Description : ignore drag event
     */
    ignoreDrag(e) {
      this.dragging = true;
      e.stopPropagation();
      e.preventDefault();
    },
    /**
     * param:
     * return:
     * exception:
     * Description : if you select a file
     */
    onChange(e) {
      e.stopPropagation();
      e.preventDefault();
      // e.dataTransfer.dropEffect = 'copy';
      this.successMsg = '';
      this.errorMsg = '';
      // e.dataTransfer.effectAllowed = 'copy';
      let files = e.target.files;
      if (files.length === 0) {
        files = e.dataTransfer.files || [];
      }
      const agent = window.navigator.userAgent.toLowerCase();
      if ((window.navigator.appName === 'Netscape' && agent.indexOf('trident') !== -1) || agent.indexOf('msie') !== -1) {
        try {
          if (files.length === 0) {
            files = e.dataTransfer.files || [];
          }
        } catch (err) {
          return;
        }
      }

      this.dragging = false;

      let tmpTotalFileSize = this.fileSizes;
      let tmpTotalFileCnt = this.itemsAdded;

      const getFileExt = fileName => fileName.substring(fileName.lastIndexOf('.') + 1, fileName.length).toLowerCase();

      const extList = this.useExtList.split('|');

      let dupChk = false;
      let extChkTxt = '';
      Object.keys(files).forEach(x => {
        tmpTotalFileSize += files[x].size;
        tmpTotalFileCnt += 1;
        if (this.itemsNames.filter(item => item.fileName === files[x].name).length > 0) {
          dupChk = true;
          return;
        }
        if (!extList.includes(getFileExt(files[x].name))) {
          extChkTxt = getFileExt(files[x].name);
        }
      });
      if (dupChk) {
        document.querySelector(`#fileAttach${this.downloadType}`).value = '';
        SDLUtil.alert('sdl.common.message.duplicated');
        return;
      }
      if (extChkTxt) {
        document.querySelector(`#fileAttach${this.downloadType}`).value = '';
        SDLUtil.alert('sdl.common.message.extensionCannotBeAttached', [`${extChkTxt}`]);
        return;
      }

      if (this.maxTotalSize < tmpTotalFileSize) {
        document.querySelector(`#fileAttach${this.downloadType}`).value = '';
        SDLUtil.alert('sdl.common.message.attachedFileExceededMaxCapacity');
        return;
      }
      if (this.maxItems < tmpTotalFileCnt) {
        document.querySelector(`#fileAttach${this.downloadType}`).value = '';
        SDLUtil.alert('sdl.common.message.attachedFileExceededMaxNumber');
        return;
      }

      Object.keys(files).forEach(x => {
        this.items = files[x] || files[x];
        const item = {
          fileName: files[x].name,
          fileSize: files[x].size,
          file: this.items,
          // selected: false,
        };
        this.itemsNames.push(item);
      });

      document.querySelector(`#fileAttach${this.downloadType}`).value = '';
      this.calViewVariable();
    },

    /**
     * param: docId -> String
     * return:
     * exception:
     * Description : component initialization
     */
    init(docId = '') {
      this.docId = docId;
      this.items = '';
      this.itemsAdded = '';
      this.itemsNames = [];
      this.itemsTotalSize = 0;
      this.dragging = false;
      if (typeof this.fileList !== 'undefined') {
        this.fileList.forEach(file => {
          this.itemsNames.push(file);
        });
        this.calViewVariable();
      }
    },
    /**
     * param:
     * return:
     * exception:
     * Description : capacity calculation
     */
    calViewVariable() {
      this.fileSizes = 0;
      for (let i = 0; i < this.itemsNames.length; i += 1) {
        this.fileSizes += this.itemsNames[i].fileSize;
      }
      this.itemsAdded = this.itemsNames.length;
      this.itemsTotalSize = this.bytesToSize(this.fileSizes);
    },
    /**
     * param:
     * return:
     * exception:
     * Description : delete selected file
     */
    removeItem(item) {
      this.itemsNames.splice(this.itemsNames.indexOf(item), 1);
      this.calViewVariable();
    },
    /**
     * param:
     * return:
     * exception:
     * Description : upload selected files
     */
    onUpload(docId) {
      let { uploadURL } = this;
      if (docId) {
        // In case of approval, receive the approval ID and upload it.
        uploadURL = `${SDLUtil.API_URL}/approval/approval-documents/${docId}/attachments`;
      }
      this.isLoaderVisible = true;
      this.formData = new FormData();

      this.formData.append('downloadType', this.downloadType);
      let addFileCnt = 0;
      for (let i = 0; i < this.itemsNames.length; i += 1) {
        if (typeof this.itemsNames[i].file !== 'undefined') {
          this.formData.append('files', this.itemsNames[i].file);
          addFileCnt += 1;
          // this.itemsNames.splice(i);
        }
      }
      if (addFileCnt > 0) {
        if (this.method === 'put' || this.method === 'post') {
          axios({
            method: this.method,
            url: uploadURL,
            data: this.formData,
          })
            .then(response => {
              this.isLoaderVisible = false;
              // Show success message
              const fileList = response.data;
              for (let i = 0; i < fileList.length; i += 1) {
                const obj = this.itemsNames.filter(item => item.fileName === fileList[i].fileName)[0];
                this.itemsNames.splice(this.itemsNames.indexOf(obj), 1, Object.assign(obj, fileList[i]));
              }
              this.isLoaderVisible = false;
              this.reIndexOrderIdx();
              this.$emit('complete', this.downloadType, this.itemsNames);
            })
            .catch(error => {
              this.isLoaderVisible = false;
              if (error.isAxiosError) SDLUtil.alert('sdl.common.message.attachedFileChanged');
              else SDLUtil.errorAlert(error);
              SDLUtil.showLoadingBar(false);
            });
        } else {
          if (this.showHttpMessages) this.errorMsg = this.httpMethodErrorMessage;
          this.isLoaderVisible = false;
          SDLUtil.alert({ msg: this.httpMethodErrorMessage });
        }
      } else {
        this.isLoaderVisible = false;
        this.reIndexOrderIdx();
        this.$emit('complete', this.downloadType, this.itemsNames);
      }
    },
    /**
     * param:
     * return:
     * exception:
     * Description : reindex upload file ordering
     */
    reIndexOrderIdx() {
      for (let i = 0; i < this.itemsNames.length; i += 1) {
        this.itemsNames[i].orderIdx = i;
        this.itemsNames.splice(i, 1, this.itemsNames[i]);
      }
    },
    /**
     * param:
     * return:
     * exception:
     * Description : watch drop event(IE)
     */
    watchDrop() {
      // console.log('watchDrop');
      const agent = window.navigator.userAgent.toLowerCase();
      if ((window.navigator.appName === 'Netscape' && agent.indexOf('trident') !== -1) || agent.indexOf('msie') !== -1) {
        let el = `#fileAttach${this.downloadType}`;
        el = document.querySelector(el) || this.$root.$el.querySelector(el);
        this.dropElement = el;

        if (this.dropElement) {
          document.addEventListener('dragenter', this.ignoreDrag, false);
          document.addEventListener('dragover', this.ignoreDrag, false);
          document.addEventListener('drop', this.ignoreDrag, false);
          this.dropElement.addEventListener('dragover', this.ignoreDrag, false);
          this.dropElement.addEventListener('drop', this.onChange, false);
        }
      }
    },
  },
  watch: {
    dragging: {
      handler() {
        this.watchDrop();
      },
      deep: true,
    },
    fileList: {
      handler() {
        this.init(this.docId);
      },
      deep: true,
    },
  },
};
</script>

<style>
.addfile {
  position: absolute;
  cursor: pointer;
  top: 0;
  right: 0;
  bottom: 0;
  left: 0;
  width: 100%;
  height: 100%;
  opacity: 0;
}

/* Loader */
.loaderArea {
  min-height: 150px;
}

.ui--file-list .loader {
  position: absolute;
  top: 0;
  bottom: 0;
  width: 100%;
  height: 100%;
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  background-color: #fff;
  opacity: 0.9;
}

.ui--file-list .loaderImg {
  border: 9px solid #eee;
  border-top: 9px solid #00adce;
  border-radius: 50%;
  width: 70px;
  height: 70px;
  animation: spin 1s linear infinite;
}

@keyframes spin {
  0% {
    transform: rotate(0deg);
  }
  100% {
    transform: rotate(360deg);
  }
}

/* End Loader */
</style>
