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 __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
    function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
    return new (P || (P = Promise))(function (resolve, reject) {
        function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
        function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
        function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
        step((generator = generator.apply(thisArg, _arguments || [])).next());
    });
};
var __generator = (this && this.__generator) || function (thisArg, body) {
    var _ = { label: 0, sent: function() { if (t[0] & 1) throw t[1]; return t[1]; }, trys: [], ops: [] }, f, y, t, g = Object.create((typeof Iterator === "function" ? Iterator : Object).prototype);
    return g.next = verb(0), g["throw"] = verb(1), g["return"] = verb(2), typeof Symbol === "function" && (g[Symbol.iterator] = function() { return this; }), g;
    function verb(n) { return function (v) { return step([n, v]); }; }
    function step(op) {
        if (f) throw new TypeError("Generator is already executing.");
        while (g && (g = 0, op[0] && (_ = 0)), _) try {
            if (f = 1, y && (t = op[0] & 2 ? y["return"] : op[0] ? y["throw"] || ((t = y["return"]) && t.call(y), 0) : y.next) && !(t = t.call(y, op[1])).done) return t;
            if (y = 0, t) op = [op[0] & 2, t.value];
            switch (op[0]) {
                case 0: case 1: t = op; break;
                case 4: _.label++; return { value: op[1], done: false };
                case 5: _.label++; y = op[1]; op = [0]; continue;
                case 7: op = _.ops.pop(); _.trys.pop(); continue;
                default:
                    if (!(t = _.trys, t = t.length > 0 && t[t.length - 1]) && (op[0] === 6 || op[0] === 2)) { _ = 0; continue; }
                    if (op[0] === 3 && (!t || (op[1] > t[0] && op[1] < t[3]))) { _.label = op[1]; break; }
                    if (op[0] === 6 && _.label < t[1]) { _.label = t[1]; t = op; break; }
                    if (t && _.label < t[2]) { _.label = t[2]; _.ops.push(op); break; }
                    if (t[2]) _.ops.pop();
                    _.trys.pop(); continue;
            }
            op = body.call(thisArg, _);
        } catch (e) { op = [6, e]; y = 0; } finally { f = t = 0; }
        if (op[0] & 5) throw op[1]; return { value: op[0] ? op[1] : void 0, done: true };
    }
};
import { AcDbArc, AcGePoint3d } from '@mlightcad/data-model';
import { AcApDocManager } from '../app';
import { AcEdCommand, AcEdOpenMode, AcEdPreviewJig, AcEdPromptDistanceOptions, AcEdPromptDoubleOptions, AcEdPromptPointOptions, AcEdPromptStatus, eventBus } from '../editor';
import { AcApI18n } from '../i18n';
var TAU = Math.PI * 2;
var EPSILON = 1e-9;
var POSITIVE_NORMAL = { x: 0, y: 0, z: 1 };
var NEGATIVE_NORMAL = { x: 0, y: 0, z: -1 };
/**
 * Normalizes an angle to the range [0, 2π).
 *
 * @param angle - Input angle in radians.
 * @returns Normalized angle in radians.
 */
function normalizeAngle(angle) {
    var value = angle % TAU;
    return value >= 0 ? value : value + TAU;
}
/**
 * Computes Euclidean distance in XY plane.
 *
 * @param p1 - First point.
 * @param p2 - Second point.
 * @returns 2D distance between points.
 */
function distance2d(p1, p2) {
    return Math.hypot(p2.x - p1.x, p2.y - p1.y);
}
/**
 * Computes point angle around a center under a given arc orientation.
 *
 * For `normalSign = -1` (clockwise), the Y component is mirrored so that
 * angle growth still follows the selected arc direction convention.
 *
 * @param center - Arc center.
 * @param point - Point on the arc/circle.
 * @param normalSign - Arc orientation sign (+Z / -Z).
 * @returns Direction-aware normalized angle in radians.
 */
function angleByNormalSign(center, point, normalSign) {
    // For -Z arcs, mirror Y so the angle still increases in the arc direction.
    var dx = point.x - center.x;
    var dy = point.y - center.y;
    var raw = normalSign === 1 ? Math.atan2(dy, dx) : Math.atan2(-dy, dx);
    return normalizeAngle(raw);
}
/**
 * Maps orientation sign to 3D normal vector.
 *
 * @param sign - Orientation sign.
 * @returns Unit normal vector used by `AcDbArc`.
 */
function toNormal(sign) {
    return sign === 1 ? POSITIVE_NORMAL : NEGATIVE_NORMAL;
}
/**
 * Creates one `AcDbArc` entity from normalized arc definition data.
 *
 * @param definition - Arc definition (center/radius/angles/orientation).
 * @returns New arc entity instance.
 */
function createArcEntity(definition) {
    return new AcDbArc(definition.center, definition.radius, definition.startAngle, definition.endAngle, toNormal(definition.normalSign));
}
/**
 * Applies arc definition fields onto an existing `AcDbArc`.
 *
 * Used by the preview jig to update transient geometry without recreating
 * entity instances on each mouse-move.
 *
 * @param entity - Target arc entity to mutate.
 * @param definition - Source definition values.
 */
function applyArcDefinition(entity, definition) {
    entity.center = definition.center;
    entity.radius = definition.radius;
    entity.startAngle = definition.startAngle;
    entity.endAngle = definition.endAngle;
    entity.normal = toNormal(definition.normalSign);
}
/**
 * Creates a tiny placeholder arc used before valid preview geometry exists.
 *
 * @param point - Seed location for fallback arc center.
 * @returns Minimal valid arc definition.
 */
function createFallbackArc(point) {
    return {
        center: { x: point.x, y: point.y, z: 0 },
        radius: 1e-6,
        startAngle: 0,
        endAngle: Math.PI / 2,
        normalSign: 1
    };
}
/**
 * Projects an arbitrary point radially onto a circle.
 *
 * @param center - Circle center.
 * @param radius - Circle radius.
 * @param point - Input point to project.
 * @returns Projected point on circle, or `undefined` when projection fails.
 */
function projectPointToCircle(center, radius, point) {
    var dx = point.x - center.x;
    var dy = point.y - center.y;
    var distance = Math.hypot(dx, dy);
    if (distance <= EPSILON)
        return undefined;
    var scale = radius / distance;
    return {
        x: center.x + dx * scale,
        y: center.y + dy * scale,
        z: 0
    };
}
/**
 * Tests whether `mid` lies on the forward sweep from `start` to `end`.
 *
 * All angles are treated as normalized radians in [0, 2π).
 *
 * @param start - Sweep start angle.
 * @param mid - Candidate angle to test.
 * @param end - Sweep end angle.
 * @returns `true` if `mid` is on the sweep.
 */
function isAngleOnSweep(start, mid, end) {
    var total = normalizeAngle(end - start);
    var offset = normalizeAngle(mid - start);
    return offset <= total + 1e-7;
}
/**
 * Chooses arc orientation for 3-point input so the sweep passes the second point.
 *
 * @param center - Computed circumcenter.
 * @param start - Arc start point.
 * @param second - Point-on-arc (the AutoCAD "second point").
 * @param end - Arc end point.
 * @returns Orientation sign producing the intended 3-point arc.
 */
function chooseThreePointNormalSign(center, start, second, end) {
    var startCcw = angleByNormalSign(center, start, 1);
    var secondCcw = angleByNormalSign(center, second, 1);
    var endCcw = angleByNormalSign(center, end, 1);
    var ccwContains = isAngleOnSweep(startCcw, secondCcw, endCcw);
    var startCw = angleByNormalSign(center, start, -1);
    var secondCw = angleByNormalSign(center, second, -1);
    var endCw = angleByNormalSign(center, end, -1);
    var cwContains = isAngleOnSweep(startCw, secondCw, endCw);
    if (ccwContains && !cwContains)
        return 1;
    if (!ccwContains && cwContains)
        return -1;
    // Degenerate/near-boundary fallback.
    var cross = (second.x - start.x) * (end.y - start.y) -
        (second.y - start.y) * (end.x - start.x);
    return cross >= 0 ? 1 : -1;
}
/**
 * Builds an arc from 3-point input (start / point-on-arc / end).
 *
 * This method computes circumcenter/radius explicitly in XY and chooses
 * orientation so the final sweep passes through `second`. If `reverseDirection`
 * is true, the orientation is flipped (used by Ctrl toggle).
 *
 * @param start - Start point.
 * @param second - Point on arc.
 * @param end - End point.
 * @param reverseDirection - Whether to invert auto-selected orientation.
 * @returns Arc definition or `undefined` when points are invalid.
 */
function createArcFromThreePoints(start, second, end, reverseDirection) {
    if (reverseDirection === void 0) { reverseDirection = false; }
    // Compute circumcenter from three non-collinear points in XY.
    var x1 = start.x;
    var y1 = start.y;
    var x2 = second.x;
    var y2 = second.y;
    var x3 = end.x;
    var y3 = end.y;
    var d = 2 * (x1 * (y2 - y3) + x2 * (y3 - y1) + x3 * (y1 - y2));
    if (Math.abs(d) <= EPSILON)
        return undefined;
    var ux = ((x1 * x1 + y1 * y1) * (y2 - y3) +
        (x2 * x2 + y2 * y2) * (y3 - y1) +
        (x3 * x3 + y3 * y3) * (y1 - y2)) /
        d;
    var uy = ((x1 * x1 + y1 * y1) * (x3 - x2) +
        (x2 * x2 + y2 * y2) * (x1 - x3) +
        (x3 * x3 + y3 * y3) * (x2 - x1)) /
        d;
    var center = { x: ux, y: uy, z: 0 };
    var radius = distance2d(center, start);
    if (!Number.isFinite(radius) || radius <= EPSILON)
        return undefined;
    // By default, choose the arc that actually passes through the second point.
    // Ctrl toggle can reverse this selection to the complementary direction.
    var autoNormalSign = chooseThreePointNormalSign(center, start, second, end);
    var normalSign = reverseDirection
        ? -autoNormalSign
        : autoNormalSign;
    return {
        center: center,
        radius: radius,
        startAngle: angleByNormalSign(center, start, normalSign),
        endAngle: angleByNormalSign(center, end, normalSign),
        normalSign: normalSign
    };
}
/**
 * Builds arc by center/start/end with explicit orientation.
 *
 * @param center - Arc center.
 * @param start - Arc start point.
 * @param end - Arc end point.
 * @param normalSign - Orientation sign (+Z / -Z).
 * @returns Arc definition, or `undefined` if geometric constraints fail.
 */
function createArcFromCenterStartEnd(center, start, end, normalSign) {
    var radiusFromStart = distance2d(center, start);
    var radiusFromEnd = distance2d(center, end);
    if (radiusFromStart <= EPSILON || radiusFromEnd <= EPSILON) {
        return undefined;
    }
    // Start/end must lie on the same circle (small tolerance for picked input).
    var tolerance = Math.max(EPSILON, radiusFromStart * 1e-6);
    if (Math.abs(radiusFromStart - radiusFromEnd) > tolerance) {
        return undefined;
    }
    return {
        center: { x: center.x, y: center.y, z: 0 },
        radius: radiusFromStart,
        startAngle: angleByNormalSign(center, start, normalSign),
        endAngle: angleByNormalSign(center, end, normalSign),
        normalSign: normalSign
    };
}
/**
 * Builds center-start arc where user end input is projected onto the circle.
 *
 * Useful for preview/interaction where raw cursor point may not lie exactly
 * on the circle defined by center+start.
 *
 * @param center - Arc center.
 * @param start - Arc start point.
 * @param rawEnd - Raw user cursor/pick point.
 * @param normalSign - Orientation sign (+Z / -Z).
 * @returns Arc definition, or `undefined` if projection/geometry fails.
 */
function createArcFromCenterStartProjectedEnd(center, start, rawEnd, normalSign) {
    var radius = distance2d(center, start);
    if (radius <= EPSILON)
        return undefined;
    var end = projectPointToCircle(center, radius, rawEnd);
    if (!end)
        return undefined;
    return createArcFromCenterStartEnd(center, start, end, normalSign);
}
/**
 * Builds center-start arc from included sweep angle.
 *
 * Positive sweep = +Z orientation, negative sweep = -Z orientation.
 *
 * @param center - Arc center.
 * @param start - Arc start point.
 * @param sweepRad - Included angle in radians (signed).
 * @returns Arc definition, or `undefined` for invalid sweep/radius.
 */
function createArcFromCenterStartSweep(center, start, sweepRad) {
    var radius = distance2d(center, start);
    var sweep = Math.abs(sweepRad);
    if (radius <= EPSILON || sweep <= EPSILON || sweep >= TAU - EPSILON) {
        return undefined;
    }
    var normalSign = sweepRad >= 0 ? 1 : -1;
    var startAngle = angleByNormalSign(center, start, normalSign);
    var endAngle = normalizeAngle(startAngle + sweep);
    return {
        center: { x: center.x, y: center.y, z: 0 },
        radius: radius,
        startAngle: startAngle,
        endAngle: endAngle,
        normalSign: normalSign
    };
}
/**
 * Builds center-start arc from chord length.
 *
 * Sign of chord length controls orientation; magnitude controls included angle.
 *
 * @param center - Arc center.
 * @param start - Arc start point.
 * @param chordLength - Chord length (signed).
 * @returns Arc definition, or `undefined` when out of geometric range.
 */
function createArcFromCenterStartChord(center, start, chordLength) {
    var radius = distance2d(center, start);
    var chord = Math.abs(chordLength);
    if (radius <= EPSILON || chord <= EPSILON || chord > 2 * radius + EPSILON) {
        return undefined;
    }
    var ratio = Math.max(-1, Math.min(1, chord / (2 * radius)));
    var sweep = 2 * Math.asin(ratio);
    var signedSweep = chordLength >= 0 ? sweep : -sweep;
    return createArcFromCenterStartSweep(center, start, signedSweep);
}
/**
 * Builds start-end arc from included angle.
 *
 * @param start - Arc start point.
 * @param end - Arc end point.
 * @param sweepRad - Included angle in radians (signed).
 * @returns Arc definition, or `undefined` if no valid solution exists.
 */
function createArcFromStartEndAngle(start, end, sweepRad) {
    var chord = distance2d(start, end);
    var sweep = Math.abs(sweepRad);
    if (chord <= EPSILON || sweep <= EPSILON || sweep >= TAU - EPSILON) {
        return undefined;
    }
    // chord = 2 * r * sin(theta/2)  ->  r = chord / (2 * sin(theta/2))
    var sinHalf = Math.sin(sweep / 2);
    if (Math.abs(sinHalf) <= EPSILON)
        return undefined;
    var radius = chord / (2 * sinHalf);
    var offsetSquared = radius * radius - (chord * chord) / 4;
    if (offsetSquared < -EPSILON)
        return undefined;
    var offset = Math.sqrt(Math.max(0, offsetSquared));
    var midX = (start.x + end.x) / 2;
    var midY = (start.y + end.y) / 2;
    var dx = end.x - start.x;
    var dy = end.y - start.y;
    var ux = -dy / chord;
    var uy = dx / chord;
    var isCounterClockwise = sweepRad >= 0;
    // Two circle centers satisfy start/end + radius. Select side by
    // CW/CCW intent and whether included angle is minor/major.
    var useLeft = (isCounterClockwise && sweep <= Math.PI) ||
        (!isCounterClockwise && sweep > Math.PI);
    var side = useLeft ? 1 : -1;
    var center = {
        x: midX + ux * offset * side,
        y: midY + uy * offset * side,
        z: 0
    };
    var normalSign = isCounterClockwise ? 1 : -1;
    return createArcFromCenterStartEnd(center, start, end, normalSign);
}
/**
 * Builds start-end arc from tangent direction at start.
 *
 * @param start - Arc start point.
 * @param end - Arc end point.
 * @param directionRad - Tangent direction at start (radians).
 * @returns Arc definition, or `undefined` when constraints are degenerate.
 */
function createArcFromStartEndDirection(start, end, directionRad) {
    // Build center from tangent constraint at start:
    // center lies on line through start with normal to tangent.
    var tx = Math.cos(directionRad);
    var ty = Math.sin(directionRad);
    var dx = end.x - start.x;
    var dy = end.y - start.y;
    var nx = -ty;
    var ny = tx;
    var denominator = 2 * (dx * nx + dy * ny);
    if (Math.abs(denominator) <= EPSILON)
        return undefined;
    // Solve center = start + lambda * n so that |center-start| = |center-end|.
    var lambda = (dx * dx + dy * dy) / denominator;
    if (!Number.isFinite(lambda))
        return undefined;
    var center = {
        x: start.x + nx * lambda,
        y: start.y + ny * lambda,
        z: 0
    };
    var radiusVectorX = start.x - center.x;
    var radiusVectorY = start.y - center.y;
    var cross = radiusVectorX * ty - radiusVectorY * tx;
    if (Math.abs(cross) <= EPSILON)
        return undefined;
    var normalSign = cross >= 0 ? 1 : -1;
    return createArcFromCenterStartEnd(center, start, end, normalSign);
}
/**
 * Builds start-end arc from radius.
 *
 * Radius sign controls side/orientation (AutoCAD-like behavior).
 *
 * @param start - Arc start point.
 * @param end - Arc end point.
 * @param radiusInput - Radius value (signed).
 * @returns Arc definition, or `undefined` when radius cannot connect points.
 */
function createArcFromStartEndRadius(start, end, radiusInput) {
    var radius = Math.abs(radiusInput);
    var chord = distance2d(start, end);
    if (radius <= EPSILON || chord <= EPSILON || chord > 2 * radius + EPSILON) {
        return undefined;
    }
    var midX = (start.x + end.x) / 2;
    var midY = (start.y + end.y) / 2;
    var dx = end.x - start.x;
    var dy = end.y - start.y;
    var ux = -dy / chord;
    var uy = dx / chord;
    var offset = Math.sqrt(Math.max(0, radius * radius - (chord * chord) / 4));
    // Keep AutoCAD-like behavior:
    // positive radius picks +Z side, negative radius picks mirrored side.
    var side = radiusInput >= 0 ? 1 : -1;
    var center = {
        x: midX + ux * offset * side,
        y: midY + uy * offset * side,
        z: 0
    };
    var normalSign = radiusInput >= 0 ? 1 : -1;
    return createArcFromCenterStartEnd(center, start, end, normalSign);
}
/**
 * Dynamic preview jig for ARC command.
 *
 * The jig holds one transient `AcDbArc` and updates it from a builder callback
 * as cursor input changes.
 */
var AcApArcJig = /** @class */ (function (_super) {
    __extends(AcApArcJig, _super);
    /**
     * Creates a preview jig.
     *
     * @param view - Active editor view.
     * @param builder - Cursor-to-arc definition resolver.
     * @param fallback - Initial valid fallback definition.
     */
    function AcApArcJig(view, builder, fallback) {
        var _this = _super.call(this, view) || this;
        _this._builder = builder;
        _this._arc = createArcEntity(fallback);
        return _this;
    }
    Object.defineProperty(AcApArcJig.prototype, "entity", {
        /**
         * Gets transient arc entity.
         */
        get: function () {
            return this._arc;
        },
        enumerable: false,
        configurable: true
    });
    /**
     * Updates transient arc for current cursor point.
     *
     * @param point - Current cursor/input point.
     */
    AcApArcJig.prototype.update = function (point) {
        // Ignore invalid intermediate states while the cursor moves.
        var definition = this._builder(point);
        if (!definition)
            return;
        applyArcDefinition(this._arc, definition);
    };
    return AcApArcJig;
}(AcEdPreviewJig));
/**
 * Command to create one arc.
 *
 * Supported input flows align with AutoCAD ARC command options:
 * - Start/Second/End (3-point)
 * - Start/Center/End, Start/Center/Angle, Start/Center/Chord Length
 * - Start/End/Center, Start/End/Angle, Start/End/Direction, Start/End/Radius
 * - Center/Start/End, Center/Start/Angle, Center/Start/Chord Length
 */
var AcApArcCmd = /** @class */ (function (_super) {
    __extends(AcApArcCmd, _super);
    /**
     * Creates ARC command instance.
     */
    function AcApArcCmd() {
        var _this = _super.call(this) || this;
        _this.mode = AcEdOpenMode.Write;
        return _this;
    }
    /**
     * Command entry point.
     *
     * Supports initial Start-point branch and Center-first branch.
     *
     * @param context - Current app context.
     */
    AcApArcCmd.prototype.execute = function (context) {
        return __awaiter(this, void 0, void 0, function () {
            var prompt, result;
            return __generator(this, function (_a) {
                switch (_a.label) {
                    case 0:
                        // Keep ARC behavior deterministic for each run (same as PLINE flow).
                        AcApDocManager.instance.editor.resetInputToggles();
                        prompt = new AcEdPromptPointOptions(AcApI18n.t('jig.arc.startPointOrCenter'));
                        this.addKeyword(prompt, 'center');
                        return [4 /*yield*/, AcApDocManager.instance.editor.getPoint(prompt)];
                    case 1:
                        result = _a.sent();
                        if (!(result.status === AcEdPromptStatus.Keyword)) return [3 /*break*/, 4];
                        if (!(result.stringResult === 'Center')) return [3 /*break*/, 3];
                        return [4 /*yield*/, this.runCenterStartFlow(context)];
                    case 2:
                        _a.sent();
                        _a.label = 3;
                    case 3: return [2 /*return*/];
                    case 4:
                        if (result.status !== AcEdPromptStatus.OK)
                            return [2 /*return*/];
                        return [4 /*yield*/, this.runStartFlow(context, result.value)];
                    case 5:
                        _a.sent();
                        return [2 /*return*/];
                }
            });
        });
    };
    /**
     * Adds one localized keyword to a prompt options object.
     *
     * @param prompt - Prompt options to extend.
     * @param key - Keyword i18n key suffix.
     */
    AcApArcCmd.prototype.addKeyword = function (prompt, key) {
        prompt.keywords.add(AcApI18n.t("jig.arc.keywords.".concat(key, ".display")), AcApI18n.t("jig.arc.keywords.".concat(key, ".global")), AcApI18n.t("jig.arc.keywords.".concat(key, ".local")));
    };
    /**
     * Appends created arc entity to model space.
     *
     * @param context - Current app context.
     * @param arc - Arc definition to append.
     */
    AcApArcCmd.prototype.appendArc = function (context, arc) {
        context.doc.database.tables.blockTable.modelSpace.appendEntity(createArcEntity(arc));
    };
    /**
     * Emits one warning message for invalid geometry input.
     *
     * @param key - Invalid-input category key.
     */
    AcApArcCmd.prototype.warnInvalidGeometry = function (key) {
        eventBus.emit('message', {
            message: AcApI18n.t("jig.arc.invalid.".concat(key)),
            type: 'warning'
        });
    };
    /**
     * Gets direction factor from Ctrl-toggle state.
     *
     * @returns `1` for normal direction, `-1` for flipped direction.
     */
    AcApArcCmd.prototype.getArcDirectionFactor = function () {
        var toggles = AcApDocManager.instance.editor.getInputToggles();
        return toggles.ctrlArcFlip ? -1 : 1;
    };
    /**
     * Applies Ctrl direction factor to orientation sign.
     *
     * @param sign - Base orientation sign.
     * @returns Possibly flipped orientation sign.
     */
    AcApArcCmd.prototype.applyCtrlDirectionSign = function (sign) {
        return (sign * this.getArcDirectionFactor());
    };
    /**
     * Applies Ctrl direction factor to a signed numeric input.
     *
     * @param value - Signed value (angle/chord/radius).
     * @returns Value with direction factor applied.
     */
    AcApArcCmd.prototype.applyCtrlDirectionValue = function (value) {
        // Used by signed numeric inputs (angle/chord/radius), so Ctrl can flip sign.
        return value * this.getArcDirectionFactor();
    };
    /**
     * Gets whether current Ctrl-toggle indicates direction reversal.
     *
     * @returns `true` when direction is flipped.
     */
    AcApArcCmd.prototype.isCtrlDirectionFlipped = function () {
        // 3-point flow uses geometric auto-direction first, then optional Ctrl reverse.
        var toggles = AcApDocManager.instance.editor.getInputToggles();
        return toggles.ctrlArcFlip;
    };
    /**
     * Handles Start-first main branch.
     *
     * Flow:
     * - Start -> Second -> End (3-point)
     * - Start -> Center -> ...
     * - Start -> End -> ...
     *
     * @param context - Current app context.
     * @param start - Start point picked by user.
     */
    AcApArcCmd.prototype.runStartFlow = function (context, start) {
        return __awaiter(this, void 0, void 0, function () {
            var prompt, result;
            return __generator(this, function (_a) {
                switch (_a.label) {
                    case 0:
                        prompt = new AcEdPromptPointOptions(AcApI18n.t('jig.arc.secondPointOrOptions'));
                        this.addKeyword(prompt, 'center');
                        this.addKeyword(prompt, 'end');
                        prompt.useBasePoint = true;
                        prompt.useDashedLine = true;
                        prompt.basePoint = new AcGePoint3d(start);
                        return [4 /*yield*/, AcApDocManager.instance.editor.getPoint(prompt)];
                    case 1:
                        result = _a.sent();
                        if (!(result.status === AcEdPromptStatus.OK)) return [3 /*break*/, 3];
                        return [4 /*yield*/, this.finishThreePointArc(context, start, result.value)];
                    case 2:
                        _a.sent();
                        return [2 /*return*/];
                    case 3:
                        if (result.status !== AcEdPromptStatus.Keyword)
                            return [2 /*return*/];
                        if (!(result.stringResult === 'Center')) return [3 /*break*/, 5];
                        return [4 /*yield*/, this.runStartCenterFlow(context, start)];
                    case 4:
                        _a.sent();
                        return [2 /*return*/];
                    case 5:
                        if (!(result.stringResult === 'End')) return [3 /*break*/, 7];
                        return [4 /*yield*/, this.runStartEndFlow(context, start)];
                    case 6:
                        _a.sent();
                        _a.label = 7;
                    case 7: return [2 /*return*/];
                }
            });
        });
    };
    /**
     * Completes 3-point arc flow (Start/Second/End).
     *
     * @param context - Current app context.
     * @param start - Start point.
     * @param second - Point on arc.
     */
    AcApArcCmd.prototype.finishThreePointArc = function (context, start, second) {
        return __awaiter(this, void 0, void 0, function () {
            var prompt, result, arc;
            var _this = this;
            return __generator(this, function (_a) {
                switch (_a.label) {
                    case 0:
                        prompt = new AcEdPromptPointOptions(AcApI18n.t('jig.arc.endPoint'));
                        prompt.useBasePoint = true;
                        prompt.useDashedLine = true;
                        prompt.basePoint = new AcGePoint3d(start);
                        prompt.jig = new AcApArcJig(context.view, function (point) {
                            return createArcFromThreePoints(start, second, point, 
                            // Live preview follows the same Ctrl reverse behavior as final commit.
                            _this.isCtrlDirectionFlipped());
                        }, createFallbackArc(start));
                        return [4 /*yield*/, AcApDocManager.instance.editor.getPoint(prompt)];
                    case 1:
                        result = _a.sent();
                        if (result.status !== AcEdPromptStatus.OK)
                            return [2 /*return*/];
                        arc = createArcFromThreePoints(start, second, result.value, 
                        // Keep commit behavior identical to preview.
                        this.isCtrlDirectionFlipped());
                        if (arc) {
                            this.appendArc(context, arc);
                        }
                        else {
                            this.warnInvalidGeometry('threePoint');
                        }
                        return [2 /*return*/];
                }
            });
        });
    };
    /**
     * Runs Center-first branch:
     * Center -> Start -> (End | Angle | Chord Length)
     *
     * @param context - Current app context.
     */
    AcApArcCmd.prototype.runCenterStartFlow = function (context) {
        return __awaiter(this, void 0, void 0, function () {
            var centerPrompt, centerResult, center, startPrompt, startResult;
            return __generator(this, function (_a) {
                switch (_a.label) {
                    case 0:
                        centerPrompt = new AcEdPromptPointOptions(AcApI18n.t('jig.arc.centerPoint'));
                        return [4 /*yield*/, AcApDocManager.instance.editor.getPoint(centerPrompt)];
                    case 1:
                        centerResult = _a.sent();
                        if (centerResult.status !== AcEdPromptStatus.OK)
                            return [2 /*return*/];
                        center = centerResult.value;
                        startPrompt = new AcEdPromptPointOptions(AcApI18n.t('jig.arc.startPoint'));
                        startPrompt.useBasePoint = true;
                        startPrompt.useDashedLine = true;
                        startPrompt.basePoint = new AcGePoint3d(center);
                        return [4 /*yield*/, AcApDocManager.instance.editor.getPoint(startPrompt)];
                    case 2:
                        startResult = _a.sent();
                        if (startResult.status !== AcEdPromptStatus.OK)
                            return [2 /*return*/];
                        return [4 /*yield*/, this.finishCenterStartFlow(context, center, startResult.value)];
                    case 3:
                        _a.sent();
                        return [2 /*return*/];
                }
            });
        });
    };
    /**
     * Runs Start->Center branch:
     * Start -> Center -> (End | Angle | Chord Length)
     *
     * @param context - Current app context.
     * @param start - Start point.
     */
    AcApArcCmd.prototype.runStartCenterFlow = function (context, start) {
        return __awaiter(this, void 0, void 0, function () {
            var centerPrompt, centerResult;
            return __generator(this, function (_a) {
                switch (_a.label) {
                    case 0:
                        centerPrompt = new AcEdPromptPointOptions(AcApI18n.t('jig.arc.centerPoint'));
                        centerPrompt.useBasePoint = true;
                        centerPrompt.useDashedLine = true;
                        centerPrompt.basePoint = new AcGePoint3d(start);
                        return [4 /*yield*/, AcApDocManager.instance.editor.getPoint(centerPrompt)];
                    case 1:
                        centerResult = _a.sent();
                        if (centerResult.status !== AcEdPromptStatus.OK)
                            return [2 /*return*/];
                        return [4 /*yield*/, this.finishCenterStartFlow(context, centerResult.value, start)];
                    case 2:
                        _a.sent();
                        return [2 /*return*/];
                }
            });
        });
    };
    /**
     * Shared handler for center-start option family.
     *
     * Supports:
     * - End point picking
     * - Included angle input
     * - Chord length input
     *
     * @param context - Current app context.
     * @param center - Arc center.
     * @param start - Arc start point.
     */
    AcApArcCmd.prototype.finishCenterStartFlow = function (context, center, start) {
        return __awaiter(this, void 0, void 0, function () {
            var endPrompt, endResult, arc, anglePrompt, angleResult, arc, chordPrompt, chordResult, arc;
            var _this = this;
            return __generator(this, function (_a) {
                switch (_a.label) {
                    case 0:
                        endPrompt = new AcEdPromptPointOptions(AcApI18n.t('jig.arc.endPointOrOptions'));
                        this.addKeyword(endPrompt, 'angle');
                        this.addKeyword(endPrompt, 'chordLength');
                        endPrompt.useBasePoint = true;
                        endPrompt.useDashedLine = true;
                        endPrompt.basePoint = new AcGePoint3d(start);
                        endPrompt.jig = new AcApArcJig(context.view, function (point) {
                            return createArcFromCenterStartProjectedEnd(center, start, point, _this.applyCtrlDirectionSign(1));
                        }, createFallbackArc(start));
                        return [4 /*yield*/, AcApDocManager.instance.editor.getPoint(endPrompt)];
                    case 1:
                        endResult = _a.sent();
                        if (endResult.status === AcEdPromptStatus.OK) {
                            arc = createArcFromCenterStartProjectedEnd(center, start, endResult.value, this.applyCtrlDirectionSign(1));
                            if (arc) {
                                this.appendArc(context, arc);
                            }
                            else {
                                this.warnInvalidGeometry('center');
                            }
                            return [2 /*return*/];
                        }
                        if (endResult.status !== AcEdPromptStatus.Keyword)
                            return [2 /*return*/];
                        if (!(endResult.stringResult === 'Angle')) return [3 /*break*/, 3];
                        anglePrompt = new AcEdPromptDoubleOptions(AcApI18n.t('jig.arc.includedAngle'));
                        anglePrompt.allowZero = false;
                        anglePrompt.allowNegative = true;
                        return [4 /*yield*/, AcApDocManager.instance.editor.getDouble(anglePrompt)];
                    case 2:
                        angleResult = _a.sent();
                        if (angleResult.status !== AcEdPromptStatus.OK)
                            return [2 /*return*/];
                        arc = createArcFromCenterStartSweep(center, start, this.applyCtrlDirectionValue(this.degToRad(angleResult.value)));
                        if (arc) {
                            this.appendArc(context, arc);
                        }
                        else {
                            this.warnInvalidGeometry('angle');
                        }
                        return [2 /*return*/];
                    case 3:
                        if (!(endResult.stringResult === 'ChordLength')) return [3 /*break*/, 5];
                        chordPrompt = new AcEdPromptDistanceOptions(AcApI18n.t('jig.arc.chordLength'));
                        chordPrompt.allowZero = false;
                        chordPrompt.allowNegative = true;
                        return [4 /*yield*/, AcApDocManager.instance.editor.getDistance(chordPrompt)];
                    case 4:
                        chordResult = _a.sent();
                        if (chordResult.status !== AcEdPromptStatus.OK)
                            return [2 /*return*/];
                        arc = createArcFromCenterStartChord(center, start, this.applyCtrlDirectionValue(chordResult.value));
                        if (arc) {
                            this.appendArc(context, arc);
                        }
                        else {
                            this.warnInvalidGeometry('chordLength');
                        }
                        _a.label = 5;
                    case 5: return [2 /*return*/];
                }
            });
        });
    };
    /**
     * Handles Start-End option family.
     *
     * Supports:
     * - Start -> End -> Center
     * - Start -> End -> Angle
     * - Start -> End -> Direction
     * - Start -> End -> Radius
     *
     * @param context - Current app context.
     * @param start - Arc start point.
     */
    AcApArcCmd.prototype.runStartEndFlow = function (context, start) {
        return __awaiter(this, void 0, void 0, function () {
            var endPrompt, endResult, end, centerOrOptionPrompt, centerOrOptionResult, arc, anglePrompt, angleResult, arc, directionPrompt, directionResult, directionPoint, direction, arc, radiusPrompt, radiusResult, arc;
            var _this = this;
            return __generator(this, function (_a) {
                switch (_a.label) {
                    case 0:
                        endPrompt = new AcEdPromptPointOptions(AcApI18n.t('jig.arc.endPoint'));
                        endPrompt.useBasePoint = true;
                        endPrompt.useDashedLine = true;
                        endPrompt.basePoint = new AcGePoint3d(start);
                        return [4 /*yield*/, AcApDocManager.instance.editor.getPoint(endPrompt)];
                    case 1:
                        endResult = _a.sent();
                        if (endResult.status !== AcEdPromptStatus.OK)
                            return [2 /*return*/];
                        end = endResult.value;
                        centerOrOptionPrompt = new AcEdPromptPointOptions(AcApI18n.t('jig.arc.centerPointOrOptions'));
                        this.addKeyword(centerOrOptionPrompt, 'angle');
                        this.addKeyword(centerOrOptionPrompt, 'direction');
                        this.addKeyword(centerOrOptionPrompt, 'radius');
                        centerOrOptionPrompt.useBasePoint = true;
                        centerOrOptionPrompt.useDashedLine = true;
                        centerOrOptionPrompt.basePoint = new AcGePoint3d(start);
                        centerOrOptionPrompt.jig = new AcApArcJig(context.view, function (point) {
                            return createArcFromCenterStartEnd(point, start, end, _this.applyCtrlDirectionSign(1));
                        }, createFallbackArc(start));
                        return [4 /*yield*/, AcApDocManager.instance.editor.getPoint(centerOrOptionPrompt)];
                    case 2:
                        centerOrOptionResult = _a.sent();
                        if (centerOrOptionResult.status === AcEdPromptStatus.OK) {
                            arc = createArcFromCenterStartEnd(centerOrOptionResult.value, start, end, this.applyCtrlDirectionSign(1));
                            if (arc) {
                                this.appendArc(context, arc);
                            }
                            else {
                                this.warnInvalidGeometry('center');
                            }
                            return [2 /*return*/];
                        }
                        if (centerOrOptionResult.status !== AcEdPromptStatus.Keyword)
                            return [2 /*return*/];
                        if (!(centerOrOptionResult.stringResult === 'Angle')) return [3 /*break*/, 4];
                        anglePrompt = new AcEdPromptDoubleOptions(AcApI18n.t('jig.arc.includedAngle'));
                        anglePrompt.allowZero = false;
                        anglePrompt.allowNegative = true;
                        return [4 /*yield*/, AcApDocManager.instance.editor.getDouble(anglePrompt)];
                    case 3:
                        angleResult = _a.sent();
                        if (angleResult.status !== AcEdPromptStatus.OK)
                            return [2 /*return*/];
                        arc = createArcFromStartEndAngle(start, end, this.applyCtrlDirectionValue(this.degToRad(angleResult.value)));
                        if (arc) {
                            this.appendArc(context, arc);
                        }
                        else {
                            this.warnInvalidGeometry('angle');
                        }
                        return [2 /*return*/];
                    case 4:
                        if (!(centerOrOptionResult.stringResult === 'Direction')) return [3 /*break*/, 6];
                        directionPrompt = new AcEdPromptPointOptions(AcApI18n.t('jig.arc.tangentDirection'));
                        directionPrompt.useBasePoint = true;
                        directionPrompt.useDashedLine = true;
                        directionPrompt.basePoint = new AcGePoint3d(start);
                        return [4 /*yield*/, AcApDocManager.instance.editor.getPoint(directionPrompt)];
                    case 5:
                        directionResult = _a.sent();
                        if (directionResult.status !== AcEdPromptStatus.OK)
                            return [2 /*return*/];
                        directionPoint = directionResult.value;
                        direction = Math.atan2(directionPoint.y - start.y, directionPoint.x - start.x);
                        arc = createArcFromStartEndDirection(start, end, direction);
                        if (arc) {
                            this.appendArc(context, arc);
                        }
                        else {
                            this.warnInvalidGeometry('direction');
                        }
                        return [2 /*return*/];
                    case 6:
                        if (!(centerOrOptionResult.stringResult === 'Radius')) return [3 /*break*/, 8];
                        radiusPrompt = new AcEdPromptDistanceOptions(AcApI18n.t('jig.arc.radius'));
                        radiusPrompt.allowZero = false;
                        radiusPrompt.allowNegative = true;
                        return [4 /*yield*/, AcApDocManager.instance.editor.getDistance(radiusPrompt)];
                    case 7:
                        radiusResult = _a.sent();
                        if (radiusResult.status !== AcEdPromptStatus.OK)
                            return [2 /*return*/];
                        arc = createArcFromStartEndRadius(start, end, this.applyCtrlDirectionValue(radiusResult.value));
                        if (arc) {
                            this.appendArc(context, arc);
                        }
                        else {
                            this.warnInvalidGeometry('radius');
                        }
                        _a.label = 8;
                    case 8: return [2 /*return*/];
                }
            });
        });
    };
    /**
     * Converts degrees to radians.
     *
     * @param degree - Angle in degrees.
     * @returns Angle in radians.
     */
    AcApArcCmd.prototype.degToRad = function (degree) {
        return (degree * Math.PI) / 180;
    };
    return AcApArcCmd;
}(AcEdCommand));
export { AcApArcCmd };
//# sourceMappingURL=AcApArcCmd.js.map