import { CanvasConfig } from './CanvasConfig.js';
export class RevealBoundaryStore {
    constructor(id, $el) {
        // The current cursor position relative to window.
        this.clientX = -1000;
        this.clientY = -1000;
        // The cursor position of painted reveal effect.
        this.paintedClientX = -1000;
        this.paintedClientY = -1000;
        this.mouseInBoundary = false;
        this.canvasList = [];
        this.animationQueue = [];
        this.animationFrame = null;
        this.mouseUpClientX = null;
        this.mouseUpClientY = null;
        this.mouseDownAnimateStartFrame = null;
        this.mouseDownAnimateCurrentFrame = null;
        this.mouseDownAnimateReleasedFrame = null;
        this.mouseDownAnimateLogicFrame = null;
        this.mousePressed = false;
        this.mouseReleased = false;
        this.maxRadius = -1;
        this.requestedAnimationFrame = false;
        this.addReveal = ($canvas, $container) => {
            const canvasConfig = new CanvasConfig(this, $canvas, $container);
            canvasConfig.cacheBoundingRect();
            canvasConfig.cacheCanvasPaintingStyle();
            canvasConfig.updateCachedBitmap();
            this.canvasList.push(canvasConfig);
        };
        this.removeReveal = ($el) => {
            this.canvasList = this.canvasList.filter((config) => {
                return config && config.canvas !== $el;
            });
        };
        this.updateMaxRadius = (x) => {
            this.maxRadius = Math.max(x, this.maxRadius);
        };
        this.requestPaintAll = (cleanup) => {
            if (this.requestedAnimationFrame)
                return;
            this.requestedAnimationFrame = true;
            this.animationFrame = window.requestAnimationFrame((frame) => {
                this.requestedAnimationFrame = false;
                this.paintAll(cleanup ? 0 : frame, true);
            });
        };
        this.onPointerMoveOnScreen = (x, y) => {
            const boundingBox = this.container.getBoundingClientRect();
            if (x < boundingBox.left - this.maxRadius)
                return false;
            if (x > boundingBox.right + this.maxRadius)
                return false;
            if (y < boundingBox.top - this.maxRadius)
                return false;
            if (y > boundingBox.bottom + this.maxRadius)
                return false;
            this.mouseInBoundary = true;
            this.clientX = x;
            this.clientY = y;
            this.requestPaintAll();
            return true;
        };
        /**
         * Repaint all canvas while user's pointer has entered the boundary.
         */
        this.onPointerEnterBoundary = () => {
            this.mouseInBoundary = true;
            if (!this.animationFrame) {
                this.requestPaintAll();
            }
        };
        /**
         * Clear all canvas while user's pointer leave the bounary.
         */
        this.onPointerLeaveBoundary = () => {
            this.mouseInBoundary = false;
            this.requestPaintAll(true);
        };
        /**
         * The painting method that draw everything on your screen.
         * @param frame current frame index.
         * @param skipSamePointerPositionCheck Literally.
         */
        this.paintAll = (frame, skipSamePointerPositionCheck) => {
            if (!skipSamePointerPositionCheck && !this.mouseInBoundary && this.animationQueue.length === 0) {
                return;
            }
            // Animation duration checkup
            for (let i = 0; i < this.animationQueue.length; i++) {
                const config = this.animationQueue[i];
                if (!frame || config.currentFrameId === frame) {
                    continue;
                }
                config.currentFrameId = frame;
                if (config.mouseDownAnimateStartFrame === null) {
                    config.mouseDownAnimateStartFrame = frame;
                }
                const relativeFrame = frame - config.mouseDownAnimateStartFrame;
                config.mouseDownAnimateCurrentFrame = relativeFrame;
                const speed = config.cachedStyle.pressAnimationSpeed;
                const accelerateRate = config.cachedStyle.releaseAnimationAccelerateRate;
                let unitLogicFrame = relativeFrame;
                if (config.mouseReleased && config.mouseDownAnimateReleasedFrame) {
                    unitLogicFrame += (relativeFrame - config.mouseDownAnimateReleasedFrame) * accelerateRate;
                }
                config.mouseDownAnimateLogicFrame = unitLogicFrame / speed;
                if (config.mouseDownAnimateLogicFrame > 1) {
                    // The `config.paint` is implicitly called here.
                    this.cleanUpAnimationUnit(config);
                    config.cleanedUp = true;
                }
            }
            for (let i = 0; i < this.canvasList.length; i++) {
                const config = this.canvasList[i];
                config.currentFrameId = frame;
                if (config.cleanedUp) {
                    config.cleanedUp = false;
                }
                else if (this.mouseInBoundary) {
                    config.paint(skipSamePointerPositionCheck);
                }
                else if (this.animationQueue.indexOf(config) === -1) {
                    config.clear();
                }
                else {
                    config.paint(skipSamePointerPositionCheck);
                }
            }
            this.paintedClientX = this.clientX;
            this.paintedClientY = this.clientY;
            if (this.mouseInBoundary || this.animationQueue.length !== 0) {
                this.requestPaintAll();
            }
            else {
                this.animationFrame = null;
            }
        };
        /**
         * Clear all canvas.
         */
        this.clearAll = () => {
            this.cleanupAnimation(true);
            for (let i = 0; i < this.canvasList.length; i++) {
                const config = this.canvasList[i];
                config.clear();
            }
        };
        /**
         * This is the effect while user pressed on the reveal highlight,
         * the ripple start to spread slowly.
         */
        this.initializeAnimation = () => {
            const config = this.canvasList.find((x) => x.mouseInCanvas());
            if (!config)
                return;
            if (!this.animationQueue.includes(config)) {
                this.animationQueue.push(config);
            }
            config.mouseDownAnimateStartFrame = null;
            config.mousePressed = true;
            config.mouseReleased = false;
        };
        /**
         * This is the effect while user released it's pointer, the spreading
         * ripple animation will start to accelerate.
         */
        this.switchAnimation = () => {
            for (let i = 0; i < this.animationQueue.length; i++) {
                const config = this.animationQueue[i];
                if (!config.mouseReleased) {
                    config.mouseReleased = true;
                    config.mouseDownAnimateReleasedFrame = config.mouseDownAnimateCurrentFrame;
                    config.mouseUpClientX = this.clientX;
                    config.mouseUpClientY = this.clientY;
                }
            }
        };
        /**
         * Cleanup all animations.
         * @param skipPaint Only cleanup data or also clean up the canvas
         * @param forceClean Clean the canvas or not
         */
        this.cleanupAnimation = (skipPaint, forceClean) => {
            for (let i = 0; i < this.animationQueue.length; i++) {
                const config = this.animationQueue[i];
                this.cleanUpAnimationUnit(config, skipPaint, forceClean);
            }
        };
        /**
         * Clean up the config of an animation, but the canvas won't cleaned up.
         * @param config The config to be cleaned up.
         */
        this.cleanUpAnimationUnit = (config, skipPaint, forceClean) => {
            config.mouseUpClientX = null;
            config.mouseUpClientY = null;
            config.mouseDownAnimateStartFrame = null;
            config.mouseDownAnimateCurrentFrame = null;
            config.mouseDownAnimateReleasedFrame = null;
            config.mouseDownAnimateLogicFrame = null;
            config.mousePressed = false;
            config.mouseReleased = false;
            const configIdx = this.animationQueue.indexOf(config);
            if (configIdx > -1) {
                this.animationQueue.splice(configIdx, 1);
            }
            if (!skipPaint) {
                if (forceClean) {
                    config.clear();
                }
                else if (this.mouseInBoundary) {
                    config.paint(true);
                }
                else {
                    config.clear();
                }
            }
        };
        this.getRevealAnimationConfig = () => {
            return this.canvasList.find((x) => x.mouseInCanvas()) || null;
        };
        this.id = id;
        this.container = $el;
    }
}
//# sourceMappingURL=RevealBoundryStore.js.map