/**
 * ✔ src
 * ✔ mode
 * ✘ show-menu-by-longpress
 * ✔ binderror
 * ✔ bindload
 * ✘ fade-in
 * ✔ webp
 * ✘ lazy-load
 * ✔ bindtap
 * ✔ DEFAULT_SIZE
 */
import { useEffect, useMemo, useState, useRef, forwardRef, createElement } from 'react';
import { Image as RNImage, View } from 'react-native';
import { noop } from '@mpxjs/utils';
import { SvgCssUri } from 'react-native-svg/css';
import useInnerProps, { getCustomEvent } from './getInnerListeners';
import useNodesRef from './useNodesRef';
import { SVG_REGEXP, useLayout, useTransformStyle, renderImage, extendObject, isAndroid } from './utils';
import Portal from './mpx-portal';
const DEFAULT_IMAGE_WIDTH = 320;
const DEFAULT_IMAGE_HEIGHT = 240;
const cropMode = [
    'top',
    'bottom',
    'center',
    'right',
    'left',
    'top left',
    'top right',
    'bottom left',
    'bottom right'
];
const ModeMap = new Map([
    ['scaleToFill', 'stretch'],
    ['aspectFit', 'contain'],
    ['aspectFill', 'cover'],
    ['widthFix', 'stretch'],
    ['heightFix', 'stretch'],
    ...cropMode.map(mode => [mode, 'stretch'])
]);
const isNumber = (value) => typeof value === 'number';
const relativeCenteredSize = (viewSize, imageSize) => {
    return (viewSize - imageSize) / 2;
};
// 获取能完全显示图片的缩放比例：长宽方向的缩放比例最小值即为能完全展示的比例
function getFitScale(width1, height1, width2, height2) {
    return Math.min(width2 / width1, height2 / height1);
}
function getFillScale(width1, height1, width2, height2) {
    return Math.max(width2 / width1, height2 / height1);
}
function noMeetCalcRule(isSvg, mode, viewWidth, viewHeight, ratio) {
    const isMeetSize = viewWidth && viewHeight && ratio;
    if (isSvg && !isMeetSize)
        return true;
    if (!isSvg && !['scaleToFill', 'aspectFit', 'aspectFill'].includes(mode) && !isMeetSize)
        return true;
    return false;
}
const getFixedWidth = (viewWidth, viewHeight, ratio) => {
    if (!ratio)
        return viewWidth;
    const fixed = viewHeight / ratio;
    return !fixed ? viewWidth : fixed;
};
const getFixedHeight = (viewWidth, viewHeight, ratio) => {
    const fixed = viewWidth * ratio;
    return !fixed ? viewHeight : fixed;
};
const Image = forwardRef((props, ref) => {
    const { src = '', mode = 'scaleToFill', style = {}, 'enable-var': enableVar, 'external-var-context': externalVarContext, 'parent-font-size': parentFontSize, 'enable-fast-image': enableFastImage, 'parent-width': parentWidth, 'parent-height': parentHeight, bindload, binderror } = props;
    const defaultStyle = {
        width: DEFAULT_IMAGE_WIDTH,
        height: DEFAULT_IMAGE_HEIGHT
    };
    const styleObj = extendObject({}, defaultStyle, style, { overflow: 'hidden' });
    const nodeRef = useRef(null);
    useNodesRef(props, ref, nodeRef, {
        defaultStyle
    });
    const isSvg = SVG_REGEXP.test(src);
    const isWidthFixMode = mode === 'widthFix';
    const isHeightFixMode = mode === 'heightFix';
    const isCropMode = cropMode.includes(mode);
    const isLayoutMode = isWidthFixMode || isHeightFixMode || isCropMode;
    const resizeMode = ModeMap.get(mode) || 'stretch';
    const onLayout = ({ nativeEvent: { layout: { width, height } } }) => {
        state.current.viewWidth = width;
        state.current.viewHeight = height;
        // 实际渲染尺寸可能会指定的值不一致，误差低于 0.5 则认为没有变化
        if (Math.abs(viewHeight - height) < 0.5 && Math.abs(viewWidth - width) < 0.5) {
            if (state.current.imageWidth && state.current.imageHeight && state.current.ratio) {
                if (!loaded)
                    setLoaded(true);
            }
            return;
        }
        if (state.current.imageWidth && state.current.imageHeight && state.current.ratio) {
            setRatio(state.current.ratio);
            setImageWidth(state.current.imageWidth);
            setImageHeight(state.current.imageHeight);
            setViewSize(state.current.viewWidth, state.current.viewHeight, state.current.ratio);
            setLoaded(true);
        }
    };
    const { hasPositionFixed, hasSelfPercent, normalStyle, setWidth, setHeight } = useTransformStyle(styleObj, { enableVar, transformRadiusPercent: isAndroid && !isSvg && !isLayoutMode, externalVarContext, parentFontSize, parentWidth, parentHeight });
    const { layoutRef, layoutStyle, layoutProps } = useLayout({
        props,
        hasSelfPercent,
        setWidth,
        setHeight,
        nodeRef,
        onLayout: isLayoutMode ? onLayout : noop
    });
    const { width, height } = normalStyle;
    const [viewWidth, setViewWidth] = useState(isNumber(width) ? width : 0);
    const [viewHeight, setViewHeight] = useState(isNumber(height) ? height : 0);
    const [imageWidth, setImageWidth] = useState(0);
    const [imageHeight, setImageHeight] = useState(0);
    const [ratio, setRatio] = useState(0);
    const [loaded, setLoaded] = useState(!isLayoutMode);
    const state = useRef({
        viewWidth,
        viewHeight
    });
    function setViewSize(viewWidth, viewHeight, ratio) {
        // 在特定模式下可预测 view 的变化，在onLayout触发时能以此避免重复render
        switch (mode) {
            case 'widthFix': {
                setViewWidth(viewWidth);
                const fixedHeight = getFixedHeight(viewWidth, viewHeight, ratio);
                setViewHeight(fixedHeight);
                break;
            }
            case 'heightFix': {
                setViewHeight(viewHeight);
                const fixedWidth = getFixedWidth(viewWidth, viewHeight, ratio);
                setViewWidth(fixedWidth);
                break;
            }
            default:
                setViewHeight(viewHeight);
                setViewWidth(viewWidth);
                break;
        }
    }
    const modeStyle = useMemo(() => {
        if (noMeetCalcRule(isSvg, mode, viewWidth, viewHeight, ratio))
            return {};
        switch (mode) {
            case 'scaleToFill': // wx 中 svg 图片的 scaleToFill 模式效果与 aspectFit 一致，不会就行图片缩放，此处保持一致
            case 'aspectFit':
                if (isSvg) {
                    const scale = getFitScale(imageWidth, imageHeight, viewWidth, viewHeight);
                    return {
                        transform: [
                            { translateY: relativeCenteredSize(viewHeight, imageHeight * scale) },
                            { translateX: relativeCenteredSize(viewWidth, imageWidth * scale) },
                            { scale }
                        ]
                    };
                }
                return {};
            case 'aspectFill':
                if (isSvg) {
                    const scale = getFillScale(imageWidth, imageHeight, viewWidth, viewHeight);
                    return {
                        transform: [
                            { translateY: relativeCenteredSize(viewHeight, imageHeight * scale) },
                            { translateX: relativeCenteredSize(viewWidth, imageWidth * scale) },
                            { scale }
                        ]
                    };
                }
                return {};
            case 'widthFix':
            case 'heightFix':
                if (isSvg) {
                    const scale = getFitScale(imageWidth, imageHeight, viewWidth, viewHeight);
                    return {
                        transform: [{ scale }]
                    };
                }
                return {};
            case 'top':
                return {
                    transform: [
                        { translateX: relativeCenteredSize(viewWidth, imageWidth) }
                    ]
                };
            case 'bottom':
                return {
                    transform: [
                        { translateY: viewHeight - imageHeight },
                        { translateX: relativeCenteredSize(viewWidth, imageWidth) }
                    ]
                };
            case 'center':
                return {
                    transform: [
                        { translateY: relativeCenteredSize(viewHeight, imageHeight) },
                        { translateX: relativeCenteredSize(viewWidth, imageWidth) }
                    ]
                };
            case 'left':
                return {
                    transform: [
                        { translateY: relativeCenteredSize(viewHeight, imageHeight) }
                    ]
                };
            case 'right':
                return {
                    transform: [
                        { translateY: relativeCenteredSize(viewHeight, imageHeight) },
                        { translateX: viewWidth - imageWidth }
                    ]
                };
            case 'top left':
                return {};
            case 'top right':
                return {
                    transform: [
                        { translateX: viewWidth - imageWidth }
                    ]
                };
            case 'bottom left':
                return {
                    transform: [
                        { translateY: viewHeight - imageHeight }
                    ]
                };
            case 'bottom right':
                return {
                    transform: [
                        { translateY: viewHeight - imageHeight },
                        { translateX: viewWidth - imageWidth }
                    ]
                };
            default:
                return {};
        }
    }, [isSvg, mode, viewWidth, viewHeight, imageWidth, imageHeight, ratio]);
    const onSvgLoad = (evt) => {
        const { width, height } = evt.nativeEvent.layout;
        state.current.imageHeight = height;
        setImageHeight(height);
        state.current.ratio = !width ? 0 : height / width;
        if (isWidthFixMode
            ? state.current.viewWidth
            : isHeightFixMode
                ? state.current.viewHeight
                : state.current.viewWidth && state.current.viewHeight) {
            setRatio(state.current.ratio);
            setImageWidth(width);
            setImageHeight(height);
            setViewSize(state.current.viewWidth, state.current.viewHeight, state.current.ratio);
            setLoaded(true);
        }
        bindload && bindload(getCustomEvent('load', evt, {
            detail: { width, height },
            layoutRef
        }, props));
    };
    const onSvgError = (evt) => {
        binderror(getCustomEvent('error', evt, {
            detail: { errMsg: evt?.message },
            layoutRef
        }, props));
    };
    const onImageLoad = (evt) => {
        evt.persist();
        RNImage.getSize(src, (width, height) => {
            bindload(getCustomEvent('load', evt, {
                detail: { width, height },
                layoutRef
            }, props));
        });
    };
    const onImageError = (evt) => {
        binderror(getCustomEvent('error', evt, {
            detail: { errMsg: evt.nativeEvent.error },
            layoutRef
        }, props));
    };
    useEffect(() => {
        if (!isSvg && isLayoutMode) {
            RNImage.getSize(src, (width, height) => {
                state.current.imageWidth = width;
                state.current.imageHeight = height;
                state.current.ratio = !width ? 0 : height / width;
                if (isWidthFixMode
                    ? state.current.viewWidth
                    : isHeightFixMode
                        ? state.current.viewHeight
                        : state.current.viewWidth && state.current.viewHeight) {
                    setRatio(state.current.ratio);
                    setImageWidth(width);
                    setImageHeight(height);
                    setViewSize(state.current.viewWidth, state.current.viewHeight, state.current.ratio);
                    setLoaded(true);
                }
            }, () => {
                setLoaded(true);
            });
        }
    }, [src, isSvg, isLayoutMode]);
    const innerProps = useInnerProps(extendObject({}, props, layoutProps, {
        ref: nodeRef,
        style: extendObject({}, normalStyle, layoutStyle, isHeightFixMode ? { width: viewWidth } : {}, isWidthFixMode ? { height: viewHeight } : {})
    }), [
        'src',
        'mode',
        'svg'
    ], {
        layoutRef
    });
    function renderSvgImage() {
        return createElement(View, innerProps, createElement(SvgCssUri, {
            uri: src,
            onLayout: onSvgLoad,
            onError: binderror && onSvgError,
            style: extendObject({ transformOrigin: 'left top' }, modeStyle)
        }));
    }
    function renderBaseImage() {
        return renderImage(extendObject({
            source: { uri: src },
            resizeMode: resizeMode,
            onLoad: bindload && onImageLoad,
            onError: binderror && onImageError,
            style: extendObject({
                transformOrigin: 'left top',
                width: isCropMode ? imageWidth : '100%',
                height: isCropMode ? imageHeight : '100%'
            }, isCropMode ? modeStyle : {})
        }, isLayoutMode ? {} : innerProps), enableFastImage);
    }
    function renderLayoutImage() {
        return createElement(View, innerProps, loaded && renderBaseImage());
    }
    const finalComponent = isSvg ? renderSvgImage() : isLayoutMode ? renderLayoutImage() : renderBaseImage();
    if (hasPositionFixed) {
        return createElement(Portal, null, finalComponent);
    }
    return finalComponent;
});
Image.displayName = 'MpxImage';
export default Image;
