'use strict';

var React = require('react');
var ReactDOM = require('react-dom');
var icons = require('./icons.js');
var utils = require('./utils.js');

const IMAGE_QUERY = ['img', 'svg', '[role="img"]', '[data-zoom]']
    .map(x => `${x}:not([aria-hidden="true"])`)
    .join(',');
const defaultBodyAttrs = {
    overflow: '',
    width: '',
};
function Controlled(props) {
    return React.createElement(ControlledBase, { ...props });
}
class ControlledBase extends React.Component {
    constructor() {
        super(...arguments);
        this.state = {
            id: '',
            isZoomImgLoaded: false,
            loadedImgEl: undefined,
            modalState: "UNLOADED",
            shouldRefresh: false,
            styleGhost: {},
        };
        this.refContent = React.createRef();
        this.refDialog = React.createRef();
        this.refModalContent = React.createRef();
        this.refModalImg = React.createRef();
        this.refWrap = React.createRef();
        this.imgEl = null;
        this.isScaling = false;
        this.prevBodyAttrs = defaultBodyAttrs;
        this.styleModalImg = {};
        this.handleModalStateChange = (prevModalState) => {
            const { modalState } = this.state;
            if (prevModalState !== "LOADING" &&
                modalState === "LOADING") {
                this.loadZoomImg();
                window.addEventListener('resize', this.handleResize, { passive: true });
                window.addEventListener('touchstart', this.handleTouchStart, {
                    passive: true,
                });
                window.addEventListener('touchmove', this.handleTouchMove, {
                    passive: true,
                });
                window.addEventListener('touchend', this.handleTouchEnd, {
                    passive: true,
                });
                window.addEventListener('touchcancel', this.handleTouchCancel, {
                    passive: true,
                });
                document.addEventListener('keydown', this.handleKeyDown, true);
            }
            else if (prevModalState !== "LOADED" &&
                modalState === "LOADED") {
                window.addEventListener('wheel', this.handleWheel, { passive: true });
            }
            else if (prevModalState !== "UNLOADING" &&
                modalState === "UNLOADING") {
                this.ensureImgTransitionEnd();
                window.removeEventListener('wheel', this.handleWheel);
                window.removeEventListener('touchstart', this.handleTouchStart);
                window.removeEventListener('touchmove', this.handleTouchMove);
                window.removeEventListener('touchend', this.handleTouchEnd);
                window.removeEventListener('touchcancel', this.handleTouchCancel);
                document.removeEventListener('keydown', this.handleKeyDown, true);
            }
            else if (prevModalState !== "UNLOADED" &&
                modalState === "UNLOADED") {
                this.bodyScrollEnable();
                window.removeEventListener('resize', this.handleResize);
                this.refModalImg.current?.removeEventListener?.('transitionend', this.handleImgTransitionEnd);
                this.refDialog.current?.close?.();
            }
        };
        this.getDialogContainer = () => {
            let el = document.querySelector('[data-rmiz-portal]');
            if (el == null) {
                el = document.createElement('div');
                el.setAttribute('data-rmiz-portal', '');
                document.body.appendChild(el);
            }
            return el;
        };
        this.setId = () => {
            const gen4 = () => Math.random().toString(16).slice(-4);
            this.setState({ id: gen4() + gen4() + gen4() });
        };
        this.setAndTrackImg = () => {
            const contentEl = this.refContent.current;
            if (!contentEl)
                return;
            this.imgEl = contentEl.querySelector(IMAGE_QUERY);
            if (this.imgEl) {
                this.contentNotFoundChangeObserver?.disconnect?.();
                this.imgEl.addEventListener('load', this.handleImgLoad);
                this.imgEl.addEventListener('click', this.handleZoom);
                if (!this.state.loadedImgEl) {
                    this.handleImgLoad();
                }
                this.imgElResizeObserver = new ResizeObserver(entries => {
                    const entry = entries[0];
                    if (entry?.target) {
                        this.imgEl = entry.target;
                        this.setState({ styleGhost: utils.getStyleGhost(this.imgEl) });
                    }
                });
                this.imgElResizeObserver.observe(this.imgEl);
                if (!this.contentChangeObserver) {
                    this.contentChangeObserver = new MutationObserver(() => {
                        this.setState({ styleGhost: utils.getStyleGhost(this.imgEl) });
                    });
                    this.contentChangeObserver.observe(contentEl, {
                        attributes: true,
                        childList: true,
                        subtree: true,
                    });
                }
            }
            else if (!this.contentNotFoundChangeObserver) {
                this.contentNotFoundChangeObserver = new MutationObserver(this.setAndTrackImg);
                this.contentNotFoundChangeObserver.observe(contentEl, {
                    childList: true,
                    subtree: true,
                });
            }
        };
        this.handleIfZoomChanged = (prevIsZoomed) => {
            const { isZoomed } = this.props;
            if (!prevIsZoomed && isZoomed) {
                this.zoom();
            }
            else if (prevIsZoomed && !isZoomed) {
                this.unzoom();
            }
        };
        this.handleImgLoad = () => {
            const imgSrc = utils.getImgSrc(this.imgEl);
            if (!imgSrc)
                return;
            const img = new Image();
            if (utils.testImg(this.imgEl)) {
                img.sizes = this.imgEl.sizes;
                img.srcset = this.imgEl.srcset;
                img.crossOrigin = this.imgEl.crossOrigin;
            }
            img.src = imgSrc;
            const setLoaded = () => {
                this.setState({
                    loadedImgEl: img,
                    styleGhost: utils.getStyleGhost(this.imgEl),
                });
            };
            img
                .decode()
                .then(setLoaded)
                .catch(() => {
                if (utils.testImgLoaded(img)) {
                    setLoaded();
                    return;
                }
                img.onload = setLoaded;
            });
        };
        this.handleZoom = (e) => {
            if (!this.props.isDisabled && this.hasImage()) {
                this.props.onZoomChange?.(true, { event: e });
            }
        };
        this.handleUnzoom = (e) => {
            if (!this.props.isDisabled) {
                this.props.onZoomChange?.(false, { event: e });
            }
        };
        this.handleBtnUnzoomClick = (e) => {
            e.preventDefault();
            e.stopPropagation();
            this.handleUnzoom(e);
        };
        this.handleDialogCancel = (e) => {
            e.preventDefault();
        };
        this.handleDialogClick = (e) => {
            if (e.target === this.refModalContent.current ||
                e.target === this.refModalImg.current) {
                e.stopPropagation();
                this.handleUnzoom(e);
            }
        };
        this.handleDialogClose = (e) => {
            e.stopPropagation();
            this.handleUnzoom(e);
        };
        this.handleKeyDown = (e) => {
            if (e.key === 'Escape' || e.keyCode === 27) {
                e.preventDefault();
                e.stopPropagation();
                this.handleUnzoom(e);
            }
        };
        this.handleWheel = (e) => {
            if (e.ctrlKey)
                return;
            e.stopPropagation();
            queueMicrotask(() => {
                this.handleUnzoom(e);
            });
        };
        this.handleTouchStart = (e) => {
            if (e.touches.length > 1) {
                this.isScaling = true;
                return;
            }
            if (e.changedTouches.length === 1 && e.changedTouches[0]) {
                this.touchYStart = e.changedTouches[0].screenY;
            }
        };
        this.handleTouchMove = (e) => {
            const browserScale = window.visualViewport?.scale ?? 1;
            if (this.props.canSwipeToUnzoom &&
                !this.isScaling &&
                browserScale <= 1 &&
                this.touchYStart != null &&
                e.changedTouches[0]) {
                this.touchYEnd = e.changedTouches[0].screenY;
                const max = Math.max(this.touchYStart, this.touchYEnd);
                const min = Math.min(this.touchYStart, this.touchYEnd);
                const delta = Math.abs(max - min);
                if (delta > this.props.swipeToUnzoomThreshold) {
                    this.touchYStart = undefined;
                    this.touchYEnd = undefined;
                    this.handleUnzoom(e);
                }
            }
        };
        this.handleTouchEnd = () => {
            this.isScaling = false;
            this.touchYStart = undefined;
            this.touchYEnd = undefined;
        };
        this.handleTouchCancel = () => {
            this.isScaling = false;
            this.touchYStart = undefined;
            this.touchYEnd = undefined;
        };
        this.handleResize = () => {
            this.setState({ shouldRefresh: true });
        };
        this.hasImage = () => {
            return (this.imgEl &&
                (this.state.loadedImgEl || utils.testSvg(this.imgEl)) &&
                window.getComputedStyle(this.imgEl).display !== 'none');
        };
        this.zoom = () => {
            this.bodyScrollDisable();
            this.refDialog.current?.showModal?.();
            this.refModalImg.current?.addEventListener?.('transitionend', this.handleImgTransitionEnd);
            this.setState({ modalState: "LOADING" });
        };
        this.unzoom = () => {
            this.setState({ modalState: "UNLOADING" });
        };
        this.handleImgTransitionEnd = () => {
            clearTimeout(this.timeoutTransitionEnd);
            if (this.state.modalState === "LOADING") {
                this.setState({ modalState: "LOADED" });
            }
            else if (this.state.modalState === "UNLOADING") {
                this.setState({ shouldRefresh: false, modalState: "UNLOADED" });
            }
        };
        this.ensureImgTransitionEnd = () => {
            if (this.refModalImg.current) {
                const td = window.getComputedStyle(this.refModalImg.current).transitionDuration;
                const tdFloat = parseFloat(td);
                if (tdFloat) {
                    const tdMs = tdFloat * (td.endsWith('ms') ? 1 : 1000) + 50;
                    this.timeoutTransitionEnd = setTimeout(this.handleImgTransitionEnd, tdMs);
                }
            }
        };
        this.bodyScrollDisable = () => {
            this.prevBodyAttrs = {
                overflow: document.body.style.overflow,
                width: document.body.style.width,
            };
            const clientWidth = document.body.clientWidth;
            document.body.style.overflow = 'hidden';
            document.body.style.width = `${clientWidth}px`;
        };
        this.bodyScrollEnable = () => {
            document.body.style.width = this.prevBodyAttrs.width;
            document.body.style.overflow = this.prevBodyAttrs.overflow;
            this.prevBodyAttrs = defaultBodyAttrs;
        };
        this.loadZoomImg = () => {
            const { props: { zoomImg }, } = this;
            const zoomImgSrc = zoomImg?.src;
            if (zoomImgSrc) {
                const img = new Image();
                img.sizes = zoomImg?.sizes ?? '';
                img.srcset = zoomImg?.srcSet ?? '';
                img.crossOrigin = zoomImg?.crossOrigin ?? undefined;
                img.src = zoomImgSrc;
                const setLoaded = () => {
                    this.setState({ isZoomImgLoaded: true });
                };
                img
                    .decode()
                    .then(setLoaded)
                    .catch(() => {
                    if (utils.testImgLoaded(img)) {
                        setLoaded();
                        return;
                    }
                    img.onload = setLoaded;
                });
            }
        };
        this.UNSAFE_handleSvg = () => {
            const { imgEl, refModalImg, styleModalImg } = this;
            if (utils.testSvg(imgEl)) {
                const svgEl = imgEl.cloneNode(true);
                utils.adjustSvgIDs(svgEl);
                svgEl.style.width = `${styleModalImg.width || 0}px`;
                svgEl.style.height = `${styleModalImg.height || 0}px`;
                svgEl.addEventListener('click', this.handleUnzoom);
                refModalImg.current?.firstChild?.remove?.();
                refModalImg.current?.appendChild?.(svgEl);
            }
        };
    }
    render() {
        const { handleBtnUnzoomClick, handleDialogCancel, handleDialogClick, handleDialogClose, handleUnzoom, handleZoom, imgEl, props: { a11yNameButtonUnzoom, a11yNameButtonZoom, children, classDialog, IconUnzoom, IconZoom, isZoomed, wrapElement: WrapElement, ZoomContent, zoomImg, zoomMargin, }, refContent, refDialog, refModalContent, refModalImg, refWrap, state: { id, isZoomImgLoaded, loadedImgEl, modalState, shouldRefresh, styleGhost, }, } = this;
        const idModal = `rmiz-modal-${id}`;
        const idModalImg = `rmiz-modal-img-${id}`;
        const isDiv = utils.testDiv(imgEl);
        const isImg = utils.testImg(imgEl);
        const isSvg = utils.testSvg(imgEl);
        const imgAlt = utils.getImgAlt(imgEl);
        const imgSrc = utils.getImgSrc(imgEl);
        const imgSizes = isImg ? imgEl.sizes : undefined;
        const imgSrcSet = isImg ? imgEl.srcset : undefined;
        const imgCrossOrigin = isImg ? imgEl.crossOrigin : undefined;
        const hasZoomImg = !!zoomImg?.src;
        const hasImage = this.hasImage();
        const labelBtnZoom = imgAlt
            ? `${a11yNameButtonZoom}: ${imgAlt}`
            : a11yNameButtonZoom;
        const isModalActive = modalState === "LOADING" || modalState === "LOADED";
        const dataContentState = hasImage ? 'found' : 'not-found';
        const dataOverlayState = modalState === "UNLOADED" || modalState === "UNLOADING"
            ? 'hidden'
            : 'visible';
        const styleContent = {
            visibility: modalState === "UNLOADED" ? 'visible' : 'hidden',
        };
        this.styleModalImg = hasImage
            ? utils.getStyleModalImg({
                hasZoomImg,
                imgSrc,
                isSvg,
                isZoomed: isZoomed && isModalActive,
                loadedImgEl,
                offset: zoomMargin,
                shouldRefresh,
                targetEl: imgEl,
            })
            : {};
        let modalContent = null;
        if (hasImage) {
            const modalImg = isImg || isDiv ? (React.createElement("img", { alt: imgAlt, crossOrigin: imgCrossOrigin, sizes: imgSizes, src: imgSrc, srcSet: imgSrcSet, ...(isZoomImgLoaded && modalState === "LOADED"
                    ? zoomImg
                    : {}), "data-rmiz-modal-img": "", height: this.styleModalImg.height || undefined, id: idModalImg, ref: refModalImg, style: this.styleModalImg, width: this.styleModalImg.width || undefined })) : isSvg ? (React.createElement("div", { "data-rmiz-modal-img": true, ref: refModalImg, style: this.styleModalImg })) : null;
            const modalBtnUnzoom = (React.createElement("button", { "aria-label": a11yNameButtonUnzoom, "data-rmiz-btn-unzoom": "", onClick: handleBtnUnzoomClick, type: "button" },
                React.createElement(IconUnzoom, null)));
            modalContent = ZoomContent ? (React.createElement(ZoomContent, { buttonUnzoom: modalBtnUnzoom, modalState: modalState, img: modalImg, isZoomImgLoaded: isZoomImgLoaded, onUnzoom: handleUnzoom })) : (React.createElement(React.Fragment, null,
                modalImg,
                modalBtnUnzoom));
        }
        return (React.createElement(WrapElement, { "aria-owns": idModal, "data-rmiz": "", ref: refWrap },
            React.createElement(WrapElement, { "data-rmiz-content": dataContentState, ref: refContent, style: styleContent }, children),
            hasImage && (React.createElement(WrapElement, { "data-rmiz-ghost": "", style: styleGhost },
                React.createElement("button", { "aria-label": labelBtnZoom, "data-rmiz-btn-zoom": "", onClick: handleZoom, type: "button" },
                    React.createElement(IconZoom, null)))),
            hasImage &&
                ReactDOM.createPortal(React.createElement("dialog", { "aria-labelledby": idModalImg, "aria-modal": "true", className: classDialog, "data-rmiz-modal": "", id: idModal, onClick: handleDialogClick, onClose: handleDialogClose, onCancel: handleDialogCancel, ref: refDialog, role: "dialog" },
                    React.createElement("div", { "data-rmiz-modal-overlay": dataOverlayState }),
                    React.createElement("div", { "data-rmiz-modal-content": "", ref: refModalContent }, modalContent)), this.getDialogContainer())));
    }
    componentDidMount() {
        this.setId();
        this.setAndTrackImg();
        this.handleImgLoad();
        this.UNSAFE_handleSvg();
    }
    componentWillUnmount() {
        if (this.state.modalState !== "UNLOADED") {
            this.bodyScrollEnable();
        }
        this.contentChangeObserver?.disconnect?.();
        this.contentNotFoundChangeObserver?.disconnect?.();
        this.imgElResizeObserver?.disconnect?.();
        this.imgEl?.removeEventListener?.('load', this.handleImgLoad);
        this.imgEl?.removeEventListener?.('click', this.handleZoom);
        this.refModalImg.current?.removeEventListener?.('transitionend', this.handleImgTransitionEnd);
        window.removeEventListener('wheel', this.handleWheel);
        window.removeEventListener('touchstart', this.handleTouchStart);
        window.removeEventListener('touchmove', this.handleTouchMove);
        window.removeEventListener('touchend', this.handleTouchEnd);
        window.removeEventListener('touchcancel', this.handleTouchCancel);
        window.removeEventListener('resize', this.handleResize);
        document.removeEventListener('keydown', this.handleKeyDown, true);
    }
    componentDidUpdate(prevProps, prevState) {
        this.handleModalStateChange(prevState.modalState);
        this.UNSAFE_handleSvg();
        this.handleIfZoomChanged(prevProps.isZoomed);
    }
}
ControlledBase.defaultProps = {
    a11yNameButtonUnzoom: 'Minimize image',
    a11yNameButtonZoom: 'Expand image',
    canSwipeToUnzoom: true,
    IconUnzoom: icons.ICompress,
    IconZoom: icons.IEnlarge,
    isDisabled: false,
    swipeToUnzoomThreshold: 10,
    wrapElement: 'div',
    zoomMargin: 0,
};

exports.Controlled = Controlled;
