"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.PositionObserver = void 0;
const callAll_1 = require("../util/call/callAll");
const parseTransformXY_1 = require("./parseTransformXY");
const throttle_1 = require("./throttle");
const geometry_1 = require("./util/geometry");
class PositionObserver {
    constructor(system, callback) {
        this._system = system;
        this._callback = callback;
    }
    observe(element) {
        // console.log('PositionObserver', 'observe')
        const { api: { document: { MutationObserver, ResizeObserver }, }, } = this._system;
        if (this._abort) {
            this._abort();
            this._abort = undefined;
        }
        const { isConnected } = element;
        if (!isConnected) {
            console.log('PositionObserver', 'observe', '!isConnected');
            throw new Error('element is not mounted');
        }
        let x = 0;
        let y = 0;
        let sx = 1;
        let sy = 1;
        let rx = 0;
        let ry = 0;
        let rz = 0;
        let width = 0;
        let height = 0;
        let offset_x = 0;
        let offset_y = 0;
        let transform_x = 0;
        let transform_y = 0;
        let scale_x = 1;
        let scale_y = 1;
        let rotate_x = 0;
        let rotate_y = 0;
        let rotate_z = 0;
        let parent_x = 0;
        let parent_y = 0;
        let parent_scroll_top = 0;
        let parent_scroll_left = 0;
        let parent_scale_x = 1;
        let parent_scale_y = 1;
        let parent_rx = 0;
        let parent_ry = 0;
        let parent_rz = 0;
        let _transform;
        const _update_local = () => {
            __update_local();
            update();
        };
        const __update_local = () => {
            const { offsetLeft, offsetTop, offsetWidth, offsetHeight, style } = element;
            offset_x = offsetLeft;
            offset_y = offsetTop;
            const { transform } = style;
            if (transform !== _transform ||
                width !== offsetWidth ||
                height !== offsetHeight) {
                if (transform) {
                    const [_transform_x, _transform_y, _scale_x, _scale_y, _rotate_x, _rotate_y, _rotate_z,] = (0, parseTransformXY_1.parseTransformXY)(transform, offsetWidth, offsetHeight);
                    transform_x = _transform_x;
                    transform_y = _transform_y;
                    scale_x = _scale_x;
                    scale_y = _scale_y;
                    rotate_x = _rotate_x;
                    rotate_y = _rotate_y;
                    rotate_z = _rotate_z;
                }
                else {
                    transform_x = 0;
                    transform_y = 0;
                    scale_x = 1;
                    scale_y = 1;
                    rotate_x = 0;
                    rotate_y = 0;
                    rotate_z = 0;
                }
                _transform = transform;
            }
            width = offsetWidth;
            height = offsetHeight;
        };
        const { f: update_local, abort } = (0, throttle_1.animateThrottle)(this._system, _update_local);
        this._abort = abort;
        // const update_local = _update_local
        // this._abort = NOOP
        const _update = () => {
            sx = scale_x * parent_scale_x;
            sy = scale_y * parent_scale_y;
            const rxr = (0, geometry_1.angleToRad)(rotate_x);
            const ryr = (0, geometry_1.angleToRad)(rotate_y);
            const rzr = (0, geometry_1.angleToRad)(rotate_z);
            rx = rxr + parent_rx;
            ry = ryr + parent_ry;
            rz = rzr + parent_rz;
            const parent_rz_cos = Math.cos(parent_rz);
            const parent_rz_sin = Math.sin(parent_rz);
            const local_x = offset_x + transform_x;
            const local_y = offset_y + transform_y;
            const parent_scaled_local_x = local_x * parent_scale_x;
            const parent_scaled_local_y = local_y * parent_scale_y;
            const parent_scaled_rotated_local_x = parent_scaled_local_x * parent_rz_cos -
                parent_scaled_local_y * parent_rz_sin;
            const parent_scaled_rotated_local_y = parent_scaled_local_y * parent_rz_cos +
                parent_scaled_local_x * parent_rz_sin;
            const px = parent_x -
                parent_scroll_left +
                parent_scaled_rotated_local_x * scale_x -
                ((width * parent_scale_x) / 2) * (scale_x - 1);
            const py = parent_y -
                parent_scroll_top +
                parent_scaled_rotated_local_y * scale_y -
                ((height * parent_scale_y) / 2) * (scale_y - 1);
            const cx = px +
                (width * parent_scale_x * parent_rz_cos -
                    height * parent_scale_y * parent_rz_sin) /
                    2;
            const cy = py +
                (width * parent_scale_y * parent_rz_sin +
                    height * parent_scale_x * parent_rz_cos) /
                    2;
            const c = { x: cx, y: cy };
            const p = { x: px, y: py };
            const cp = (0, geometry_1.subtractVector)(p, c);
            const rcp = (0, geometry_1.rotateVector)(cp, rzr);
            const fp = (0, geometry_1.addVector)(c, rcp);
            x = fp.x;
            y = fp.y;
        };
        const update = () => {
            _update();
            this._callback({ x, y, sx, sy, rx, ry, rz });
        };
        const mutationConfig = {
            childList: false,
            subtree: false,
            attributes: true,
            attributeFilter: ['style'],
        };
        const mutationObserver = new MutationObserver(update_local);
        mutationObserver.observe(element, mutationConfig);
        const resizeObserver = new ResizeObserver(update_local);
        const resizeConfig = {
            box: 'device-pixel-content-box',
        };
        // Safari (on iOS) will not accept this config
        // resizeObserver.observe(element, resizeConfig)
        resizeObserver.observe(element);
        const unlisten_self = () => {
            mutationObserver.disconnect();
            resizeObserver.unobserve(element);
            resizeObserver.disconnect();
        };
        const update_parent = () => {
            const { offsetParent, parentElement } = element;
            const targetParent = parentElement;
            if (targetParent) {
                const scrollParentUnlisten = [];
                const pushScrollParent = (p) => {
                    const { scrollLeft, scrollTop } = p;
                    parent_scroll_top += scrollTop;
                    parent_scroll_left += scrollLeft;
                    let _scrollLeft = scrollLeft;
                    let _scrollTop = scrollTop;
                    const parentScrollListener = function () {
                        const { scrollLeft, scrollTop } = p;
                        parent_scroll_left += scrollLeft - _scrollLeft;
                        parent_scroll_top += scrollTop - _scrollTop;
                        _scrollLeft = scrollLeft;
                        _scrollTop = scrollTop;
                        update();
                    };
                    const { f: _parentScrollListener } = (0, throttle_1.animateThrottle)(this._system, parentScrollListener);
                    p.addEventListener('scroll', _parentScrollListener, {
                        passive: true,
                    });
                    const unlisten = () => {
                        p.removeEventListener('scroll', _parentScrollListener);
                    };
                    scrollParentUnlisten.push(unlisten);
                };
                let p = parentElement;
                while (p && p !== targetParent) {
                    pushScrollParent(p);
                    p = p.parentElement;
                }
                pushScrollParent(targetParent);
                const unlitenScroll = (0, callAll_1.callAll)(scrollParentUnlisten);
                const parentConfig = {
                    childList: true,
                    subtree: false,
                    attributes: true,
                    attributeFilter: ['style'],
                };
                const parentMutationCallback = (mutationsList) => {
                    update_local();
                };
                const parentMutationObserver = new MutationObserver(parentMutationCallback);
                parentMutationObserver.observe(targetParent, parentConfig);
                const parentPositionCallback = ({ x: _parent_x, y: _parent_y, sx: _parent_scale_x, sy: _parent_scale_y, rx: _parent_rotate_x, ry: _parent_rotate_y, rz: _parent_rotate_z, }) => {
                    parent_x = _parent_x;
                    parent_y = _parent_y;
                    parent_scale_x = _parent_scale_x;
                    parent_scale_y = _parent_scale_y;
                    parent_rx = _parent_rotate_x;
                    parent_ry = _parent_rotate_y;
                    parent_rz = _parent_rotate_z;
                    update_local();
                };
                const parentPostionObserver = new PositionObserver(this._system, parentPositionCallback);
                const { x: _parent_x, y: _parent_y, sx: _parent_scale_x, sy: _parent_scale_y, } = parentPostionObserver.observe(targetParent);
                parent_x = _parent_x;
                parent_y = _parent_y;
                parent_scale_x = _parent_scale_x;
                parent_scale_y = _parent_scale_y;
                _update();
                const parentResizeObserverCallback = () => {
                    update_local();
                };
                const parentResizeObserver = new ResizeObserver(parentResizeObserverCallback);
                parentResizeObserver.observe(targetParent);
                return () => {
                    unlitenScroll();
                    parentResizeObserver.disconnect();
                    parentMutationObserver.disconnect();
                    parentPostionObserver.disconnect();
                };
            }
            else {
                return () => { };
            }
        };
        const unlisten_parent = update_parent();
        const unlisten = () => {
            unlisten_self();
            unlisten_parent();
        };
        this._unlisten = unlisten;
        __update_local();
        _update();
        return { x, y, sx, sy, rx, ry, rz };
    }
    disconnect() {
        // console.log('PositionObserver', 'disconnect')
        if (this._unlisten) {
            this._unlisten();
            this._unlisten = undefined;
        }
    }
}
exports.PositionObserver = PositionObserver;
//# sourceMappingURL=PositionObserver.js.map