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 { AcDbLine, AcGiLineWeight } from '@mlightcad/data-model';
import { AcEdCommand, AcEdCorsorType, AcEdOpenMode, AcEdPreviewJig, AcEdPromptPointOptions, AcEdPromptStatus, AcEdViewMode } from '../editor';
import { AcApI18n } from '../i18n';
import { cssColor, makeBadge, makeDot, makeLiveBadge, makeOverlayCanvas, measurementColor } from '../util';
import { registerMeasurementCleanup } from './AcApClearMeasurementsCmd';
/** Returns the angle in degrees between two arms sharing a common vertex. */
function calcAngleDeg(vertex, arm1, arm2) {
    var dx1 = arm1.x - vertex.x;
    var dy1 = arm1.y - vertex.y;
    var dx2 = arm2.x - vertex.x;
    var dy2 = arm2.y - vertex.y;
    var dot = dx1 * dx2 + dy1 * dy2;
    var cross = dx1 * dy2 - dy1 * dx2;
    var rad = Math.atan2(Math.abs(cross), dot);
    return (rad * 180) / Math.PI;
}
/** Normalises an angle into [0, 2pi). */
var normaliseAngle = function (a) {
    return ((a % (2 * Math.PI)) + 2 * Math.PI) % (2 * Math.PI);
};
/**
 * Draws the first arm (vertex->arm1) as a dashed line on a canvas overlay,
 * used during second arm selection so the user can see the first arm.
 */
function drawArm1OnCanvas(canvas, view, vertex, arm1, 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 sv = view.worldToScreen(vertex);
    var sa = view.worldToScreen(arm1);
    ctx.beginPath();
    ctx.moveTo(sv.x, sv.y);
    ctx.lineTo(sa.x, sa.y);
    ctx.strokeStyle = cssColor(color);
    ctx.lineWidth = 2;
    ctx.setLineDash([8, 5]);
    ctx.stroke();
    ctx.setLineDash([]);
    ctx.restore();
}
/**
 * Draws an arc between two arms on a canvas overlay for the persistent
 * angle visualization.
 */
function drawAngleArcOnCanvas(canvas, view, vertex, arm1, arm2, 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 sv = view.worldToScreen(vertex);
    var sa1 = view.worldToScreen(arm1);
    var sa2 = view.worldToScreen(arm2);
    var len1 = Math.hypot(sa1.x - sv.x, sa1.y - sv.y);
    var len2 = Math.hypot(sa2.x - sv.x, sa2.y - sv.y);
    var arcR = Math.max(Math.min(len1, len2) * 0.3, 15);
    var startAngle = Math.atan2(sa1.y - sv.y, sa1.x - sv.x);
    var endAngle = Math.atan2(sa2.y - sv.y, sa2.x - sv.x);
    var antiClockwise = normaliseAngle(endAngle - startAngle) > Math.PI;
    ctx.beginPath();
    ctx.arc(sv.x, sv.y, arcR, startAngle, endAngle, antiClockwise);
    ctx.strokeStyle = cssColor(color);
    ctx.lineWidth = 2;
    ctx.stroke();
    ctx.restore();
}
/**
 * Simple rubber-band jig for picking arm1: draws a transient line
 * from vertex to cursor.
 */
var AcApMeasureArm1Jig = /** @class */ (function (_super) {
    __extends(AcApMeasureArm1Jig, _super);
    function AcApMeasureArm1Jig(view, vertex, color) {
        var _this = _super.call(this, view) || this;
        _this._line = new AcDbLine(vertex, vertex);
        _this._line.color = color;
        _this._line.lineWeight = AcGiLineWeight.LineWeight070;
        return _this;
    }
    Object.defineProperty(AcApMeasureArm1Jig.prototype, "entity", {
        get: function () {
            return this._line;
        },
        enumerable: false,
        configurable: true
    });
    AcApMeasureArm1Jig.prototype.update = function (p) {
        this._line.endPoint = p;
    };
    return AcApMeasureArm1Jig;
}(AcEdPreviewJig));
/**
 * Preview jig for picking arm2: draws a transient line from vertex to
 * cursor, redraws the first arm as a dashed canvas line, and shows a live
 * angle badge.
 */
var AcApMeasureAngleJig = /** @class */ (function (_super) {
    __extends(AcApMeasureAngleJig, _super);
    function AcApMeasureAngleJig(view, vertex, arm1, canvas, color) {
        var _this = _super.call(this, view) || this;
        _this._vertex = vertex;
        _this._arm1 = arm1;
        _this._view = view;
        _this._canvas = canvas;
        _this._color = color;
        _this._line = new AcDbLine(vertex, vertex);
        _this._line.color = color;
        _this._line.lineWeight = AcGiLineWeight.LineWeight070;
        _this._badge = makeLiveBadge(color);
        return _this;
    }
    Object.defineProperty(AcApMeasureAngleJig.prototype, "entity", {
        get: function () {
            return this._line;
        },
        enumerable: false,
        configurable: true
    });
    AcApMeasureAngleJig.prototype.update = function (p) {
        this._line.endPoint = p;
        // Redraw the first arm dashed line (stays in sync with pan/zoom)
        drawArm1OnCanvas(this._canvas, this._view, this._vertex, this._arm1, this._color);
        var deg = calcAngleDeg(this._vertex, this._arm1, p);
        this._badge.textContent = "~ ".concat(deg.toFixed(2), "\u00B0");
        this._badge.style.display = 'block';
        var rect = this._view.canvas.getBoundingClientRect();
        var sv = this._view.worldToScreen(this._vertex);
        this._badge.style.left = "".concat(sv.x + rect.left, "px");
        this._badge.style.top = "".concat(sv.y + rect.top - 30, "px");
    };
    AcApMeasureAngleJig.prototype.end = function () {
        _super.prototype.end.call(this);
        this._badge.remove();
    };
    return AcApMeasureAngleJig;
}(AcEdPreviewJig));
/**
 * Command that measures the angle between two arms sharing a common vertex.
 *
 * Prompts the user to pick three world points: the vertex, a point on the
 * first arm, and a point on the second arm. After the second arm is confirmed,
 * transient CAD lines are added for both arms and persistent DOM overlays
 * (arc canvas + dots + badge) are placed via {@link AcTrHtmlTransientManager}.
 */
var AcApMeasureAngleCmd = /** @class */ (function (_super) {
    __extends(AcApMeasureAngleCmd, _super);
    function AcApMeasureAngleCmd() {
        var _this = _super.call(this) || this;
        _this.mode = AcEdOpenMode.Read;
        return _this;
    }
    AcApMeasureAngleCmd.prototype.execute = function (context) {
        return __awaiter(this, void 0, void 0, function () {
            var editor, color;
            var _this = this;
            return __generator(this, function (_a) {
                switch (_a.label) {
                    case 0:
                        editor = context.view.editor;
                        color = measurementColor(context.doc.database);
                        return [4 /*yield*/, context.view.withMode(AcEdViewMode.SELECTION, function () {
                                return editor.withCursor(AcEdCorsorType.Crosshair, function () { return __awaiter(_this, void 0, void 0, function () {
                                    var vertexPrompt, vertexResult, vertex, arm1Prompt, arm1Result, arm1, armCanvas, redrawOnViewChange, arm2Prompt, arm2, arm2Result, _a, degrees, line1, line2, persistCanvas, redrawPersist, htManager, id, dx1, dy1, dx2, dy2, wLen1, wLen2, u1x, u1y, u2x, u2y, bx, by, bLen, badgeOffset, badgeWorld;
                                    return __generator(this, function (_b) {
                                        switch (_b.label) {
                                            case 0:
                                                vertexPrompt = new AcEdPromptPointOptions(AcApI18n.t('jig.measureAngle.vertex'));
                                                return [4 /*yield*/, editor.getPoint(vertexPrompt)];
                                            case 1:
                                                vertexResult = _b.sent();
                                                if (vertexResult.status !== AcEdPromptStatus.OK)
                                                    return [2 /*return*/];
                                                vertex = vertexResult.value;
                                                arm1Prompt = new AcEdPromptPointOptions(AcApI18n.t('jig.measureAngle.arm1'));
                                                arm1Prompt.useBasePoint = true;
                                                arm1Prompt.jig = new AcApMeasureArm1Jig(context.view, vertex, color);
                                                return [4 /*yield*/, editor.getPoint(arm1Prompt)];
                                            case 2:
                                                arm1Result = _b.sent();
                                                if (arm1Result.status !== AcEdPromptStatus.OK)
                                                    return [2 /*return*/];
                                                arm1 = arm1Result.value;
                                                armCanvas = makeOverlayCanvas(context.view.container);
                                                drawArm1OnCanvas(armCanvas, context.view, vertex, arm1, color);
                                                redrawOnViewChange = function () {
                                                    return drawArm1OnCanvas(armCanvas, context.view, vertex, arm1, color);
                                                };
                                                context.view.events.viewChanged.addEventListener(redrawOnViewChange);
                                                arm2Prompt = new AcEdPromptPointOptions(AcApI18n.t('jig.measureAngle.arm2'));
                                                arm2Prompt.jig = new AcApMeasureAngleJig(context.view, vertex, arm1, armCanvas, color);
                                                _b.label = 3;
                                            case 3:
                                                _b.trys.push([3, 5, , 6]);
                                                return [4 /*yield*/, editor.getPoint(arm2Prompt)];
                                            case 4:
                                                arm2Result = _b.sent();
                                                if (arm2Result.status !== AcEdPromptStatus.OK) {
                                                    // User pressed ESC / cancelled — clean up construction-phase DOM
                                                    context.view.events.viewChanged.removeEventListener(redrawOnViewChange);
                                                    armCanvas.remove();
                                                    return [2 /*return*/];
                                                }
                                                arm2 = arm2Result.value;
                                                return [3 /*break*/, 6];
                                            case 5:
                                                _a = _b.sent();
                                                // User pressed ESC / cancelled — clean up construction-phase DOM
                                                context.view.events.viewChanged.removeEventListener(redrawOnViewChange);
                                                armCanvas.remove();
                                                return [2 /*return*/];
                                            case 6:
                                                // Clean up construction-phase canvas
                                                context.view.events.viewChanged.removeEventListener(redrawOnViewChange);
                                                armCanvas.remove();
                                                degrees = calcAngleDeg(vertex, arm1, arm2);
                                                line1 = new AcDbLine(vertex, arm1);
                                                line1.color = color;
                                                line1.lineWeight = AcGiLineWeight.LineWeight070;
                                                context.view.addTransientEntity(line1);
                                                line2 = new AcDbLine(vertex, arm2);
                                                line2.color = color;
                                                line2.lineWeight = AcGiLineWeight.LineWeight070;
                                                context.view.addTransientEntity(line2);
                                                persistCanvas = makeOverlayCanvas(context.view.container);
                                                drawAngleArcOnCanvas(persistCanvas, context.view, vertex, arm1, arm2, color);
                                                redrawPersist = function () {
                                                    return drawAngleArcOnCanvas(persistCanvas, context.view, vertex, arm1, arm2, color);
                                                };
                                                context.view.events.viewChanged.addEventListener(redrawPersist);
                                                htManager = context.view.htmlTransientManager;
                                                id = "angle-".concat(Date.now());
                                                htManager.add("".concat(id, "-dotV"), makeDot(color), vertex, 'measurement');
                                                htManager.add("".concat(id, "-dot1"), makeDot(color), arm1, 'measurement');
                                                htManager.add("".concat(id, "-dot2"), makeDot(color), arm2, 'measurement');
                                                dx1 = arm1.x - vertex.x;
                                                dy1 = arm1.y - vertex.y;
                                                dx2 = arm2.x - vertex.x;
                                                dy2 = arm2.y - vertex.y;
                                                wLen1 = Math.hypot(dx1, dy1);
                                                wLen2 = Math.hypot(dx2, dy2);
                                                u1x = wLen1 > 0 ? dx1 / wLen1 : 1;
                                                u1y = wLen1 > 0 ? dy1 / wLen1 : 0;
                                                u2x = wLen2 > 0 ? dx2 / wLen2 : 1;
                                                u2y = wLen2 > 0 ? dy2 / wLen2 : 0;
                                                bx = u1x + u2x;
                                                by = u1y + u2y;
                                                bLen = Math.hypot(bx, by);
                                                if (bLen > 0) {
                                                    bx /= bLen;
                                                    by /= bLen;
                                                }
                                                else {
                                                    // Arms are exactly opposite — use perpendicular
                                                    bx = -u1y;
                                                    by = u1x;
                                                }
                                                badgeOffset = Math.max(Math.min(wLen1, wLen2) * 0.4, Math.max(wLen1, wLen2) * 0.15);
                                                badgeWorld = {
                                                    x: vertex.x + bx * badgeOffset,
                                                    y: vertex.y + by * badgeOffset
                                                };
                                                htManager.add("".concat(id, "-badge"), makeBadge(color, "".concat(degrees.toFixed(2), "\u00B0")), badgeWorld, 'measurement');
                                                registerMeasurementCleanup(function () {
                                                    context.view.removeTransientEntity(line1.objectId);
                                                    context.view.removeTransientEntity(line2.objectId);
                                                    persistCanvas.remove();
                                                    context.view.events.viewChanged.removeEventListener(redrawPersist);
                                                    htManager.remove("".concat(id, "-dotV"));
                                                    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 AcApMeasureAngleCmd;
}(AcEdCommand));
export { AcApMeasureAngleCmd };
//# sourceMappingURL=AcApMeasureAngleCmd.js.map