import { createVNode as _createVNode } from "vue";
import _extends from "@babel/runtime/helpers/esm/extends";
import { computed, defineComponent, ref, toRef, TransitionGroup, watch, watchEffect, onMounted } from 'vue';
import '../grid';
import MasonryItem from './MasonryItem';
import { responsiveArray } from '../_util/responsiveObserve';
import useConfigInject from '../config-provider/hooks/useConfigInject';
import useBreakpoint from '../_util/hooks/useBreakpoint';
import useGutter from './hooks/useGutter';
import useDelay from './hooks/useDelay';
import usePositions from './hooks/usePositions';
import useRefs from './hooks/useRefs';
import isEqual from '../vc-util/isEqual';
import ResizeObserver from '../vc-resize-observer';
import { getTransitionGroupProps } from '../_util/transition';
import useStyle from './style';
export const masonryProps = () => ({
  prefixCls: String,
  className: String,
  rootClassName: String,
  style: Object,
  gutter: {
    type: [Number, Object, Array],
    default: undefined
  },
  items: Array,
  itemRender: Function,
  /** Number of columns in the masonry grid layout */
  columns: {
    type: [Number, Object],
    default: undefined
  },
  /** Trigger when item layout order changed */
  onLayoutChange: Function,
  fresh: Boolean
});
const Masonry = defineComponent({
  name: 'AMasonry',
  props: masonryProps(),
  slots: Object,
  setup(props, {
    slots
  }) {
    const {
      prefixCls,
      direction
    } = useConfigInject('masonry', props);
    const containerRef = ref();
    const {
      getRef,
      setRef
    } = useRefs();
    const [wrapSSR, hashId] = useStyle(prefixCls);
    // ======================= Item =======================
    const mergedItems = ref([]);
    watchEffect(() => {
      mergedItems.value = props.items;
    });
    // ==================== Breakpoint ====================
    const screens = useBreakpoint();
    const gutters = useGutter(toRef(props, 'gutter'), screens);
    // ====================== Layout ======================
    const columnCount = computed(() => {
      var _a;
      const {
        columns
      } = props;
      if (!columns) {
        return 3;
      }
      if (typeof columns === 'number') {
        return columns;
      }
      const matchingBreakpoint = responsiveArray.find(breakpoint => screens.value[breakpoint] && columns[breakpoint] !== undefined);
      if (matchingBreakpoint) {
        return columns[matchingBreakpoint];
      }
      return (_a = columns === null || columns === void 0 ? void 0 : columns.xs) !== null && _a !== void 0 ? _a : 1;
    });
    // ================== Items Position ==================
    const itemHeights = ref();
    const collectItemSize = useDelay(() => {
      const nextItemsHeight = mergedItems.value.map((item, index) => {
        var _a;
        const itemKey = (_a = item.key) !== null && _a !== void 0 ? _a : index;
        const itemEle = getRef(itemKey);
        const rect = itemEle === null || itemEle === void 0 ? void 0 : itemEle.getBoundingClientRect();
        return [itemKey, rect ? rect.height : 0, item.column];
      });
      itemHeights.value = isEqual(itemHeights.value, nextItemsHeight) ? itemHeights.value : nextItemsHeight;
    });
    const verticalGutter = computed(() => {
      const [horizontalGutter = 0, verticalGutter = horizontalGutter] = gutters.value;
      return verticalGutter;
    });
    const horizontalGutter = computed(() => {
      const [horizontalGutter = 0] = gutters.value;
      return horizontalGutter;
    });
    const positions = usePositions(itemHeights, columnCount, verticalGutter);
    const itemWithPositions = computed(() => mergedItems.value.map((item, index) => {
      var _a;
      const key = (_a = item.key) !== null && _a !== void 0 ? _a : index;
      return {
        item,
        itemIndex: index,
        itemKey: key,
        key,
        position: positions.value[0].get(key)
      };
    }));
    watch([mergedItems, columnCount], () => {
      collectItemSize();
    }, {
      immediate: true
    });
    const itemColumns = ref();
    function handleImgLoadOrError(e) {
      const img = e.target;
      // tips: only trigger once
      img.removeEventListener('load', handleImgLoadOrError);
      img.removeEventListener('error', handleImgLoadOrError);
      collectItemSize();
    }
    onMounted(() => {
      const images = containerRef.value.querySelectorAll('img');
      images.forEach(img => {
        img.addEventListener('load', handleImgLoadOrError);
        img.addEventListener('error', handleImgLoadOrError);
      });
    });
    watch(() => itemWithPositions.value, nextItemWithPositions => {
      if (props.onLayoutChange && nextItemWithPositions.every(({
        position
      }) => position)) {
        const nextItemColumns = nextItemWithPositions.map(({
          item,
          position
        }) => [item, position.column]);
        itemColumns.value = isEqual(itemColumns.value, nextItemColumns) ? itemColumns.value : nextItemColumns;
      }
    }, {
      flush: 'post',
      immediate: true
    });
    watch(() => itemColumns.value, nextItemColumns => {
      const {
        onLayoutChange,
        items
      } = props;
      if (onLayoutChange && items && items.length === nextItemColumns.length) {
        onLayoutChange(nextItemColumns.map(([item, column]) => _extends(_extends({}, item), {
          column
        })));
      }
    }, {
      flush: 'post',
      immediate: true
    });
    // ====================== Render ======================
    const transitionProps = getTransitionGroupProps(`${prefixCls.value}-item-fade`);
    return () => {
      const {
        rootClassName,
        className,
        itemRender = slots.itemRender,
        style = {}
      } = props;
      return wrapSSR(_createVNode(ResizeObserver, {
        "onResize": collectItemSize
      }, {
        default: () => [_createVNode("div", {
          "ref": containerRef,
          "class": [prefixCls.value, rootClassName, className, hashId.value, {
            [`${prefixCls.value}-rtl`]: direction.value === 'rtl'
          }],
          "style": _extends({
            height: `${positions.value[1]}px`
          }, style)
        }, [_createVNode(TransitionGroup, transitionProps, {
          default: () => [itemWithPositions.value.map(({
            position,
            itemIndex,
            key,
            item
          }) => {
            const {
              column: columnIndex = 0,
              top
            } = position || {};
            const itemStyle = {
              '--item-width': `calc((100% + ${horizontalGutter.value}px) / ${columnCount.value})`,
              insetInlineStart: `calc(var(--item-width) * ${columnIndex})`,
              width: `calc(var(--item-width) - ${horizontalGutter.value}px)`,
              top: `${top}px`,
              position: 'absolute'
            };
            return _createVNode(MasonryItem, {
              "prefixCls": prefixCls.value,
              "key": key,
              "item": item,
              "style": itemStyle,
              "ref": el => setRef(item.key, el === null || el === void 0 ? void 0 : el.itemRef),
              "index": itemIndex,
              "itemRender": itemRender,
              "column": columnIndex,
              "onResize": props.fresh ? collectItemSize : null
            }, null);
          })]
        })])]
      }));
    };
  }
});
export default Masonry;