/**
 * @license
 * Copyright 2023 Tyler Technologies, Inc. 
 * License: Apache-2.0
 */
import { isDefined, isNumber, isString } from '@tylertech/forge-core';
import { DIALOG_CONSTANTS } from './dialog-constants';
export class DialogFoundation {
    constructor(_adapter) {
        this._adapter = _adapter;
        // Private vars
        this._open = false;
        this._backdropClose = true;
        this._escapeClose = true;
        this._fullscreen = false;
        this._positionType = 'absolute';
        this._positionX = null;
        this._positionY = null;
        this._moveable = false;
        this._moveTarget = DIALOG_CONSTANTS.selectors.DFEAULT_MOVE_TARGET;
        this._isAnimating = false;
        this._isMoving = false;
        // Event handler states
        this._attachedDocumentKeydownHandler = false;
        this._attachedBackdropClickHandler = false;
        this._transitionEndHandler = (evt) => this._onTransitionEnd();
        this._documentKeydownHandler = (evt) => this._onDocumentKeydown(evt);
        this._backdropClickHandler = (evt) => this._onBackdropClick(evt);
        this._moveTargetMouseDownHandler = (evt) => this._onMoveTargetMouseDown(evt);
        this._moveTargetMouseMoveHandler = (evt) => this._onMoveTargetMouseMove(evt);
        this._moveTargetMouseUpHandler = (evt) => this._onMoveTargetMouseUp(evt);
    }
    initialize() {
        this._adapter.initializeAccessibility();
        if (this._open) {
            if (this._moveable) {
                this._adapter.setMoveable(this._moveable);
                this._initMoveTarget();
            }
        }
    }
    destroy() {
        if (this._open) {
            this._removeDragHandlers();
            this._adapter.deregisterTransitionEndHandler(this._transitionEndHandler);
            if (this._attachedDocumentKeydownHandler) {
                this._adapter.removeDocumentListener('keydown', this._documentKeydownHandler);
            }
            if (this._attachedBackdropClickHandler) {
                this._adapter.deregisterBackdropClickHandler(this._backdropClickHandler);
            }
        }
    }
    async _applyOpen(value) {
        if (value) {
            if (typeof this._openCallback === 'function' && !await this._executeOpenCallback()) {
                return;
            }
            this._open = value;
            this._openDialog();
        }
        else {
            if (typeof this._closeCallback === 'function' && !await this._executeCloseCallback()) {
                return;
            }
            this._open = value;
            this._closeDialog();
        }
        this._adapter.toggleHostAttribute(DIALOG_CONSTANTS.attributes.OPEN, this._open);
    }
    async _executeOpenCallback() {
        try {
            return await Promise.resolve(this._openCallback()) !== false;
        }
        catch (e) {
            return false;
        }
    }
    async _executeCloseCallback() {
        try {
            return await Promise.resolve(this._closeCallback()) !== false;
        }
        catch (e) {
            return false;
        }
    }
    initializeMoveTarget() {
        if (!this._moveTarget) {
            this._moveTarget = DIALOG_CONSTANTS.selectors.DFEAULT_MOVE_TARGET;
        }
        this._initMoveTarget();
    }
    resetPosition() {
        this._adapter.setSurfacePosition(this._positionX, this._positionY, this._positionType);
    }
    _normalizePositionValue(value) {
        if (isNumber(value)) {
            return `${value}px`;
        }
        else if (isString(value)) {
            return value;
        }
        return null;
    }
    _openDialog() {
        this._adapter.attach();
        if (!this._fullscreen) {
            if (this._moveable) {
                this._adapter.setMoveable(this._moveable);
            }
            if (this._positionX != null || this._positionY != null) {
                this._adapter.setSurfacePosition(this._positionX, this._positionY, this._positionType);
            }
        }
        this._adapter.setBodyAttribute(DIALOG_CONSTANTS.attributes.OPEN, 'true');
        this._adapter.registerTransitionEndHandler(this._transitionEndHandler);
        this._setDocumentKeydownListener(this._escapeClose);
        this._setBackdropClickListener(this._backdropClose);
        this._adapter.setAnimating(true);
        this._isAnimating = true;
        // Ensure transitions are triggered properly
        window.requestAnimationFrame(() => {
            window.requestAnimationFrame(() => {
                this._adapter.setVisibility(true);
                this._adapter.emitHostEvent(DIALOG_CONSTANTS.events.OPEN);
                this._adapter.trySetInitialFocus();
                if (this._adapter.isScrollable()) {
                    this._adapter.addRootClass(DIALOG_CONSTANTS.classes.SCROLLABLE);
                }
            });
        });
        // Wait for the dialog to finish animating open, then emit the ready event and attach any listeners
        setTimeout(() => {
            window.requestAnimationFrame(() => {
                if (this._open && this._isAnimating) {
                    this._onTransitionEnd();
                }
            });
        }, DIALOG_CONSTANTS.numbers.ANIMATION_DURATION);
    }
    _closeDialog() {
        if (this._moveTarget) {
            this._removeDragHandlers();
        }
        this._adapter.deregisterTransitionEndHandler(this._transitionEndHandler);
        if (this._attachedDocumentKeydownHandler) {
            this._adapter.removeDocumentListener('keydown', this._documentKeydownHandler);
        }
        if (this._attachedBackdropClickHandler) {
            this._adapter.deregisterBackdropClickHandler(this._backdropClickHandler);
        }
        this._isAnimating = false;
        this._adapter.setAnimating(true);
        this._adapter.setVisibility(false);
        setTimeout(() => {
            this._adapter.emitHostEvent(DIALOG_CONSTANTS.events.CLOSE);
            this._adapter.detach();
            const openDialogs = this._adapter.getOpenDialogs(DIALOG_CONSTANTS.elementName);
            if (!openDialogs.length) {
                this._adapter.removeBodyAttribute(DIALOG_CONSTANTS.attributes.OPEN);
            }
        }, DIALOG_CONSTANTS.numbers.ANIMATION_DURATION);
    }
    _onTransitionEnd() {
        if (!this._isAnimating) {
            return;
        }
        this._adapter.deregisterTransitionEndHandler(this._transitionEndHandler);
        this._adapter.setAnimating(false);
        this._adapter.emitHostEvent(DIALOG_CONSTANTS.events.READY);
        this._adapter.tryLayoutRippleChildren();
        if (this._moveable) {
            this._initMoveTarget();
        }
        this._isAnimating = false;
    }
    _initMoveTarget() {
        if (!this._fullscreen && this._moveable && this._moveTarget && this._adapter.setMoveTarget(this._moveTarget)) {
            this._adapter.setMoveTargetHandler('mousedown', this._moveTargetMouseDownHandler);
        }
    }
    _removeDragHandlers() {
        this._adapter.removeDragTargetHandler('mousedown', this._moveTargetMouseDownHandler);
        this._adapter.removeDocumentListener('mousemove', this._moveTargetMouseMoveHandler);
        this._adapter.removeDocumentListener('mouseup', this._moveTargetMouseUpHandler);
    }
    _onMoveTargetMouseDown(evt) {
        evt.preventDefault();
        this._adapter.captureActiveElement();
        const bounds = this._adapter.getSurfaceBounds();
        this._moveContext = {
            top: evt.pageY - bounds.top,
            left: evt.pageX - bounds.left,
            height: bounds.height,
            width: bounds.width
        };
        this._adapter.setDocumentListener('mousemove', this._moveTargetMouseMoveHandler);
        this._adapter.setDocumentListener('mouseup', this._moveTargetMouseUpHandler);
    }
    _onMoveTargetMouseMove(evt) {
        evt.preventDefault();
        const position = this._calculateOffsetPosition(evt.pageX, evt.pageY, this._moveContext);
        // If this is the beginning of the move sequence, we emit the start event (to allow for preventing default) and
        // then update the surface position if not prevented
        if (!this._isMoving) {
            this._isMoving = true;
            const canDrag = this._adapter.emitHostEvent(DIALOG_CONSTANTS.events.MOVE_START, position, true, true);
            if (!canDrag) {
                return;
            }
        }
        // Ensure that the surface position stays within the bounds of the screen
        const newPosition = this._clampPosition(position, this._moveContext);
        // Only update the position if it actually changed
        if (!this._lastPosition || newPosition.x !== this._lastPosition.x || newPosition.y !== this._lastPosition.y) {
            const canMove = this._adapter.emitHostEvent(DIALOG_CONSTANTS.events.MOVED, newPosition, true, true);
            if (canMove) {
                this._lastPosition = Object.assign({}, newPosition);
                const newX = this._normalizePositionValue(newPosition.x);
                const newY = this._normalizePositionValue(newPosition.y);
                this._adapter.setSurfacePosition(newX, newY, 'absolute');
            }
        }
    }
    _onMoveTargetMouseUp(evt) {
        if (this._isMoving) {
            this._adapter.emitHostEvent(DIALOG_CONSTANTS.events.MOVE_END);
        }
        this._moveComplete();
    }
    _moveComplete() {
        this._adapter.tryRestoreActiveElement();
        this._adapter.removeDocumentListener('mousemove', this._moveTargetMouseMoveHandler);
        this._adapter.removeDocumentListener('mouseup', this._moveTargetMouseUpHandler);
        this._lastPosition = undefined;
        this._moveContext = undefined;
        this._isMoving = false;
    }
    _onDocumentKeydown(evt) {
        evt.stopPropagation();
        if (evt.key && (evt.key === 'Escape' || evt.key === 'Esc')) {
            this._tryClose();
        }
    }
    _onBackdropClick(evt) {
        evt.stopPropagation();
        this._tryClose();
    }
    async _tryClose() {
        const isCancelled = !this._adapter.emitHostEvent(DIALOG_CONSTANTS.events.BEFORE_CLOSE, undefined, undefined, true);
        if (isCancelled) {
            return;
        }
        if (!this._beforeCloseCallback) {
            this.open = false;
            return;
        }
        try {
            const shouldClose = await Promise.resolve(this._beforeCloseCallback()) !== false;
            if (shouldClose) {
                this.open = false;
                return;
            }
        }
        catch (err) {
            return;
        }
    }
    _setBackdropClickListener(attach) {
        if (!this._open) {
            return;
        }
        if (attach && !this._attachedBackdropClickHandler) {
            this._adapter.registerBackdropClickHandler(this._backdropClickHandler);
            this._attachedBackdropClickHandler = true;
        }
        else if (!attach && this._attachedBackdropClickHandler) {
            this._adapter.deregisterBackdropClickHandler(this._backdropClickHandler);
            this._attachedBackdropClickHandler = false;
        }
    }
    _setDocumentKeydownListener(attach) {
        if (!this._open) {
            return;
        }
        if (attach && !this._attachedDocumentKeydownHandler) {
            this._adapter.setDocumentListener('keydown', this._documentKeydownHandler);
            this._attachedDocumentKeydownHandler = true;
        }
        else if (!attach && this._attachedDocumentKeydownHandler) {
            this._adapter.removeDocumentListener('keydown', this._documentKeydownHandler);
            this._attachedDocumentKeydownHandler = false;
        }
    }
    _calculateOffsetPosition(pageX, pageY, context) {
        return {
            x: pageX - ((context === null || context === void 0 ? void 0 : context.left) || 0),
            y: pageY - ((context === null || context === void 0 ? void 0 : context.top) || 0)
        };
    }
    _clampPosition({ x, y }, context) {
        let width = 0;
        let height = 0;
        if (context) {
            width = context.width;
            height = context.height;
        }
        if (x <= 0) {
            x = 0;
        }
        else if (x + width >= window.innerWidth) {
            x = window.innerWidth - width;
        }
        if (y <= 0) {
            y = 0;
        }
        else if (y + height >= window.innerHeight) {
            y = window.innerHeight - height;
        }
        return { x, y };
    }
    /** Controls whether clicking the backdrop closes the dialog or not. */
    set backdropClose(value) {
        value = Boolean(value);
        if (this._backdropClose !== value) {
            this._backdropClose = value;
            this._setBackdropClickListener(this._backdropClose);
            if (isDefined(this._backdropClose)) {
                this._adapter.setHostAttribute(DIALOG_CONSTANTS.attributes.BACKDROP_CLOSE, this._backdropClose.toString());
            }
            else {
                this._adapter.removeHostAttribute(DIALOG_CONSTANTS.attributes.BACKDROP_CLOSE);
            }
        }
    }
    get backdropClose() {
        return this._backdropClose;
    }
    /** Controls whether pressing the escape key closes the dialog or not. */
    set escapeClose(value) {
        value = Boolean(value);
        if (this._escapeClose !== value) {
            this._escapeClose = value;
            this._setDocumentKeydownListener(this._escapeClose);
            if (isDefined(this._escapeClose)) {
                this._adapter.setHostAttribute(DIALOG_CONSTANTS.attributes.ESCAPE_CLOSE, this._escapeClose.toString());
            }
            else {
                this._adapter.removeHostAttribute(DIALOG_CONSTANTS.attributes.ESCAPE_CLOSE);
            }
        }
    }
    get escapeClose() {
        return this._escapeClose;
    }
    get open() {
        return this._open;
    }
    set open(value) {
        if (this._open !== value) {
            value = Boolean(value);
            if (value !== this._open) {
                this._applyOpen(value);
            }
        }
    }
    get fullscreen() {
        return this._fullscreen;
    }
    set fullscreen(value) {
        if (this._fullscreen !== value) {
            this._fullscreen = !!value;
            this._adapter.setFullscreen(this._fullscreen);
            this._adapter.setHostAttribute(DIALOG_CONSTANTS.attributes.FULLSCREEN, String(this._fullscreen));
        }
    }
    get openCallback() {
        return this._openCallback;
    }
    set openCallback(callback) {
        this._openCallback = callback;
    }
    get closeCallback() {
        return this._closeCallback;
    }
    set closeCallback(callback) {
        this._closeCallback = callback;
    }
    get beforeCloseCallback() {
        return this._beforeCloseCallback;
    }
    set beforeCloseCallback(callback) {
        this._beforeCloseCallback = callback;
    }
    get positionType() {
        return this._positionType;
    }
    set positionType(value) {
        if (this._positionType !== value) {
            this._positionType = value;
            if (this._open) {
                this._adapter.setSurfacePosition(this._positionX, this._positionY, this._positionType);
            }
        }
    }
    get positionX() {
        return this._positionX;
    }
    set positionX(value) {
        if (this._positionX !== value) {
            this._positionX = this._normalizePositionValue(value);
            if (this._open) {
                this._adapter.setSurfacePosition(this._positionX, this._positionY, this._positionType);
            }
        }
    }
    get positionY() {
        return this._positionY;
    }
    set positionY(value) {
        if (this._positionY !== value) {
            this._positionY = this._normalizePositionValue(value);
            if (this._open) {
                this._adapter.setSurfacePosition(this._positionX, this._positionY, this._positionType);
            }
        }
    }
    get moveable() {
        return this._moveable;
    }
    set moveable(value) {
        if (this._moveable !== value) {
            this._moveable = value;
            if (this._open) {
                this._adapter.setMoveable(this._moveable);
                if (this._moveable) {
                    this._initMoveTarget();
                }
                else {
                    this._removeDragHandlers();
                    this._isMoving = false;
                }
            }
        }
    }
    get moveTarget() {
        return this._moveTarget;
    }
    set moveTarget(value) {
        this._moveTarget = value;
    }
}
