var __extends = (this && this.__extends) || (function () {
    var extendStatics = function (d, b) {
        extendStatics = Object.setPrototypeOf ||
            ({ __proto__: [] } instanceof Array && function (d, b) { d.__proto__ = b; }) ||
            function (d, b) { for (var p in b) if (Object.prototype.hasOwnProperty.call(b, p)) d[p] = b[p]; };
        return extendStatics(d, b);
    };
    return function (d, b) {
        if (typeof b !== "function" && b !== null)
            throw new TypeError("Class extends value " + String(b) + " is not a constructor or null");
        extendStatics(d, b);
        function __() { this.constructor = d; }
        d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __());
    };
})();
var __assign = (this && this.__assign) || function () {
    __assign = Object.assign || function(t) {
        for (var s, i = 1, n = arguments.length; i < n; i++) {
            s = arguments[i];
            for (var p in s) if (Object.prototype.hasOwnProperty.call(s, p))
                t[p] = s[p];
        }
        return t;
    };
    return __assign.apply(this, arguments);
};
import { acdbHostApplicationServices, acdbMaskToOsnapModes, AcDbOsnapMode, AcDbSystemVariables, AcDbSysVarManager } from '@mlightcad/data-model';
import { AcApSettingManager } from '../../../app';
import { AcEdMarkerManager } from '../marker';
import { AcEdFloatingInputBoxes } from './AcEdFloatingInputBoxes';
import { AcEdFloatingMessage } from './AcEdFloatingMessage';
import { AcEdRubberBand } from './AcEdRubberBand';
/**
 * A UI component providing a small floating input box used inside CAD editing
 * workflows. It supports both single-input (distance, angle, etc.) and
 * double-input (coordinate entry) modes.
 *
 * The component is responsible for:
 *
 * - Creating, styling, and destroying its HTML structure
 * - Handling keyboard events (Enter, Escape)
 * - Managing live validation (via built-in or custom callback)
 * - Emitting commit/change/cancel events
 * - Ensuring no memory leaks via `dispose()`
 *
 * This abstraction allows higher-level objects such as AcEdInputManager to
 * remain clean and free from DOM-handling logic.
 */
var AcEdFloatingInput = /** @class */ (function (_super) {
    __extends(AcEdFloatingInput, _super);
    // ---------------------------------------------------------------------------
    // CONSTRUCTOR
    // ---------------------------------------------------------------------------
    /**
     * Constructs a new floating input widget with the given options.
     *
     * @param view - The view associated with the floating input
     * @param options Configuration object controlling behavior, callbacks,
     *                validation, and display mode.
     */
    function AcEdFloatingInput(view, options) {
        var _this = _super.call(this, view, options) || this;
        /** Stores last confirmed WCS point */
        _this.lastPoint = null;
        /** Whether to suppress UI display while keeping input active */
        _this.suppressDisplay = false;
        _this.allowPrompt = options.allowPrompt !== false;
        _this.suppressDisplay = !_this.isDynamicInputEnabled();
        // -----------------------------
        // OSNAP
        // -----------------------------
        if (!options.disableOSnap) {
            _this.osnapMarkerManager = new AcEdMarkerManager(view);
        }
        // -----------------------------
        // Rubber band
        // -----------------------------
        if (options.basePoint) {
            _this.rubberBand = new AcEdRubberBand(view);
            _this.rubberBand.start(options.basePoint, {
                color: 'var(--ml-ui-canvas-line, #0f0)',
                showBaseLineOnly: options.showBaseLineOnly,
                baseAngle: options.baseAngle
            });
        }
        // -----------------------------
        // Callbacks
        // -----------------------------
        _this.validateFn = options.validate;
        _this.getDynamicValue = options.getDynamicValue;
        _this.drawPreview = options.drawPreview;
        _this.onCommit = options.onCommit;
        _this.onChange = options.onChange;
        _this.onCancel = options.onCancel;
        _this.onNone = options.onNone;
        // -----------------------------
        // Input boxes
        // -----------------------------
        if (options.inputCount !== 0) {
            _this.inputs = new AcEdFloatingInputBoxes({
                parent: _this.container,
                twoInputs: options.inputCount === 2,
                validate: _this.validateFn,
                onCancel: _this.onCancel,
                onNone: _this.onNone,
                onCommit: _this.onCommit,
                onChange: _this.onChange,
                autoFocus: _this.isDynamicInputEnabled(),
                allowNone: options.allowNone,
                useDefaultValue: options.useDefaultValue,
                defaultValue: options.defaultValue
            });
        }
        // -----------------------------
        // Click commit
        // -----------------------------
        _this.boundOnClick = function (e) { return _this.handleClick(e); };
        _this.parent.addEventListener('click', _this.boundOnClick);
        // -----------------------------
        // Dynamic input settings listener
        // -----------------------------
        _this.boundOnInputSysVarChanged = function (args) {
            var _a;
            var name = (_a = args.name) === null || _a === void 0 ? void 0 : _a.toLowerCase();
            if (name === AcDbSystemVariables.DYNMODE.toLowerCase() ||
                name === AcDbSystemVariables.DYNPROMPT.toLowerCase()) {
                _this.updateDynamicInputDisplay();
            }
        };
        AcDbSysVarManager.instance().events.sysVarChanged.addEventListener(_this.boundOnInputSysVarChanged);
        _this.updateDynamicInputDisplay();
        _this.injectInputCSS();
        return _this;
    }
    AcEdFloatingInput.prototype.showAt = function (pos) {
        if (this.disposed)
            return;
        this.updateDynamicInputDisplay();
        if (!this.suppressDisplay) {
            _super.prototype.showAt.call(this, pos);
            // Seed preview so modifier toggles can refresh immediately.
            var wcsPos_1 = this.view.screenToWorld(this.view.viewportToCanvas(pos));
            this.updateDynamicPreview(wcsPos_1);
            return;
        }
        this.visible = true;
        this.container.style.display = 'none';
        this.setPosition(pos);
        this.parent.addEventListener('mousemove', this.boundOnMouseMove);
        var wcsPos = this.view.screenToWorld(this.view.viewportToCanvas(pos));
        this.updateDynamicPreview(wcsPos);
    };
    AcEdFloatingInput.prototype.injectInputCSS = function () {
        if (AcEdFloatingInput.inputStylesInjected)
            return;
        AcEdFloatingInput.inputStylesInjected = true;
        var style = document.createElement('style');
        style.textContent = "\n      .ml-floating-input input {\n        font-size: 12px;\n        padding: 2px 4px;\n        margin-left: 6px;\n        height: 22px;\n        width: 90px;\n        background: var(--ml-ui-bg, #888);\n        border: 1px solid var(--ml-ui-border, #666);\n        color: var(--ml-ui-text, #fff);\n        border-radius: 2px;\n      }\n  \n      .ml-floating-input input.invalid {\n        border-color: var(--ml-ui-danger, red);\n        color: var(--ml-ui-danger, red);\n      }\n    ";
        document.head.appendChild(style);
    };
    // ---------------------------------------------------------------------------
    // Overrides
    // ---------------------------------------------------------------------------
    AcEdFloatingInput.prototype.dispose = function () {
        var _a, _b, _c;
        if (this.disposed)
            return;
        _super.prototype.dispose.call(this);
        this.parent.removeEventListener('click', this.boundOnClick);
        AcDbSysVarManager.instance().events.sysVarChanged.removeEventListener(this.boundOnInputSysVarChanged);
        (_a = this.inputs) === null || _a === void 0 ? void 0 : _a.dispose();
        (_b = this.rubberBand) === null || _b === void 0 ? void 0 : _b.dispose();
        (_c = this.osnapMarkerManager) === null || _c === void 0 ? void 0 : _c.clear();
    };
    /**
     * Mouse move handler.
     * Updates dynamic input values, rubber-band preview, OSNAP marker,
     * and optional preview drawing.
     */
    AcEdFloatingInput.prototype.handleMouseMove = function (e) {
        if (!this.visible)
            return;
        var wcsPos = this.getPosition(e);
        this.updateDynamicPreview(wcsPos);
    };
    /**
     * Re-render the current preview using the most recent cursor position.
     * Useful when modifier keys change without mouse movement.
     */
    AcEdFloatingInput.prototype.requestPreviewRefresh = function () {
        if (!this.visible || !this.lastDynamicPoint)
            return;
        this.updateDynamicPreview(this.lastDynamicPoint);
    };
    // ---------------------------------------------------------------------------
    // Click / Commit
    // ---------------------------------------------------------------------------
    AcEdFloatingInput.prototype.handleClick = function (e) {
        var _a;
        if (!this.visible)
            return;
        var wcsPos = this.getPosition(e);
        var defaults = this.getDynamicValue(wcsPos);
        this.lastPoint = wcsPos;
        (_a = this.onCommit) === null || _a === void 0 ? void 0 : _a.call(this, defaults.value, wcsPos);
    };
    AcEdFloatingInput.prototype.updateDynamicPreview = function (wcsPos) {
        var _a, _b, _c;
        this.lastDynamicPoint = { x: wcsPos.x, y: wcsPos.y };
        var defaults = this.getDynamicValue(wcsPos);
        (_a = this.inputs) === null || _a === void 0 ? void 0 : _a.setValue(defaults.raw);
        // Ensure focus stays in input boxes
        if (this.inputs && !this.inputs.focused && !this.suppressDisplay) {
            this.inputs.focus();
        }
        (_b = this.rubberBand) === null || _b === void 0 ? void 0 : _b.update(wcsPos);
        (_c = this.drawPreview) === null || _c === void 0 ? void 0 : _c.call(this, wcsPos);
    };
    // ---------------------------------------------------------------------------
    // Position & OSNAP
    // ---------------------------------------------------------------------------
    /**
     * Gets the current cursor position in WCS, considering OSNAP.
     */
    AcEdFloatingInput.prototype.getPosition = function (e) {
        // Update floating UI position (screen space)
        var mousePos = _super.prototype.setPosition.call(this, e);
        // Convert cursor to WCS
        var wcsPos = this.view.screenToWorld(mousePos);
        // Apply OSNAP
        if (this.osnapMarkerManager) {
            this.osnapMarkerManager.hideMarker();
            this.lastOsnapPoint = this.getOsnapPoint();
            if (this.lastOsnapPoint) {
                wcsPos.x = this.lastOsnapPoint.x;
                wcsPos.y = this.lastOsnapPoint.y;
                this.osnapMarkerManager.showMarker(this.lastOsnapPoint, this.osnapMode2MarkerType(this.lastOsnapPoint.type));
            }
        }
        return wcsPos;
    };
    // ---------------------------------------------------------------------------
    // Dynamic Input Settings
    // ---------------------------------------------------------------------------
    AcEdFloatingInput.prototype.updateDynamicInputDisplay = function () {
        var dynamicEnabled = this.isDynamicInputEnabled();
        var promptEnabled = this.isDynamicPromptEnabled();
        this.setSuppressDisplay(!dynamicEnabled);
        this.setPromptVisible(this.allowPrompt && dynamicEnabled && promptEnabled);
    };
    AcEdFloatingInput.prototype.setSuppressDisplay = function (suppress) {
        var _a;
        if (this.suppressDisplay === suppress)
            return;
        this.suppressDisplay = suppress;
        if (!this.visible)
            return;
        if (this.suppressDisplay) {
            this.container.style.display = 'none';
            (_a = this.inputs) === null || _a === void 0 ? void 0 : _a.blur();
        }
        else {
            this.container.style.display = 'flex';
            if (this.inputs && !this.inputs.focused) {
                this.inputs.focus();
            }
        }
    };
    AcEdFloatingInput.prototype.setPromptVisible = function (show) {
        var _a;
        var hasMessage = !!((_a = this.label.textContent) === null || _a === void 0 ? void 0 : _a.trim());
        if (!hasMessage) {
            this.label.style.display = 'none';
            return;
        }
        this.label.style.display = show ? 'inline' : 'none';
    };
    AcEdFloatingInput.prototype.osnapMode2MarkerType = function (osnapMode) {
        switch (osnapMode) {
            case AcDbOsnapMode.EndPoint:
                return 'rect';
            case AcDbOsnapMode.MidPoint:
                return 'triangle';
            case AcDbOsnapMode.Center:
                return 'circle';
            case AcDbOsnapMode.Quadrant:
                return 'diamond';
            case AcDbOsnapMode.Nearest:
                return 'x';
            default:
                return 'rect';
        }
    };
    // ---------------------------------------------------------------------------
    // OSNAP calculation
    // ---------------------------------------------------------------------------
    /**
     * Returns the priority tier for a given OSNAP mode.
     * Lower number = higher priority. Matches AutoCAD behavior where
     * Endpoint/Midpoint/Center take precedence over Nearest.
     */
    AcEdFloatingInput.prototype.osnapModePriority = function (mode) {
        switch (mode) {
            case AcDbOsnapMode.EndPoint:
            case AcDbOsnapMode.MidPoint:
            case AcDbOsnapMode.Center:
                return 0;
            case AcDbOsnapMode.Quadrant:
                return 1;
            case AcDbOsnapMode.Nearest:
                return 2;
            default:
                return 1;
        }
    };
    AcEdFloatingInput.prototype.getOsnapPoint = function (point, hitRadius) {
        if (hitRadius === void 0) { hitRadius = 20; }
        var snapPoints = this.getOsnapPoints(point, hitRadius);
        if (snapPoints.length === 0)
            return undefined;
        var p1 = this.view.screenToWorld({ x: 0, y: 0 });
        var p2 = this.view.screenToWorld({ x: hitRadius, y: 0 });
        var threshold = p2.x - p1.x;
        // Group candidates by priority tier, picking the nearest within each tier.
        // Higher-priority modes (Endpoint, Midpoint, Center) always win over
        // lower-priority ones (Nearest), matching AutoCAD behavior.
        var bestPriority = Number.MAX_VALUE;
        var bestDist = Number.MAX_VALUE;
        var bestIndex = -1;
        for (var i = 0; i < snapPoints.length; i++) {
            var d = this.view.curPos.distanceTo(snapPoints[i]);
            if (d >= threshold)
                continue;
            var priority = this.osnapModePriority(snapPoints[i].type);
            if (priority < bestPriority ||
                (priority === bestPriority && d < bestDist)) {
                bestPriority = priority;
                bestDist = d;
                bestIndex = i;
            }
        }
        return bestIndex !== -1 ? snapPoints[bestIndex] : undefined;
    };
    AcEdFloatingInput.prototype.getOsnapPoints = function (point, hitRadius) {
        var _this = this;
        if (hitRadius === void 0) { hitRadius = 20; }
        var results = this.view.pick(point, hitRadius);
        var db = acdbHostApplicationServices().workingDatabase;
        var modelSpace = db.tables.blockTable.modelSpace;
        var osnapPoints = [];
        results.forEach(function (item) {
            var entity = modelSpace.getIdAt(item.id);
            if (!entity)
                return;
            if (item.children) {
                item.children.forEach(function (child) {
                    return _this.getOsnapPointsInAvailableModes(entity, osnapPoints, child.id);
                });
            }
            else {
                _this.getOsnapPointsInAvailableModes(entity, osnapPoints);
            }
        });
        return osnapPoints;
    };
    AcEdFloatingInput.prototype.getOsnapPointsInAvailableModes = function (entity, osnapPoints, gsMark) {
        var _this = this;
        var modes = acdbMaskToOsnapModes(AcApSettingManager.instance.osnapModes);
        modes.forEach(function (mode) {
            return _this.getOsnapPointsByMode(entity, mode, osnapPoints, gsMark);
        });
    };
    AcEdFloatingInput.prototype.getOsnapPointsByMode = function (entity, osnapMode, osnapPoints, gsMark) {
        var start = osnapPoints.length;
        entity.subGetOsnapPoints(osnapMode, __assign(__assign({}, this.view.curPos), { z: 0 }), this.lastPoint, osnapPoints, gsMark);
        for (var i = start; i < osnapPoints.length; i++) {
            osnapPoints[i].type = osnapMode;
        }
    };
    /** Inject styles only once */
    AcEdFloatingInput.inputStylesInjected = false;
    return AcEdFloatingInput;
}(AcEdFloatingMessage));
export { AcEdFloatingInput };
//# sourceMappingURL=AcEdFloatingInput.js.map