<template>
  <div :class="className">
    <input
      ref="inputFile"
      type="file"
      :name="inputName"
      :class="name+'__input'"
      :multiple="multiple"
      :accept="accept"
      @change="onInputFileChange"
    >
    <div
      v-if="!limit||curFileList.length < limit"
      :class="name+'__select-container'"
      @click="onSelectFile"
      @drop="onDrop"
      @dragover.prevent
    >
      <slot />
    </div>
    <div :class="name+'__file-list'">
      <div
        :class="name+'__file-list__item'"
        v-for="(item,index) in curFileList"
        :key="item.key"
      >
        <div :class="name+'__file-list__item-info'">
          <i
            class="loading_icon"
            v-if="item.status===fileState.LOADING"
          />
          <Icon
            icon="circle_tick"
            v-if="item.status===fileState.ERROR"
          />
          <Icon
            icon="circle_clear"
            class="upload-error-icon"
            v-if="item.status===fileState.DONE"
          />
          <div
            :class="name+'__file-list__item-name'"
            :title="item.name"
          >
            {{ item.name }}
          </div>
        </div>
        <div
          :class="`${name}-uploaded-stats`"
          v-if="item.status===fileState.LOADING"
          hidden
        >
          <template>
            <div :class="`${name}-uploaded-progress`">
              <i
                :class="progressBarClass(item)"
                :style="progressBarStyle(item)"
              />
            </div>
          </template>
        </div>
        <Icon
          icon="circle_clear"
          @click="removeFile(item,index)"
        />
      </div>
    </div>
  </div>
</template>

<script>
import requestUtils from './request';
import Icon from '../icon';

const name = 'hive-upload';
const FILE_STATUS = {
  DONE: 'done',
  LOADING: 'loading',
  ERROR: 'error',
  REMOVE: 'remove'
};

export default {
  name,
  components: {
    Icon
  },
  props: {
    /**
     * 服务器接收到的文件名
     */
    inputName: {
      type: String,
      default: 'file'
    },
    /**
     * 接收文件的服务器地址
     */
    url: {
      type: String
    },
    /**
     * 限制上传的文件类型
     */
    accept: {
      type: String
    },
    /**
     * 限制上传文件数
     */
    limit: {
      type: Number,
      default: 0
    },
    /**
     * 使用拖拽模式
     */
    dragFlag: {
      type: Boolean,
      default: false
    },
    /**
     * 是否多选
     */
    multiple: {
      type: Boolean,
      default: false
    },
    /**
     * 设置请求头信息
     */
    headers: {
      type: Object,
      default: function() {
        return {};
      }
    },
    /**
     * 设置请求的其他参数
     */
    formData: {
      type: Object,
      default: function() {
        return {};
      }
    },
    /**
     * 文件列表状态发生改变时触发
     */
    onChange: Function,
    /**
     * 上传出错时触发，回调方法
     */
    onUploadError: Function,
    /**
     * 上传进度发生改变时触发,回调方法
     */
    onUploadProgress: Function,
    /**
     * 自定义上传方法
     */
    request: {
      type: Function
    },
    /**
     * 文件列表
     */
    fileList: {
      type: Array,
      default: function() {
        return [];
      }
    }
  },
  data() {
    return {
      curFileList: this.fileList,
      uploadList: [],
      inputProps: {
        type: 'file',
        className: `${name}__input`,
        onChange: this.onChange
      },
      fileState: FILE_STATUS
    };
  },
  computed: {
    name() {
      return name;
    },
    className() {
      return [
        `${name}`,
        {
          [`${name}--drag`]: this.dragFlag
        }
      ];
    }
  },
  watch: {
    fileList(value) {
      this.curFileList = value;
    }
  },
  mounted() {
    this.loadRequest();
  },
  methods: {
    onInputFileChange(e) {
      this.addFiles(e.target.files);
      e.target.value = '';
    },
    progressBarStyle(item) {
      return {
        width: `${item.progress}%`
      };
    },
    progressBarClass() {
      return [`${name}-uploaded-bar`];
    },
    getFileItemStatusIcon(item) {
      switch (item.status) {
        case FILE_STATUS.LOADING:
          return '<i class="loading_icon" />';
        case FILE_STATUS.DONE:
          return '<Icon icon="circle_tick" />';
        case FILE_STATUS.ERROR:
          return '<Icon icon="circle_clear" class="upload-error-icon" />';
        default:
      }
      return '';
    },
    onSelectFile(e) {
      this.$refs.inputFile.click();
    },
    onDragOver(e) {
      e.stopPropagation();
      e.preventDefault();
    },
    onDrop(e) {
      e.stopPropagation();
      e.preventDefault();
      this.addFiles(e.dataTransfer.files);
    },
    onFileChange(e) {
      this.onChange && this.onChange(e);
    },
    addFiles(files) {
      var that = this;
      const {
        limit,
        inputName,
        url,
        headers,
        onChange,
        formData,
        request,
        accept
      } = this.$props;
      const fileLength = that.curFileList.length;
      const newFiles = accept ? [] : files;
      if (accept) {
        const acceptList = accept.split(',');
        for (let i = 0; i < files.length; i++) {
          const file = files[i];
          for (const type of acceptList) {
            const reg = new RegExp(
              `${type.replace('.', '\\.').replace('*', '.+')}$`
            );
            if (reg.test(file.type) || reg.test(file.name)) {
              newFiles.push(file);
              break;
            }
          }
        }
      }
      if (newFiles.length === 0) {
        return;
      }

      for (let i = 0; i < newFiles.length; i++) {
        if (limit && fileLength + i + 1 > limit) {
          break;
        }
        const file = newFiles[i];
        const key =
            that.curFileList.length > 0
              ? Math.max.apply(null, that.curFileList.map(item => item.key)) + 1
              : 1;
        const fileObj = {
          key,
          name: file.name,
          status: FILE_STATUS.LOADING,
          file
        };
        const requestProps = {
          name: inputName,
          url,
          headers,
          formData,
          file,
          onStart: this.onStart,
          onError: this.onError,
          onSuccess: this.onSuccess,
          onProgress: this.onProgress
        };
        const xhr = request
          ? request(requestProps)
          : requestUtils(requestProps);
        that.uploadList.push({ key, xhr });
        that.curFileList.push(fileObj);
      }

      onChange && onChange(that.curFileList);
    },
    loadRequest() {
      if (this.curFileList.length === 0) {
        return;
      }
      const { inputName, url, headers, formData, request } = this.$props;

      for (const fileObj of this.curFileList) {
        const { key, status, file } = fileObj;
        if (
          status !== FILE_STATUS.LOADING ||
          this.uploadList.some(item => item.key === key)
        ) {
          return;
        }
        const requestProps = {
          name: inputName,
          url,
          headers,
          formData,
          file,
          onStart: this.onStart,
          onProgress: this.onProgress,
          onError: this.onError,
          onSuccess: this.onSuccess
        };

        const xhr = request
          ? request(requestProps)
          : requestUtils(requestProps);

        this.uploadList.push({ key }); // xhr
      }
    },
    removeFile(file, idx) {
      this.curFileList.splice(idx, 1);

      if (this.uploadList[index].xhr) {
        this.uploadList[index].xhr.abort();
      }

      this.uploadList.splice(index, 1);
      const { onRemove } = this.$props;
      const { key } = file;
      const fileList = this.fileList;
      if (onRemove && !onRemove(file)) {
        return;
      }

      this.onChange(
        { ...file, status: FILE_STATUS.REMOVE },
        fileList.filter(item => item.key !== key)
      );

      const index = this.uploadList.findIndex(item => item.key === key);
      if (index !== -1) {
        if (this.uploadList[index].xhr) {
          this.uploadList[index].xhr.abort();
        }

        this.uploadList.splice(index, 1);
      }
      this.onChange && this.onChange(this.curFileList);
    },
    onStart(file) {
      this.setFileStatus(file, FILE_STATUS.LOADING);
    },
    onError(file, response) {
      this.setFileStatus(file, FILE_STATUS.ERROR, { response });
      this.onUploadError && this.onUploadError(file, response);
    },
    onSuccess(file, response) {
      this.setFileStatus(file, FILE_STATUS.DONE, { response });
    },
    onProgress(file, response) {
      const progress = response.loaded / response.total;
      this.setFileStatus(file, FILE_STATUS.LOADING, { progress });
      this.onUploadProgress && this.onUploadProgress(file, response);
    },
    setFileStatus(file, status, otherParams) {
      var index = this.curFileList.findIndex(item => item.file === file);
      if (index === -1) {
        return;
      }
      const item = { ...this.curFileList[index], status, ...otherParams };
      this.curFileList.splice(index, 1, item);
      this.onChange && this.onChange(this.curFileList);
    }
  }
};
</script>
