/**
 * @license
 * Copyright 2023 Tyler Technologies, Inc. 
 * License: Apache-2.0
 */
import { getEventPath } from '@tylertech/forge-core';
import { BOTTOM_SHEET_CONSTANTS } from './bottom-sheet-constants';
export class BottomSheetFoundation {
    constructor(_adapter) {
        this._adapter = _adapter;
        this._open = false;
        this._backdropClose = true;
        this._escapeClose = true;
        this._fullscreen = false;
        this._showBackdrop = false;
        this._isDragging = false;
        this._bodyScrollHandler = () => this._onBodyScroll();
        this._dragStartHandler = ($event) => this._onDragStart($event);
        this._dragMoveHandler = ($event) => this._onDragMove($event);
        this._dragEndHandler = () => this._onDragEnd();
        this._dragCancelHandler = () => this._onDragCancel();
        this._openTransitionEndHandler = (_evt) => this._onOpenTransitionEnd();
        this._closeTransitionEndHandler = (_evt) => this._onCloseTransitionEnd();
        this._documentKeydownHandler = (evt) => this._onDocumentKeydown(evt);
        this._backdropClickHandler = (evt) => this._onBackdropClick(evt);
    }
    initialize() {
        this._adapter.initializeAccessibility();
    }
    destroy() {
        if (this._open) {
            this._adapter.deregisterTransitionEndHandler(this._openTransitionEndHandler);
            this._adapter.removeDocumentListener('keydown', this._documentKeydownHandler);
            this._adapter.deregisterBackdropClickHandler(this._backdropClickHandler);
        }
    }
    _setOpen(open) {
        this._open = open;
        if (this._open) {
            this._openBottomSheet();
        }
        else {
            this._closeBottomSheet();
        }
        this._adapter.toggleHostAttribute(BOTTOM_SHEET_CONSTANTS.attributes.OPEN, this._open);
    }
    _openBottomSheet() {
        this._adapter.attach();
        this._adapter.setBodyAttribute(BOTTOM_SHEET_CONSTANTS.attributes.OPEN, 'true');
        this._adapter.registerTransitionEndHandler(this._openTransitionEndHandler);
        this._setDocumentKeydownListener(this._escapeClose);
        this._setBackdropClickListener(this._backdropClose);
        // Ensure transitions are triggered properly
        window.requestAnimationFrame(() => {
            window.requestAnimationFrame(() => {
                this._adapter.setVisibility(true);
                this._adapter.emitHostEvent(BOTTOM_SHEET_CONSTANTS.events.OPEN);
                this._adapter.trySetInitialFocus();
                this._adapter.initScrollable();
            });
        });
    }
    _closeBottomSheet() {
        this._adapter.deregisterTransitionEndHandler(this._openTransitionEndHandler);
        this._adapter.removeDocumentListener('keydown', this._documentKeydownHandler);
        this._adapter.deregisterBackdropClickHandler(this._backdropClickHandler);
        this._adapter.registerTransitionEndHandler(this._closeTransitionEndHandler);
        this._adapter.setVisibility(false);
    }
    _onCloseTransitionEnd() {
        this._adapter.emitHostEvent(BOTTOM_SHEET_CONSTANTS.events.CLOSE);
        this._adapter.detach();
        const openBottomSheets = this._adapter.getOpenBottomSheets(BOTTOM_SHEET_CONSTANTS.elementName);
        if (!openBottomSheets.length) {
            this._adapter.removeBodyAttribute(BOTTOM_SHEET_CONSTANTS.attributes.OPEN);
        }
    }
    _onOpenTransitionEnd() {
        if (!this._open) {
            return;
        }
        this._adapter.deregisterTransitionEndHandler(this._openTransitionEndHandler);
        this._adapter.emitHostEvent(BOTTOM_SHEET_CONSTANTS.events.READY);
        this._adapter.tryLayoutChildren();
        if (this._adapter.isScrollable()) {
            this._initScrollableHandlers();
        }
    }
    _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(BOTTOM_SHEET_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());
            if (shouldClose) {
                this.open = false;
                return;
            }
        }
        catch (err) {
            return;
        }
    }
    _setBackdropClickListener(attach) {
        if (!this._open) {
            return;
        }
        if (attach) {
            this._adapter.registerBackdropClickHandler(this._backdropClickHandler);
        }
        else {
            this._adapter.deregisterBackdropClickHandler(this._backdropClickHandler);
        }
    }
    _setDocumentKeydownListener(attach) {
        if (!this._open) {
            return;
        }
        if (attach) {
            this._adapter.addDocumentListener('keydown', this._documentKeydownHandler);
        }
        else {
            this._adapter.removeDocumentListener('keydown', this._documentKeydownHandler);
        }
    }
    /** Controls whether clicking the backdrop closes the bottom sheet or not. */
    set backdropClose(value) {
        value = Boolean(value);
        if (this._backdropClose !== value) {
            this._backdropClose = value;
            this._setBackdropClickListener(this._backdropClose);
            this._adapter.toggleHostAttribute(BOTTOM_SHEET_CONSTANTS.attributes.BACKDROP_CLOSE, this._backdropClose);
        }
    }
    get backdropClose() {
        return this._backdropClose;
    }
    /** Controls whether pressing the escape key closes the sheet or not. */
    set escapeClose(value) {
        value = Boolean(value);
        if (this._escapeClose !== value) {
            this._escapeClose = value;
            this._setDocumentKeydownListener(this._escapeClose);
            this._adapter.toggleHostAttribute(BOTTOM_SHEET_CONSTANTS.attributes.ESCAPE_CLOSE, this._escapeClose);
        }
    }
    get escapeClose() {
        return this._escapeClose;
    }
    get open() {
        return this._open;
    }
    set open(value) {
        if (this._open !== value) {
            value = Boolean(value);
            if (value !== this._open) {
                const callback = value ? this._openCallback : this._closeCallback;
                if (callback) {
                    Promise.resolve(callback())
                        .then(() => {
                        this._setOpen(value);
                    })
                        .catch(() => { });
                }
                else {
                    this._setOpen(value);
                }
            }
        }
    }
    get fullscreen() {
        return this._fullscreen;
    }
    set fullscreen(value) {
        if (this._fullscreen !== value) {
            this._fullscreen = value;
            this._adapter.setFullscreen(this._fullscreen);
            this._adapter.toggleHostAttribute(BOTTOM_SHEET_CONSTANTS.attributes.FULLSCREEN, this._fullscreen);
        }
    }
    get showBackdrop() {
        return this._showBackdrop;
    }
    set showBackdrop(value) {
        if (this._showBackdrop !== value) {
            this._showBackdrop = value;
            this._adapter.setBackdropVisiblity(value);
            this._adapter.toggleHostAttribute(BOTTOM_SHEET_CONSTANTS.attributes.SHOW_BACKDROP, this._showBackdrop);
        }
    }
    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;
    }
    /** If scrollable, allow drag to/from fullscreen, and make fullscreen on scroll. */
    _initScrollableHandlers() {
        if (!this._fullscreen && this._adapter.isScrollable()) {
            this._adapter.setDragTargetHandler('mousedown', this._dragStartHandler);
            this._adapter.setDragTargetHandler('touchstart', this._dragStartHandler);
            this._adapter.setBodyScrollHandler(this._bodyScrollHandler);
        }
    }
    _onBodyScroll() {
        if (!this._fullscreen && !this._isDragging) {
            this.fullscreen = true;
            this._adapter.emitHostEvent(BOTTOM_SHEET_CONSTANTS.events.FULLSCREEN, this._fullscreen);
        }
    }
    _onDragStart(evt) {
        if (this._fullscreen) {
            const eventPath = getEventPath(evt);
            const isWithinScrollContainer = this._adapter.isScrollable() && this._adapter.isContentChild(eventPath[0]);
            if (isWithinScrollContainer) {
                // We ignore drag events that originate from within the scrollable content when in full screen mode (to allow for scroll interactions)
                return;
            }
        }
        evt.stopPropagation();
        const bounds = this._adapter.getContainerBounds();
        const clientY = evt instanceof MouseEvent ? evt.clientY : evt.touches[0].clientY;
        this._dragContext = {
            top: clientY - bounds.top,
            height: bounds.height
        };
        this._adapter.setBodyListener('mousemove', this._dragMoveHandler, { passive: false });
        this._adapter.setBodyListener('touchmove', this._dragMoveHandler, { passive: false });
        this._adapter.setBodyListener('mouseup', this._dragEndHandler);
        this._adapter.setBodyListener('touchend', this._dragEndHandler);
        this._adapter.setBodyListener('touchcancel', this._dragCancelHandler);
    }
    _onDragMove(evt) {
        evt.stopPropagation();
        evt.preventDefault();
        if (!this._dragContext) {
            return;
        }
        const clientY = evt instanceof MouseEvent ? evt.clientY : evt.touches[0].clientY;
        const height = { y: window.innerHeight - clientY + this._dragContext.top };
        // 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._isDragging) {
            this._isDragging = true;
            const canDrag = this._adapter.emitHostEvent(BOTTOM_SHEET_CONSTANTS.events.DRAG_START, height, true, true);
            if (!canDrag) {
                return;
            }
            this._adapter.setDragging(true);
        }
        // If not fullscreen, clamp to minimum of original height.
        const newPosition = this._fullscreen ? height : { y: Math.max(height.y, this._dragContext.height) };
        // Only update the position if it actually changed
        if (!this._lastPosition || newPosition.y !== this._lastPosition.y) {
            const canMove = this._adapter.emitHostEvent(BOTTOM_SHEET_CONSTANTS.events.DRAGGED, newPosition, true, true);
            if (canMove) {
                this._lastPosition = Object.assign(Object.assign({}, newPosition), { clientY });
                this._adapter.setContainerHeight(newPosition.y);
            }
        }
    }
    _onDragEnd() {
        if (this._lastPosition) {
            const clientY = this._lastPosition.clientY;
            const dragContext = this._dragContext;
            if (dragContext) {
                if (this._fullscreen && clientY > 0) {
                    this.fullscreen = false;
                    this._adapter.emitHostEvent(BOTTOM_SHEET_CONSTANTS.events.FULLSCREEN, this._fullscreen);
                }
                else if (!this._fullscreen && clientY < dragContext.height + dragContext.top) {
                    this.fullscreen = true;
                    this._adapter.emitHostEvent(BOTTOM_SHEET_CONSTANTS.events.FULLSCREEN, this._fullscreen);
                }
            }
        }
        if (this._isDragging) {
            this._adapter.emitHostEvent(BOTTOM_SHEET_CONSTANTS.events.DRAG_END);
        }
        this._dragComplete();
    }
    _onDragCancel() {
        if (this._isDragging) {
            this._adapter.emitHostEvent(BOTTOM_SHEET_CONSTANTS.events.DRAG_CANCEL);
        }
        this._dragComplete();
    }
    _dragComplete() {
        this._adapter.setDragging(false);
        this._adapter.removeBodyListener('mousemove', this._dragMoveHandler);
        this._adapter.removeBodyListener('touchmove', this._dragMoveHandler);
        this._adapter.removeBodyListener('mouseup', this._dragEndHandler);
        this._adapter.removeBodyListener('touchend', this._dragEndHandler);
        this._adapter.removeBodyListener('touchcancel', this._dragCancelHandler);
        this._adapter.setContainerHeight(null);
        this._lastPosition = undefined;
        this._dragContext = undefined;
        this._isDragging = false;
    }
}
