/**
 * @license
 * Copyright Tyler Technologies, Inc. 
 * License: Apache-2.0
 */
import { getActiveElement, isNumber, isString } from '@tylertech/forge-core';
// eslint-disable-next-line @typescript-eslint/explicit-function-return-type
export function WithMoveable(base = class {
}) {
    class Moveable extends base {
        constructor() {
            super(...arguments);
            this._isMoving = false;
            this._activeElement = undefined;
            this._movePointerDownListener = this._onMoveTargetPointerDown.bind(this);
            this._movePointerMoveListener = this._onMoveTargetPointerMove.bind(this);
            this._movePointerUpListener = this._onMoveTargetPointerUp.bind(this);
        }
        stopMoveListener() {
            this._updatePosition(null, null);
            document.removeEventListener('pointermove', this._movePointerMoveListener);
            document.removeEventListener('pointerup', this._movePointerUpListener);
            this._isMoving = false;
            this._moveContext = undefined;
            this._lastPosition = undefined;
            this._activeElement = undefined;
        }
        _onMoveTargetPointerDown(evt) {
            evt.preventDefault();
            this._captureActiveElement();
            const bounds = this._getMoveableBounds();
            this._moveContext = {
                top: evt.pageY - bounds.top,
                left: evt.pageX - bounds.left,
                height: bounds.height,
                width: bounds.width
            };
            document.addEventListener('pointermove', this._movePointerMoveListener);
            document.addEventListener('pointerup', this._movePointerUpListener);
        }
        _onMoveTargetPointerMove(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 defaultPrevented = this._onMoveStart();
                if (defaultPrevented) {
                    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 defaultPrevented = this._onMove(newPosition);
                if (!defaultPrevented) {
                    this._lastPosition = Object.assign({}, newPosition);
                    const newX = this._normalizePositionValue(newPosition.x);
                    const newY = this._normalizePositionValue(newPosition.y);
                    this._updatePosition(newX, newY);
                }
            }
        }
        _onMoveTargetPointerUp(_evt) {
            if (this._isMoving) {
                this._onMoveEnd();
            }
            this._moveComplete();
        }
        _moveComplete() {
            this._tryRestoreActiveElement();
            document.removeEventListener('pointermove', this._movePointerMoveListener);
            document.removeEventListener('pointerup', this._movePointerUpListener);
            this._lastPosition = undefined;
            this._moveContext = undefined;
            this._isMoving = false;
        }
        _captureActiveElement() {
            var _a;
            this._activeElement = getActiveElement();
            (_a = this._activeElement) === null || _a === void 0 ? void 0 : _a.blur();
        }
        _tryRestoreActiveElement() {
            var _a;
            if ((_a = this._activeElement) === null || _a === void 0 ? void 0 : _a.isConnected) {
                this._activeElement.focus();
            }
            this._activeElement = undefined;
        }
        _normalizePositionValue(value) {
            if (isNumber(value)) {
                return `${value}px`;
            }
            else if (isString(value)) {
                return value;
            }
            return null;
        }
        _calculateOffsetPosition(pageX, pageY, context) {
            var _a, _b;
            return {
                x: pageX - ((_a = context === null || context === void 0 ? void 0 : context.left) !== null && _a !== void 0 ? _a : 0),
                y: pageY - ((_b = context === null || context === void 0 ? void 0 : context.top) !== null && _b !== void 0 ? _b : 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 };
        }
    }
    return Moveable;
}
