import _objectSpread from "@babel/runtime/helpers/esm/objectSpread2";
import { createVNode as _createVNode } from "vue";
import _extends from "@babel/runtime/helpers/esm/extends";
import { computed, defineComponent, ref, watch } from 'vue';
import { arrayType, objectType, someType, withInstall } from '../_util/type';
import { initDefaultProps } from '../_util/props-util';
import { useToken } from '../theme/internal';
import { getPixelRatio, reRendering, toList } from './utils';
import useClips, { FontGap } from './useClips';
import useSingletonCache from './useSingletonCache';
import useRafDebounce from './useRafDebounce';
import useWatermark from './useWatermark';
import { useMutateObserver } from './useMutateObserver';
import { useProvideWatermark } from './context';
export const watermarkProps = () => ({
  zIndex: Number,
  rotate: Number,
  width: Number,
  height: Number,
  image: String,
  content: someType([String, Array]),
  font: objectType(),
  rootClassName: String,
  gap: arrayType(),
  offset: arrayType(),
  inherit: Boolean,
  onRemove: Function
});
function getSizeDiff(prev, next) {
  return prev.size === next.size ? prev : next;
}
const fixedStyle = {
  position: 'relative',
  overflow: 'hidden'
};
const Watermark = defineComponent({
  name: 'AWatermark',
  inheritAttrs: false,
  props: initDefaultProps(watermarkProps(), {
    zIndex: 9,
    rotate: -22,
    font: {},
    gap: [100, 100],
    inherit: true
  }),
  setup(props, {
    attrs,
    slots
  }) {
    const [, token] = useToken();
    const fontSize = computed(() => {
      var _a, _b;
      return (_b = (_a = props.font) === null || _a === void 0 ? void 0 : _a.fontSize) !== null && _b !== void 0 ? _b : token.value.fontSizeLG;
    });
    const fontWeight = computed(() => {
      var _a, _b;
      return (_b = (_a = props.font) === null || _a === void 0 ? void 0 : _a.fontWeight) !== null && _b !== void 0 ? _b : 'normal';
    });
    const fontStyle = computed(() => {
      var _a, _b;
      return (_b = (_a = props.font) === null || _a === void 0 ? void 0 : _a.fontStyle) !== null && _b !== void 0 ? _b : 'normal';
    });
    const fontFamily = computed(() => {
      var _a, _b;
      return (_b = (_a = props.font) === null || _a === void 0 ? void 0 : _a.fontFamily) !== null && _b !== void 0 ? _b : 'sans-serif';
    });
    const color = computed(() => {
      var _a, _b;
      return (_b = (_a = props.font) === null || _a === void 0 ? void 0 : _a.color) !== null && _b !== void 0 ? _b : token.value.colorFill;
    });
    const textAlign = computed(() => {
      var _a;
      return (_a = props.font.textAlign) !== null && _a !== void 0 ? _a : 'center';
    });
    const gapX = computed(() => {
      var _a, _b;
      return (_b = (_a = props.gap) === null || _a === void 0 ? void 0 : _a[0]) !== null && _b !== void 0 ? _b : 100;
    });
    const gapY = computed(() => {
      var _a, _b;
      return (_b = (_a = props.gap) === null || _a === void 0 ? void 0 : _a[1]) !== null && _b !== void 0 ? _b : 100;
    });
    const gapXCenter = computed(() => gapX.value / 2);
    const gapYCenter = computed(() => gapY.value / 2);
    const offsetLeft = computed(() => {
      var _a, _b;
      return (_b = (_a = props.offset) === null || _a === void 0 ? void 0 : _a[0]) !== null && _b !== void 0 ? _b : gapXCenter.value;
    });
    const offsetTop = computed(() => {
      var _a, _b;
      return (_b = (_a = props.offset) === null || _a === void 0 ? void 0 : _a[1]) !== null && _b !== void 0 ? _b : gapYCenter.value;
    });
    const markStyle = computed(() => {
      var _a;
      const markStyle = {
        zIndex: (_a = props.zIndex) !== null && _a !== void 0 ? _a : 9,
        position: 'absolute',
        left: 0,
        top: 0,
        width: '100%',
        height: '100%',
        pointerEvents: 'none',
        backgroundRepeat: 'repeat'
      };
      /** Calculate the style of the offset */
      let positionLeft = offsetLeft.value - gapXCenter.value;
      let positionTop = offsetTop.value - gapYCenter.value;
      if (positionLeft > 0) {
        markStyle.left = `${positionLeft}px`;
        markStyle.width = `calc(100% - ${positionLeft}px)`;
        positionLeft = 0;
      }
      if (positionTop > 0) {
        markStyle.top = `${positionTop}px`;
        markStyle.height = `calc(100% - ${positionTop}px)`;
        positionTop = 0;
      }
      markStyle.backgroundPosition = `${positionLeft}px ${positionTop}px`;
      return markStyle;
    });
    const container = ref(null);
    const subElements = ref(new Set());
    const targetElements = computed(() => {
      const list = container.value ? [container.value] : [];
      return [...list, ...Array.from(subElements.value)];
    });
    // ============================ Content =============================
    /**
     * Get the width and height of the watermark. The default values are as follows
     * Image: [120, 64]; Content: It's calculated by content;
     */
    const getMarkSize = ctx => {
      var _a, _b;
      let defaultWidth = 120;
      let defaultHeight = 64;
      if (!props.image && ctx.measureText) {
        ctx.font = `${Number(fontSize.value)}px ${fontFamily.value}`;
        const contents = toList(props.content);
        const sizes = contents.map(item => {
          const metrics = ctx.measureText(item);
          return [metrics.width, metrics.fontBoundingBoxAscent + metrics.fontBoundingBoxDescent];
        });
        defaultWidth = Math.ceil(Math.max(...sizes.map(size => size[0])));
        defaultHeight = Math.ceil(Math.max(...sizes.map(size => size[1]))) * contents.length + (contents.length - 1) * FontGap;
      }
      return [(_a = props.width) !== null && _a !== void 0 ? _a : defaultWidth, (_b = props.height) !== null && _b !== void 0 ? _b : defaultHeight];
    };
    const getClips = useClips();
    const getClipsCache = useSingletonCache();
    const watermarkInfo = ref(null);
    // Generate new Watermark content
    const renderWatermark = () => {
      const canvas = document.createElement('canvas');
      const ctx = canvas.getContext('2d');
      if (ctx) {
        const ratio = getPixelRatio();
        const [markWidth, markHeight] = getMarkSize(ctx);
        const drawCanvas = drawContent => {
          const params = [drawContent || '', props.rotate, ratio, markWidth, markHeight, {
            color: color.value,
            fontSize: fontSize.value,
            fontStyle: fontStyle.value,
            fontWeight: fontWeight.value,
            fontFamily: fontFamily.value,
            textAlign: textAlign.value
          }, gapX.value, gapY.value];
          const [nextClips, clipWidth] = getClipsCache(params, () => getClips(...params));
          watermarkInfo.value = [nextClips, clipWidth];
        };
        if (props.image) {
          const img = new Image();
          img.onload = () => {
            drawCanvas(img);
          };
          img.onerror = () => {
            drawCanvas(props.content);
          };
          img.crossOrigin = 'anonymous';
          img.referrerPolicy = 'no-referrer';
          img.src = props.image;
        } else {
          drawCanvas(props.content);
        }
      }
    };
    const syncWatermark = useRafDebounce(renderWatermark);
    // ============================= Effect =============================
    // Append watermark to the container
    const [appendWatermark, removeWatermark, isWatermarkEle] = useWatermark(markStyle, props.onRemove);
    watch([() => watermarkInfo.value, () => targetElements.value], ([rawWatermarkInfo, rawTargetElements]) => {
      if (rawWatermarkInfo) {
        rawTargetElements.forEach(holder => {
          appendWatermark(rawWatermarkInfo[0], rawWatermarkInfo[1], holder);
        });
      }
    });
    // ============================ Observe =============================
    const onMutate = mutations => {
      const mergedStyle = _extends(_extends({}, fixedStyle), attrs.style || {});
      mutations.forEach(mutation => {
        if (reRendering(mutation, isWatermarkEle)) {
          syncWatermark();
        } else if (mutation.target === container.value && mutation.attributeName === 'style') {
          // We've only force container not modify.
          // Not consider nest case.
          const keyStyles = Object.keys(fixedStyle);
          for (let i = 0; i < keyStyles.length; i += 1) {
            const key = keyStyles[i];
            const oriValue = mergedStyle[key];
            const currentValue = container.value.style[key];
            if (oriValue && oriValue !== currentValue) {
              container.value.style[key] = oriValue;
            }
          }
        }
      });
    };
    useMutateObserver(targetElements, onMutate);
    watch([() => props.rotate, () => props.zIndex, () => props.width, () => props.height, () => props.image, () => props.content, color, fontSize, fontWeight, fontStyle, fontFamily, textAlign, gapX, gapY, offsetLeft, offsetTop], () => {
      syncWatermark();
    }, {
      flush: 'post',
      immediate: true
    });
    if (props.inherit) {
      useProvideWatermark({
        add(ele) {
          const clone = new Set(subElements.value);
          clone.add(ele);
          subElements.value = clone;
        },
        remove(ele) {
          removeWatermark(ele);
          const clone = new Set(subElements.value);
          clone.delete(ele);
          subElements.value = getSizeDiff(subElements.value, clone);
        }
      });
    }
    return () => {
      var _a;
      return _createVNode("div", _objectSpread(_objectSpread({}, attrs), {}, {
        "ref": container,
        "class": [attrs.class, props.rootClassName],
        "style": [fixedStyle, attrs.style]
      }), [(_a = slots.default) === null || _a === void 0 ? void 0 : _a.call(slots)]);
    };
  }
});
export default withInstall(Watermark);