<template>
  <div class="hn-viewer" ref="viewer">
    <template v-if="$slots.default">
      <slot></slot>
    </template>
    <template v-else>
      <div class="hn-viewer__item" v-for="(item, index) in imageList" :key="index">
        <img
          v-if="(item instanceof Object)"
          :src="item.picUrl || item.src || item.url"
          :alt="item.title || item.picContent"
          :index="index">
        <img v-else :src="item" :index="index">
      </div>
    </template>
  </div>
</template>

<script>
import Viewer from '../viewerjs/viewer.js';
import { utils, fillProps, toHighLightedItems } from './util';

const TOOLBAR_OPTIONS = {
  zoomIn: 1,
  zoomOut: 1,
  oneToOne: 1,
  reset: 1,
  prev: 1,
  play: 1,
  next: 1,
  rotateLeft: 1,
  rotateRight: 1,
  flipHorizontal: 1,
  flipVertical: 1
};
const TOOLBAR_OPTIONS_SHOWMARK = {
  zoomIn: 1,
  zoomOut: 1,
  oneToOne: 1,
  reset: 0,
  prev: 1,
  play: 0,
  next: 1,
  rotateLeft: 0,
  rotateRight: 0,
  flipHorizontal: 0,
  flipVertical: 0
};

export default {
  name: 'HnViewer',
  props: {
    images: {
      type: [String, Object, Array]
    },
    showMark: { // 是否需要高亮标注
      type: Boolean,
      default: false
    },
    markBackgroundColor: {
      type: String,
      default: '#0ff'
    },
    visible: {
      type: Boolean,
      default: false
    },
    className: {
      type: String,
      default: ''
    },
    inline: { // 是否启用内联模式
      type: Boolean,
      default: false
    },
    button: { // 显示右上角的按钮
      type: Boolean,
      default: true
    },
    navbar: { // 导航栏的可见性 0/false：隐藏，1/true：显示，2：屏幕宽度大于768像素时显示，3：屏幕宽度大于992像素时显示，4：屏幕宽度大于1200像素时显示
      type: [Boolean, Number],
      default: 1
    },
    title: { // 标题的可见性 0/false：隐藏，1/true：显示，2：屏幕宽度大于768像素时显示，3：屏幕宽度大于992像素时显示，4：屏幕宽度大于1200像素时显示, Function: 自定义标题内容，[Number, Function]： Function(image, imageData)
      type: [Boolean, Number, Function, Array],
      default: 1
    },
    toolbarType: { // 工具栏的可见性 0/false：隐藏，1/true：显示，2：屏幕宽度大于768像素时显示，3：屏幕宽度大于992像素时显示，4：屏幕宽度大于1200像素时显示
      type: [Boolean, Number],
      default: 1
    },
    toolbarOptions: { // 工具栏按钮的可见性和布局
      type: Object
    },
    tooltipShow: { // 放大或缩小时图像比率（百分比）提示
      type: Boolean,
      default: true
    },
    movable: { // 是否可以移动图像
      type: Boolean,
      default: true
    },
    zoomable: { // 是否可以放大缩小图像
      type: Boolean,
      default: true
    },
    rotatable: { // 是否可以旋转图像
      type: Boolean,
      default: true
    },
    scalable: { // 是否可以翻转图像
      type: Boolean,
      default: true
    },
    transition: { // 是否启用transition
      type: Boolean,
      default: true
    },
    fullscreen: { // 是否可以查看原始图片大小
      type: Boolean,
      default: true
    },
    keyboard: { // 是否启用键盘
      type: Boolean,
      default: true
    },
    backdrop: { // 是否启用遮罩，static不可点击遮罩关闭
      type: [Boolean, String],
      default: true
    },
    loading: { // 加载图像时是否显示加载动画
      type: Boolean,
      default: true
    },
    loop: { // 是否启用循环
      type: Boolean,
      default: true
    },
    interval: { // 自动循环播放时间间隔
      type: Number,
      default: 5000
    },
    minWidth: { // 最小宽度
      type: Number,
      default: 200
    },
    minHeight: { // 最小高度
      type: Number,
      default: 100
    },
    zoomRatio: { // 鼠标缩放图像时的比率
      type: Number,
      default: 0.1
    },
    minZoomRatio: { // 最小缩放图像比率
      type: Number,
      default: 0.01
    },
    maxZoomRatio: { // 最大缩放图像比率
      type: Number,
      default: 100
    },
    zIndex: { // z-index值
      type: Number,
      default: 9999
    },
    zIndexInline: { // 内联模式z-index值
      type: Number,
      default: 100
    },
    url: { // 占位图片
      type: [String, Function],
      default: 'src'
    },
    container: { // 插入位置
      type: [Object, String],
      default: 'body'
    },
    filter: { // 过滤器
      type: Function,
      default: null
    },
    toggleOnDoubleClick: { // 双击功能
      type: Boolean,
      default: true
    },
    value: {
      type: Number,
      default: 0
    }
  },
  data() {
    return {
      list: [],
      index: 0,
      toolbar: '',
      closed: false
    };
  },
  computed: {
    imageList() {
      let list;
      if (this.images instanceof Array) {
        list = this.images;
      } else {
        list = [this.images];
      }
      return this.showMark
        ? toHighLightedItems(list)
        : list;
    }
  },
  watch: {
    images(val) {
      this.$nextTick(this.update);
    },
    visible(val) {
      const w = window;
      if (val) {
        this.show();
        // 若不存在，添加
        if (!w.hnViewers) {
          w.hnViewers = {
            [String(this._uid)]: this
          };
        }
        const { hnViewers } = w;
        Object.keys(hnViewers).map(key => {
          if (String(this._uid) !== key) {
            w.hnViewers[String(this._uid)] = this;
            if (hnViewers[key].viewer) {
              hnViewers[key].viewer.destroy();
            }
            delete w.hnViewers[key].viewer;
          }
        });
      } else {
        this.hide();
      }
      if (this.closed === val) return;
      this.closed = val;
    },
    index() {
      this.$emit('input', this.index);
    },
    value(val) {
      if (!val && val !== 0) return;
      this.view(val);
    }
  },
  created() {
    this.$_optionsInit();
  },
  mounted() {
    this.$_viewerInit();
    this.$refs.viewer.addEventListener('click', this.handleClick);
  },
  beforeDestroy() {
    const w = window;
    this.$refs.viewer.removeEventListener('click', this.handleClick);
    if (w.hnViewers) {
      delete w.hnViewers;
    }
    if (this.viewer) {
      this.destroy();
    }
  },
  methods: {
    handleClick(e) {
      const w = window;
      if (e.target.tagName.toLowerCase() === 'img' && w.hnViewers && w.hnViewers[this._uid]) {
        const newIndex = e.target.attributes.index.value;
        const { viewer, $_optionsInit, $_viewerInit, index, visible } = w.hnViewers[this._uid];
        if (!viewer) {
          $_optionsInit();
          this.$nextTick(() => {
            $_viewerInit();
            if (!visible) {
              this.show();
            }
            if (Number(index) !== Number(newIndex)) {
              this.view(Number(newIndex));
            }
            w.hnViewers[this._uid] = this;
          });
        }
      }
    },
    $_optionsInit() {
      this.index = this.value;
      if (this.toolbarOptions) {
        this.toolbar = {
          ...TOOLBAR_OPTIONS,
          ...this.toolbarOptions
        };
      } else {
        if (this.showMark) {
          this.toolbar = {
            ...TOOLBAR_OPTIONS_SHOWMARK,
            ...this.toolbarOptions
          };
        } else {
          this.toolbar = this.toolbarType;
        }
      }
      this.closed = this.visible;
    },
    $_viewerInit() {
      this.viewer = new Viewer(this.$refs.viewer, {
        showMark: this.showMark,
        initialViewIndex: this.index,
        inline: this.inline,
        button: this.button,
        navbar: this.navbar,
        title: this.title,
        className: this.className,
        toolbar: this.toolbar,
        tooltip: this.tooltipShow,
        movable: this.movable,
        zoomable: this.zoomable,
        rotatable: this.showMark ? false : this.rotatable,
        scalable: this.scalable,
        transition: this.showMark ? false : this.transition,
        fullscreen: this.showMark ? false : this.fullscreen,
        keyboard: this.keyboard,
        backdrop: this.backdrop,
        loading: this.loading,
        loop: this.loop,
        interval: this.interval,
        minWidth: this.minWidth,
        minHeight: this.minHeight,
        zoomRatio: this.zoomRatio,
        minZoomRatio: this.minZoomRatio,
        maxZoomRatio: this.maxZoomRatio,
        zIndex: this.zIndex,
        zIndexInline: this.zIndexInline,
        url: this.url,
        container: this.container,
        filter: this.filter,
        toggleOnDblclick: this.toggleOnDoubleClick,
        ready: (event) => { // 初始化ready事件
          this.$emit('ready', event);
        },
        show: (event) => { // 显示事件-开始
          this.$emit('show', event);
        },
        shown: (event) => { // 显示事件-结束
          this.closed = true;
          this.$emit('shown', event);
          this.$emit('update:visible', true);
        },
        hide: (event) => { // 隐藏事件-开始
          this.$emit('hide', event);
        },
        hidden: (event) => { // 隐藏事件-结束
          this.closed = false;
          this.$emit('hidden', event);
          this.$emit('update:visible', false);
        },
        view: (event) => { // 切换事件-开始
          this.$emit('view', event);
        },
        viewed: (event) => { // 切换事件-结束
          this.index = event.detail.index;
          this.$emit('viewed', event);
        },
        zoom: (event) => { // 缩放事件-开始
          this.$emit('zoom', event);
        },
        zoomed: (event) => { // 缩放事件-结束
          this.$emit('zoomed', event);
        },
        epcImgMark: () => {
          this.epcImgMark();
        }
      });
      if (this.visible) {
        this.show();
      }
    },
    epcImgMark() {
      // 对当前有位置信息的epc图片打点
      this.$nextTick(() => {
        if (this.showMark && this.visible) {
          const handledImages = fillProps(this.images);
          const oCurrImg = handledImages[this.index];
          if (!oCurrImg.hasMarkPos) return;
          const currZoomLevel = this.viewer.imageData.ratio;
          utils.updateRect(this.viewer.canvas.firstChild.firstChild, {
            ...utils.calculateRect(oCurrImg.markPos, currZoomLevel),
            background: this.markBackgroundColor
          });
        }
      });
    },
    show(immediate) {
      this.viewer.show(immediate);
    },
    hide(immediate) {
      this.viewer.hide(immediate);
    },
    view(index) {
      this.viewer.view(index);
    },
    prev(loop) {
      this.viewer.prev(loop);
    },
    next(loop) {
      this.viewer.next(loop);
    },
    move(offsetX, offsetY) {
      this.viewer.move(offsetX, offsetY);
    },
    moveTo(x, y) {
      this.viewer.moveTo(x, x);
    },
    zoom(ratio, hasTooltip) {
      this.viewer.zoom(ratio, hasTooltip);
    },
    zoomTo(ratio, hasTooltip) {
      this.viewer.zoomTo(ratio, hasTooltip);
    },
    rotate(degree) {
      this.viewer.rotate(degree);
    },
    rotateTo(degree) {
      this.viewer.rotateTo(degree);
    },
    scale(scaleX, scaleY) {
      this.viewer.scale(scaleX, scaleY);
    },
    scaleX(scaleX) {
      this.viewer.scaleX(scaleX);
    },
    scaleY(scaleY) {
      this.viewer.scaleY(scaleY);
    },
    play(fullscreen) {
      this.viewer.play(fullscreen);
    },
    stop() {
      this.viewer.stop();
    },
    full() {
      this.viewer.full();
    },
    exit() {
      this.viewer.exit();
    },
    tooltip() {
      this.viewer.tooltip();
    },
    toggle() {
      this.viewer.toggle();
    },
    reset() {
      this.viewer.reset();
    },
    update() {
      this.viewer && this.viewer.update();
    },
    destroy() {
      this.viewer.destroy();
      utils.destroyRect();
    }
  }
};
</script>
