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 };
    }
};
var __values = (this && this.__values) || function(o) {
    var s = typeof Symbol === "function" && Symbol.iterator, m = s && o[s], i = 0;
    if (m) return m.call(o);
    if (o && typeof o.length === "number") return {
        next: function () {
            if (o && i >= o.length) o = void 0;
            return { value: o && o[i++], done: !o };
        }
    };
    throw new TypeError(s ? "Object is not iterable." : "Symbol.iterator is not defined.");
};
import { AcDbArc, AcDbCircle, AcDbLine } from '@mlightcad/data-model';
import { AcEdCommand, AcEdCorsorType, AcEdOpenMode, AcEdPreviewJig, AcEdPromptPointOptions, AcEdPromptStatus, AcEdViewMode } from '../editor';
import { AcApI18n } from '../i18n';
import { cssColor, makeBadge, makeDot, makeLiveBadge, makeLiveDot, makeOverlayCanvas, makeSnapIndicator, measurementColor } from '../util';
import { registerMeasurementCleanup } from './AcApClearMeasurementsCmd';
/** Returns the world point snapped to the nearest point on the circle circumference */
function snapToCircle(p, g) {
    var angle = Math.atan2(p.y - g.cy, p.x - g.cx);
    return {
        x: g.cx + g.r * Math.cos(angle),
        y: g.cy + g.r * Math.sin(angle),
        z: 0
    };
}
/** Returns the shorter arc length between two points on a circle */
function shortArcLength(p1, p2, g) {
    var a1 = Math.atan2(p1.y - g.cy, p1.x - g.cx);
    var a2 = Math.atan2(p2.y - g.cy, p2.x - g.cx);
    var norm = function (a) {
        return ((a % (2 * Math.PI)) + 2 * Math.PI) % (2 * Math.PI);
    };
    var span = norm(a2 - a1);
    return Math.min(span, 2 * Math.PI - span) * g.r;
}
/** World midpoint of the shorter arc between two circle points */
function shortArcMid(p1, p2, g) {
    var a1 = Math.atan2(p1.y - g.cy, p1.x - g.cx);
    var a2 = Math.atan2(p2.y - g.cy, p2.x - g.cx);
    var norm = function (a) {
        return ((a % (2 * Math.PI)) + 2 * Math.PI) % (2 * Math.PI);
    };
    var ccwSpan = norm(a2 - a1);
    var mid = ccwSpan <= Math.PI ? a1 + ccwSpan / 2 : a1 - (2 * Math.PI - ccwSpan) / 2;
    return { x: g.cx + g.r * Math.cos(mid), y: g.cy + g.r * Math.sin(mid), z: 0 };
}
/**
 * Draws the arc between two snapped circle points onto a canvas element.
 * Uses screen-space angles so the Y-axis flip is handled automatically.
 */
function drawArcOnCanvas(canvas, view, g, p1, p2, color) {
    var rect = view.canvas.getBoundingClientRect();
    var dpr = window.devicePixelRatio || 1;
    var w = Math.round(rect.width);
    var h = Math.round(rect.height);
    var origin = view.canvasToContainer({ x: 0, y: 0 });
    canvas.style.left = "".concat(origin.x, "px");
    canvas.style.top = "".concat(origin.y, "px");
    canvas.style.width = "".concat(w, "px");
    canvas.style.height = "".concat(h, "px");
    if (canvas.width !== w * dpr || canvas.height !== h * dpr) {
        canvas.width = w * dpr;
        canvas.height = h * dpr;
    }
    var ctx = canvas.getContext('2d');
    if (!ctx)
        return;
    ctx.clearRect(0, 0, canvas.width, canvas.height);
    ctx.save();
    ctx.scale(dpr, dpr);
    var sc = view.worldToScreen({ x: g.cx, y: g.cy });
    var ss = view.worldToScreen(p1);
    var se = view.worldToScreen(p2);
    var screenR = Math.hypot(ss.x - sc.x, ss.y - sc.y);
    var sa = Math.atan2(ss.y - sc.y, ss.x - sc.x);
    var ea = Math.atan2(se.y - sc.y, se.x - sc.x);
    var norm = function (a) {
        return ((a % (2 * Math.PI)) + 2 * Math.PI) % (2 * Math.PI);
    };
    var cwSpan = norm(ea - sa);
    var antiClockwise = cwSpan > Math.PI;
    ctx.beginPath();
    ctx.arc(sc.x, sc.y, screenR, sa, ea, antiClockwise);
    ctx.strokeStyle = cssColor(color);
    ctx.lineWidth = 4;
    ctx.stroke();
    ctx.restore();
}
/**
 * Jig for the first point: shows a small square snap indicator when the
 * cursor hovers over a circle or arc entity. Notifies the caller via onSnap.
 */
var AcApArcSnapJig = /** @class */ (function (_super) {
    __extends(AcApArcSnapJig, _super);
    function AcApArcSnapJig(context, color, onSnap) {
        var _this = _super.call(this, context.view) || this;
        _this._ctx = context;
        _this._onSnap = onSnap;
        var o = { x: 0, y: 0, z: 0 };
        _this._dummy = new AcDbLine(o, o);
        _this._indicator = makeSnapIndicator(color);
        return _this;
    }
    Object.defineProperty(AcApArcSnapJig.prototype, "entity", {
        get: function () {
            return this._dummy;
        },
        enumerable: false,
        configurable: true
    });
    AcApArcSnapJig.prototype.update = function (p) {
        var e_1, _a;
        var hits = this._ctx.view.pick(p);
        var modelSpace = this._ctx.doc.database.tables.blockTable.modelSpace;
        // Collect all circle/arc candidates, then pick the one whose
        // circumference is closest to the cursor (fixes wrong-arc selection
        // when multiple arcs overlap in the pick area).
        var bestGeom = null;
        var bestDist = Number.MAX_VALUE;
        try {
            for (var hits_1 = __values(hits), hits_1_1 = hits_1.next(); !hits_1_1.done; hits_1_1 = hits_1.next()) {
                var hit = hits_1_1.value;
                var entity = modelSpace.getIdAt(hit.id);
                var geom = null;
                if (entity instanceof AcDbCircle) {
                    geom = { cx: entity.center.x, cy: entity.center.y, r: entity.radius };
                }
                else if (entity instanceof AcDbArc) {
                    geom = { cx: entity.center.x, cy: entity.center.y, r: entity.radius };
                }
                if (geom) {
                    // Distance from cursor to circumference
                    var distToCenter = Math.hypot(p.x - geom.cx, p.y - geom.cy);
                    var distToCircumference = Math.abs(distToCenter - geom.r);
                    if (distToCircumference < bestDist) {
                        bestDist = distToCircumference;
                        bestGeom = geom;
                    }
                }
            }
        }
        catch (e_1_1) { e_1 = { error: e_1_1 }; }
        finally {
            try {
                if (hits_1_1 && !hits_1_1.done && (_a = hits_1.return)) _a.call(hits_1);
            }
            finally { if (e_1) throw e_1.error; }
        }
        if (bestGeom) {
            // Only snap when cursor is close enough to the circumference in
            // screen space (20 px threshold) — prevents snapping from far away.
            var snapped = snapToCircle(p, bestGeom);
            var cursorScreen = this._ctx.view.worldToScreen(p);
            var snapScreen = this._ctx.view.worldToScreen(snapped);
            var screenDist = Math.hypot(cursorScreen.x - snapScreen.x, cursorScreen.y - snapScreen.y);
            if (screenDist <= 20) {
                var rect = this._ctx.view.canvas.getBoundingClientRect();
                this._indicator.style.left = "".concat(snapScreen.x + rect.left, "px");
                this._indicator.style.top = "".concat(snapScreen.y + rect.top, "px");
                this._indicator.style.display = 'block';
                this._onSnap(bestGeom, snapped);
                return;
            }
        }
        this._indicator.style.display = 'none';
        this._onSnap(null, null);
    };
    AcApArcSnapJig.prototype.end = function () {
        _super.prototype.end.call(this);
        this._indicator.remove();
    };
    return AcApArcSnapJig;
}(AcEdPreviewJig));
/**
 * Jig for the second point: snaps cursor to the known circle, shows the
 * square snap indicator, and fires onMove with the already-snapped point.
 */
var AcApArcEndSnapJig = /** @class */ (function (_super) {
    __extends(AcApArcEndSnapJig, _super);
    function AcApArcEndSnapJig(context, geom, color, onMove) {
        var _this = _super.call(this, context.view) || this;
        _this._ctx = context;
        _this._geom = geom;
        _this._onMove = onMove;
        var o = { x: 0, y: 0, z: 0 };
        _this._dummy = new AcDbLine(o, o);
        _this._indicator = makeSnapIndicator(color);
        _this._indicator.style.display = 'block';
        return _this;
    }
    Object.defineProperty(AcApArcEndSnapJig.prototype, "entity", {
        get: function () {
            return this._dummy;
        },
        enumerable: false,
        configurable: true
    });
    AcApArcEndSnapJig.prototype.update = function (p) {
        var snapped = snapToCircle(p, this._geom);
        var rect = this._ctx.view.canvas.getBoundingClientRect();
        var sp = this._ctx.view.worldToScreen(snapped);
        this._indicator.style.left = "".concat(sp.x + rect.left, "px");
        this._indicator.style.top = "".concat(sp.y + rect.top, "px");
        this._onMove(snapped);
    };
    AcApArcEndSnapJig.prototype.end = function () {
        _super.prototype.end.call(this);
        this._indicator.remove();
    };
    return AcApArcEndSnapJig;
}(AcEdPreviewJig));
/**
 * Command that measures the length of an arc on an existing circle or arc entity.
 *
 * Uses a two-phase pick flow that mirrors AutoCAD's arc-length measurement:
 *
 * 1. **Phase 1 — entity snap**: The user hovers over a circle or arc in the
 *    drawing. A square snap indicator appears on the circumference and the
 *    entity geometry is captured. Clicking confirms the start point.
 *
 * 2. **Phase 2 — end point**: The same square indicator follows the cursor,
 *    always locked to the captured circle. A canvas overlay draws the shorter
 *    arc between the start and current position in real time, together with a
 *    live badge showing the arc length.
 *
 * Persistent overlays are placed via {@link AcTrHtmlTransientManager} for dots
 * and badge. The arc canvas is managed with a viewChanged listener cleaned up
 * via {@link registerMeasurementCleanup}.
 */
var AcApMeasureArcCmd = /** @class */ (function (_super) {
    __extends(AcApMeasureArcCmd, _super);
    function AcApMeasureArcCmd() {
        var _this = _super.call(this) || this;
        _this.mode = AcEdOpenMode.Read;
        return _this;
    }
    AcApMeasureArcCmd.prototype.execute = function (context) {
        return __awaiter(this, void 0, void 0, function () {
            var editor, color, arcCanvas;
            var _this = this;
            return __generator(this, function (_a) {
                switch (_a.label) {
                    case 0:
                        editor = context.view.editor;
                        color = measurementColor(context.doc.database);
                        arcCanvas = makeOverlayCanvas(context.view.container);
                        return [4 /*yield*/, context.view.withMode(AcEdViewMode.SELECTION, function () {
                                return editor.withCursor(AcEdCorsorType.Crosshair, function () { return __awaiter(_this, void 0, void 0, function () {
                                    var snapGeom, snappedStart, snapJig, p1Prompt, p1Result, _a, geom, start, dot1, liveBadge, reposDot1, redrawPreview, onViewChangedPreview, onMove, p2Prompt, p2Raw, p2Result, _b, end, arcLen, mid, persistCanvas, redrawPersist, htManager, id;
                                    return __generator(this, function (_c) {
                                        switch (_c.label) {
                                            case 0:
                                                snapGeom = null;
                                                snappedStart = null;
                                                snapJig = new AcApArcSnapJig(context, color, function (geom, snapped) {
                                                    snapGeom = geom;
                                                    snappedStart = snapped;
                                                });
                                                p1Prompt = new AcEdPromptPointOptions(AcApI18n.t('jig.measureArc.startPoint'));
                                                p1Prompt.jig = snapJig;
                                                _c.label = 1;
                                            case 1:
                                                _c.trys.push([1, 3, , 4]);
                                                return [4 /*yield*/, editor.getPoint(p1Prompt)];
                                            case 2:
                                                p1Result = _c.sent();
                                                if (p1Result.status !== AcEdPromptStatus.OK) {
                                                    arcCanvas.remove();
                                                    return [2 /*return*/];
                                                }
                                                return [3 /*break*/, 4];
                                            case 3:
                                                _a = _c.sent();
                                                arcCanvas.remove();
                                                return [2 /*return*/];
                                            case 4:
                                                if (!snapGeom || !snappedStart) {
                                                    arcCanvas.remove();
                                                    return [2 /*return*/];
                                                }
                                                geom = snapGeom;
                                                start = snappedStart;
                                                dot1 = makeLiveDot(color);
                                                liveBadge = makeLiveBadge(color);
                                                reposDot1 = function () {
                                                    var rect = context.view.canvas.getBoundingClientRect();
                                                    var sp = context.view.worldToScreen(start);
                                                    dot1.style.left = "".concat(sp.x + rect.left, "px");
                                                    dot1.style.top = "".concat(sp.y + rect.top, "px");
                                                };
                                                reposDot1();
                                                redrawPreview = function () {
                                                    return drawArcOnCanvas(arcCanvas, context.view, geom, start, start, color);
                                                };
                                                onViewChangedPreview = function () {
                                                    reposDot1();
                                                    redrawPreview();
                                                };
                                                context.view.events.viewChanged.addEventListener(onViewChangedPreview);
                                                onMove = function (snapped) {
                                                    drawArcOnCanvas(arcCanvas, context.view, geom, start, snapped, color);
                                                    var len = shortArcLength(start, snapped, geom);
                                                    liveBadge.textContent = "~ ".concat(len.toFixed(4), " m");
                                                    liveBadge.style.display = '';
                                                    var mid = shortArcMid(start, snapped, geom);
                                                    var rect = context.view.canvas.getBoundingClientRect();
                                                    var sm = context.view.worldToScreen(mid);
                                                    liveBadge.style.left = "".concat(sm.x + rect.left, "px");
                                                    liveBadge.style.top = "".concat(sm.y + rect.top, "px");
                                                    reposDot1();
                                                };
                                                p2Prompt = new AcEdPromptPointOptions(AcApI18n.t('jig.measureArc.endPoint'));
                                                p2Prompt.jig = new AcApArcEndSnapJig(context, geom, color, onMove);
                                                _c.label = 5;
                                            case 5:
                                                _c.trys.push([5, 7, , 8]);
                                                return [4 /*yield*/, editor.getPoint(p2Prompt)];
                                            case 6:
                                                p2Result = _c.sent();
                                                if (p2Result.status !== AcEdPromptStatus.OK) {
                                                    arcCanvas.remove();
                                                    dot1.remove();
                                                    liveBadge.remove();
                                                    context.view.events.viewChanged.removeEventListener(onViewChangedPreview);
                                                    return [2 /*return*/];
                                                }
                                                p2Raw = p2Result.value;
                                                return [3 /*break*/, 8];
                                            case 7:
                                                _b = _c.sent();
                                                arcCanvas.remove();
                                                dot1.remove();
                                                liveBadge.remove();
                                                context.view.events.viewChanged.removeEventListener(onViewChangedPreview);
                                                return [2 /*return*/];
                                            case 8:
                                                // Clean up construction-phase elements
                                                liveBadge.remove();
                                                dot1.remove();
                                                context.view.events.viewChanged.removeEventListener(onViewChangedPreview);
                                                arcCanvas.remove();
                                                end = snapToCircle(p2Raw, geom);
                                                arcLen = shortArcLength(start, end, geom);
                                                mid = shortArcMid(start, end, geom);
                                                persistCanvas = makeOverlayCanvas(context.view.container);
                                                drawArcOnCanvas(persistCanvas, context.view, geom, start, end, color);
                                                redrawPersist = function () {
                                                    return drawArcOnCanvas(persistCanvas, context.view, geom, start, end, color);
                                                };
                                                context.view.events.viewChanged.addEventListener(redrawPersist);
                                                htManager = context.view.htmlTransientManager;
                                                id = "arc-".concat(Date.now());
                                                htManager.add("".concat(id, "-dot1"), makeDot(color), start, 'measurement');
                                                htManager.add("".concat(id, "-dot2"), makeDot(color), end, 'measurement');
                                                htManager.add("".concat(id, "-badge"), makeBadge(color, "~ ".concat(arcLen.toFixed(4), " m")), mid, 'measurement');
                                                registerMeasurementCleanup(function () {
                                                    persistCanvas.remove();
                                                    context.view.events.viewChanged.removeEventListener(redrawPersist);
                                                    htManager.remove("".concat(id, "-dot1"));
                                                    htManager.remove("".concat(id, "-dot2"));
                                                    htManager.remove("".concat(id, "-badge"));
                                                });
                                                return [2 /*return*/];
                                        }
                                    });
                                }); });
                            })];
                    case 1:
                        _a.sent();
                        return [2 /*return*/];
                }
            });
        });
    };
    return AcApMeasureArcCmd;
}(AcEdCommand));
export { AcApMeasureArcCmd };
//# sourceMappingURL=AcApMeasureArcCmd.js.map