import { defineComponent, ref, computed, reactive, inject, watch, onUnmounted, nextTick, resolveComponent, openBlock, createElementBlock, normalizeClass, normalizeStyle, renderSlot, createElementVNode, createVNode, mergeProps, createBlock, createCommentVNode } from 'vue';
import { useThrottleFn, useEventListener } from '@vueuse/core';
import { isString } from 'lodash-es';
import getPrefixCls from '../_util/getPrefixCls';
import { PictureOutlined, PictureFailOutlined } from '../icon';
import { isHtmlElement, getScrollContainer, isInContainer } from '../_util/dom';
import { pxfy, noop, noopInNoop } from '../_util/utils';
import { ERROR_EVENT, LOAD_EVENT, CLOSE_EVENT } from '../_util/constants';
import download from '../_util/download';
import { useTheme } from '../_theme/useTheme';
import { PREVIEW_PROVIDE_KEY } from './props';
import Preview from './preview.js';

const prefixCls = getPrefixCls('img');
let curIndex = 0;
let prevOverflow = '';
const imageProps = {
  src: {
    type: String,
    default: ''
  },
  name: String,
  preview: {
    type: Boolean,
    default: false
  },
  fit: {
    type: String
  },
  lazy: {
    type: Boolean,
    default: false
  },
  hideOnClickModal: {
    type: Boolean,
    default: false
  },
  scrollContainer: [String, Object],
  download: {
    type: Boolean,
    default: false
  },
  previewContainer: {
    type: Function
  },
  height: [String, Number],
  width: [String, Number]
};
var script = defineComponent({
  name: 'FImage',
  componentName: 'FImage',
  components: {
    Preview,
    PictureOutlined,
    PictureFailOutlined
  },
  props: imageProps,
  emits: [ERROR_EVENT, LOAD_EVENT, CLOSE_EVENT],
  setup(props, _ref) {
    let {
      attrs,
      emit
    } = _ref;
    useTheme();
    const loading = ref(true);
    const isLoadError = ref(false);
    const container = ref(null);
    const isShowPreview = ref(false);
    const currentId = ref(curIndex++);
    const imgAttrs = computed(() => {
      const {
        crossorigin = undefined,
        decoding = 'auto',
        alt = undefined,
        sizes = undefined,
        srcset = undefined,
        usemap = undefined
      } = attrs;
      return {
        crossorigin,
        decoding,
        alt,
        sizes,
        srcset,
        usemap
      };
    });
    const style = computed(() => {
      const {
        width,
        height
      } = props;
      return {
        width: pxfy(width),
        height: pxfy(height)
      };
    });
    const imageSize = reactive({
      height: 0,
      width: 0
    });
    const {
      isGroup,
      setShowPreview,
      setCurrent,
      registerImage
    } = inject(PREVIEW_PROVIDE_KEY, {
      setShowPreview: noop,
      isGroup: ref(false),
      setCurrent: noop,
      registerImage: noopInNoop
    });
    const canPreview = computed(() => props.preview && !isLoadError.value);
    const canGroupPreview = computed(() => isGroup.value && !isLoadError.value);
    const _scrollContainer = computed(() => {
      let dom;
      const _container = props.scrollContainer;
      if (isString(_container) && _container !== '') {
        dom = document.querySelector(_container);
      }
      if (isHtmlElement(_container)) {
        dom = _container;
      } else if (container.value) {
        dom = getScrollContainer(container.value);
      }
      return dom;
    });
    const imageStyle = computed(() => {
      const {
        fit
      } = props;
      const styleObj = {
        objectFit: 'fill',
        cursor: ''
      };
      if (fit) {
        styleObj.objectFit = fit;
      }
      if (props.download || canPreview.value || canGroupPreview.value) {
        styleObj.cursor = 'pointer';
      }
      return styleObj;
    });
    const handleLoaded = (e, img) => {
      imageSize.width = img.width;
      imageSize.height = img.height;
      loading.value = false;
      isLoadError.value = false;
      emit(LOAD_EVENT, e);
    };
    const handleError = e => {
      loading.value = false;
      isLoadError.value = true;
      emit(ERROR_EVENT, e);
    };
    let currentImageId = 0;
    const loadImage = () => {
      // loading 为true 才会加载图片
      if (!loading.value) {
        return;
      }
      const img = new Image();
      const imageId = ++currentImageId;
      img.addEventListener('load', e => {
        // 检查 imageId 是否与 currentImageId 相同
        if (imageId !== currentImageId) {
          return;
        }
        handleLoaded(e, img);
      });
      img.addEventListener('error', e => {
        // 检查 imageId 是否与 currentImageId 相同
        if (imageId !== currentImageId) {
          return;
        }
        handleError(e);
      });

      // 赋值开始加载图片src
      img.src = props.src;
    };
    const lazyLoadHandler = useThrottleFn(() => {
      // load image until image enter the container
      if (isInContainer(container.value, _scrollContainer.value)) {
        loadImage();
      }
    }, 200);
    let clearScrollListener = noop;
    async function addLazyLoadListener() {
      await nextTick();
      if (clearScrollListener) {
        clearScrollListener();
      }
      if (_scrollContainer.value) {
        clearScrollListener = useEventListener(_scrollContainer, 'scroll', lazyLoadHandler);
      }
      lazyLoadHandler();
    }
    function clickHandler() {
      if (canGroupPreview.value) {
        setCurrent(currentId.value);
        setShowPreview(true);
      } else if (canPreview.value) {
        // prevent body scroll
        prevOverflow = document.body.style.overflow;
        document.body.style.overflow = 'hidden';
        isShowPreview.value = true;
      } else if (props.download) {
        // 下载
        download({
          href: props.src,
          name: props.name
        });
      }
    }
    function closeViewer() {
      document.body.style.overflow = prevOverflow;
      isShowPreview.value = false;
      emit(CLOSE_EVENT);
    }
    watch(() => props.src, _src => {
      if (_src) {
        // 将 loading 状态设置为 true
        loading.value = true;
        // 重置 isLoadError 状态
        isLoadError.value = false;
        if (props.lazy) {
          addLazyLoadListener();
        } else {
          loadImage();
        }
      }
    }, {
      immediate: true
    });
    let unRegister = noop;
    watch([() => props.src, () => props.name, () => props.download, canGroupPreview], () => {
      unRegister();
      if (canGroupPreview.value) {
        unRegister = registerImage({
          id: currentId.value,
          url: props.src,
          name: props.name,
          size: imageSize,
          download: props.download
        });
      }
    }, {
      immediate: true
    });
    onUnmounted(() => {
      if (unRegister) {
        unRegister();
      }
      if (clearScrollListener) {
        clearScrollListener();
      }
    });
    return {
      imgAttrs,
      imageStyle,
      isShowPreview,
      clickHandler,
      closeViewer,
      container,
      prefixCls,
      isLoadError,
      loading,
      imageSize,
      style
    };
  }
});

const _hoisted_1 = ["src"];
function render(_ctx, _cache, $props, $setup, $data, $options) {
  const _component_PictureOutlined = resolveComponent("PictureOutlined");
  const _component_PictureFailOutlined = resolveComponent("PictureFailOutlined");
  const _component_Preview = resolveComponent("Preview");
  return openBlock(), createElementBlock("div", {
    ref: "container",
    class: normalizeClass(_ctx.prefixCls),
    style: normalizeStyle(_ctx.style)
  }, [_ctx.loading ? renderSlot(_ctx.$slots, "placeholder", {
    key: 0
  }, () => [createElementVNode("div", {
    class: normalizeClass(`${_ctx.prefixCls}__placeholder`)
  }, [createVNode(_component_PictureOutlined), _cache[1] || (_cache[1] = createElementVNode("span", null, "加载中", -1 /* HOISTED */))], 2 /* CLASS */)]) : _ctx.isLoadError ? renderSlot(_ctx.$slots, "error", {
    key: 1
  }, () => [createElementVNode("div", {
    class: normalizeClass(`${_ctx.prefixCls}__error`)
  }, [createVNode(_component_PictureFailOutlined), _cache[2] || (_cache[2] = createElementVNode("span", null, "加载失败", -1 /* HOISTED */))], 2 /* CLASS */)]) : (openBlock(), createElementBlock("div", {
    key: 2,
    class: normalizeClass(`${_ctx.prefixCls}__inner`),
    onClick: _cache[0] || (_cache[0] = function () {
      return _ctx.clickHandler && _ctx.clickHandler(...arguments);
    })
  }, [renderSlot(_ctx.$slots, "default", {}, () => [createElementVNode("img", mergeProps({
    src: _ctx.src,
    class: `${_ctx.prefixCls}__inner-image`,
    style: _ctx.imageStyle
  }, _ctx.imgAttrs), null, 16 /* FULL_PROPS */, _hoisted_1)])], 2 /* CLASS */)), _ctx.isShowPreview ? (openBlock(), createBlock(_component_Preview, {
    key: 3,
    src: _ctx.src,
    name: _ctx.name,
    size: _ctx.imageSize,
    download: _ctx.download,
    "hide-on-click-modal": _ctx.hideOnClickModal,
    getContainer: _ctx.previewContainer,
    onClose: _ctx.closeViewer
  }, null, 8 /* PROPS */, ["src", "name", "size", "download", "hide-on-click-modal", "getContainer", "onClose"])) : createCommentVNode("v-if", true)], 6 /* CLASS, STYLE */);
}

script.render = render;
script.__file = "components/image/image.vue";

export { script as default, imageProps };
