/**
 * @license
 * Copyright Tyler Technologies, Inc. 
 * License: Apache-2.0
 */
import { getEventPath } from '@tylertech/forge-core';
import { DIALOG_CONSTANTS } from '../dialog';
import { BOTTOM_SHEET_CONSTANTS } from './bottom-sheet-constants';
export class BottomSheetFoundation {
    constructor(_adapter) {
        this._adapter = _adapter;
        this._open = false;
        this._mode = 'nonmodal';
        this._persistent = false;
        this._fullscreen = false;
        this._isDragging = false;
        this._bodyScrollHandler = () => this._onBodyScroll();
        this._dragStartHandler = this._onDragStart.bind(this);
        this._dragMoveHandler = this._onDragMove.bind(this);
        this._dragEndHandler = this._onDragEnd.bind(this);
        this._dragCancelHandler = this._onDragCancel.bind(this);
        this._dialogDismissListener = this._onDialogDismiss.bind(this);
        this._dialogBeforeCloseListener = this._onDialogBeforeClose.bind(this);
    }
    initialize() {
        this._adapter.initialize();
        if (this._open) {
            this._openBottomSheet();
        }
    }
    _openBottomSheet() {
        if (this._fullscreen) {
            this._adapter.setFullscreen(true);
        }
        this._adapter.addDialogListener(DIALOG_CONSTANTS.events.BEFORE_CLOSE, this._dialogBeforeCloseListener);
        this._adapter.addDialogListener(DIALOG_CONSTANTS.events.CLOSE, this._dialogDismissListener);
        this._adapter.open();
        if (this._adapter.isScrollable()) {
            this._initScrollableHandlers();
        }
        this._adapter.trySetInitialFocus();
        this._adapter.setBodyAttribute(BOTTOM_SHEET_CONSTANTS.attributes.OPEN, 'true');
        this._adapter.dispatchHostEvent(new CustomEvent(BOTTOM_SHEET_CONSTANTS.events.OPEN, { bubbles: true }));
    }
    _closeBottomSheet() {
        this._adapter.removeDialogListener(DIALOG_CONSTANTS.events.BEFORE_CLOSE, this._dialogBeforeCloseListener);
        this._adapter.removeDialogListener(DIALOG_CONSTANTS.events.CLOSE, this._dialogDismissListener);
        this._adapter.close();
        this._adapter.dispatchHostEvent(new CustomEvent(BOTTOM_SHEET_CONSTANTS.events.CLOSE, { bubbles: true }));
        this._open = false;
        this._adapter.setFullscreen(false);
    }
    _onDialogDismiss() {
        this._closeBottomSheet();
    }
    _onDialogBeforeClose(evt) {
        const beforeCloseEvent = new CustomEvent(BOTTOM_SHEET_CONSTANTS.events.BEFORE_CLOSE, { bubbles: true, cancelable: true });
        this._adapter.dispatchHostEvent(beforeCloseEvent);
        if (beforeCloseEvent.defaultPrevented) {
            evt.preventDefault();
        }
    }
    /** If scrollable, allow drag to/from fullscreen, and make fullscreen on scroll. */
    _initScrollableHandlers() {
        if (!this._adapter.isFullscreen() && this._adapter.isScrollable()) {
            this._adapter.setDragTargetHandler('mousedown', this._dragStartHandler);
            this._adapter.setDragTargetHandler('touchstart', this._dragStartHandler);
            this._adapter.setBodyScrollHandler(this._bodyScrollHandler);
        }
    }
    _onBodyScroll() {
        if (!this._adapter.isFullscreen() && !this._isDragging) {
            this._adapter.setFullscreen(true);
            this._adapter.dispatchHostEvent(new CustomEvent(BOTTOM_SHEET_CONSTANTS.events.FULLSCREEN, { bubbles: true, detail: true }));
        }
    }
    _onDragStart(evt) {
        if (this._adapter.isFullscreen()) {
            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 dragStartEvt = new CustomEvent(BOTTOM_SHEET_CONSTANTS.events.DRAG_START, { bubbles: true, detail: height, cancelable: true });
            this._adapter.dispatchHostEvent(dragStartEvt);
            if (dragStartEvt.defaultPrevented) {
                return;
            }
            this._adapter.setDragging(true);
        }
        // If not fullscreen, clamp to minimum of original height.
        const newPosition = this._adapter.isFullscreen() ? 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 dragMoveEvt = new CustomEvent(BOTTOM_SHEET_CONSTANTS.events.DRAGGED, { bubbles: true, detail: newPosition, cancelable: true });
            this._adapter.dispatchHostEvent(dragMoveEvt);
            if (dragMoveEvt.defaultPrevented) {
                return;
            }
            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) {
                const isFullscreen = this._adapter.isFullscreen();
                if (isFullscreen && clientY > 0) {
                    this._adapter.setFullscreen(false);
                }
                else if (!isFullscreen && clientY < dragContext.height + dragContext.top) {
                    this._adapter.setFullscreen(true);
                }
                const fullscreenEvt = new CustomEvent(BOTTOM_SHEET_CONSTANTS.events.FULLSCREEN, { bubbles: true, detail: isFullscreen });
                this._adapter.dispatchHostEvent(fullscreenEvt);
            }
        }
        if (this._isDragging) {
            const dragEndEvt = new CustomEvent(BOTTOM_SHEET_CONSTANTS.events.DRAG_END, { bubbles: true });
            this._adapter.dispatchHostEvent(dragEndEvt);
        }
        this._dragComplete();
    }
    _onDragCancel() {
        if (this._isDragging) {
            const dragCancelEvt = new CustomEvent(BOTTOM_SHEET_CONSTANTS.events.DRAG_CANCEL, { bubbles: true });
            this._adapter.dispatchHostEvent(dragCancelEvt);
        }
        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;
    }
    get open() {
        return this._open;
    }
    set open(value) {
        value = Boolean(value);
        if (this._open !== value) {
            this._open = value;
            if (this._adapter.isConnected) {
                if (this._open) {
                    this._openBottomSheet();
                }
                else {
                    this._closeBottomSheet();
                }
            }
            this._adapter.toggleHostAttribute(BOTTOM_SHEET_CONSTANTS.attributes.OPEN, value);
        }
    }
    get mode() {
        return this._mode;
    }
    set mode(value) {
        if (this._mode !== value) {
            this._mode = value;
            this._adapter.setDialogProperty('mode', this._mode);
        }
    }
    get persistent() {
        return this._persistent;
    }
    set persistent(value) {
        value = Boolean(value);
        if (this._persistent !== value) {
            this._persistent = value;
            this._adapter.setDialogProperty('persistent', this._persistent);
            this._adapter.toggleHostAttribute(BOTTOM_SHEET_CONSTANTS.attributes.PERSISTENT, this._persistent);
        }
    }
    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);
        }
    }
}
