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 { AcDbPolyline, AcGePoint2d, AcGePoint3d } from '@mlightcad/data-model';
import { AcApDocManager } from '../app';
import { AcEdCommand, AcEdOpenMode, AcEdPreviewJig, AcEdPromptPointOptions, AcEdPromptStatus, AcEdPromptStringOptions, eventBus } from '../editor';
import { AcApI18n } from '../i18n';
var MIN_SIDES = 3;
var MAX_SIDES = 1024;
var EPSILON = 1e-9;
function distance2d(p1, p2) {
    return Math.hypot(p2.x - p1.x, p2.y - p1.y);
}
function updatePolylineVertices(polyline, vertices) {
    polyline.reset(false);
    vertices.forEach(function (point, index) { return polyline.addVertexAt(index, point); });
    polyline.closed = true;
}
function buildPolygonFromCenter(center, sides, point, mode) {
    var inputRadius = distance2d(center, point);
    if (inputRadius <= EPSILON)
        return undefined;
    var step = (Math.PI * 2) / sides;
    var baseAngle = Math.atan2(point.y - center.y, point.x - center.x);
    var circumRadius = mode === 'Inscribed' ? inputRadius : inputRadius / Math.cos(Math.PI / sides);
    if (!Number.isFinite(circumRadius) || circumRadius <= EPSILON)
        return undefined;
    var firstVertexAngle = mode === 'Inscribed' ? baseAngle : baseAngle + step / 2;
    var vertices = [];
    for (var i = 0; i < sides; i++) {
        var angle = firstVertexAngle + i * step;
        vertices.push(new AcGePoint2d(center.x + circumRadius * Math.cos(angle), center.y + circumRadius * Math.sin(angle)));
    }
    return vertices;
}
function buildPolygonFromEdge(firstPoint, secondPoint, sides) {
    var dx = secondPoint.x - firstPoint.x;
    var dy = secondPoint.y - firstPoint.y;
    var edgeLength = Math.hypot(dx, dy);
    if (edgeLength <= EPSILON)
        return undefined;
    var step = (Math.PI * 2) / sides;
    var vertices = [
        new AcGePoint2d(firstPoint),
        new AcGePoint2d(secondPoint)
    ];
    var currentX = secondPoint.x;
    var currentY = secondPoint.y;
    var edgeX = dx;
    var edgeY = dy;
    for (var i = 2; i < sides; i++) {
        var rotatedX = edgeX * Math.cos(step) - edgeY * Math.sin(step);
        var rotatedY = edgeX * Math.sin(step) + edgeY * Math.cos(step);
        currentX += rotatedX;
        currentY += rotatedY;
        vertices.push(new AcGePoint2d(currentX, currentY));
        edgeX = rotatedX;
        edgeY = rotatedY;
    }
    return vertices;
}
/**
 * Dynamic preview jig for center-based polygon creation.
 */
var AcApPolygonCenterJig = /** @class */ (function (_super) {
    __extends(AcApPolygonCenterJig, _super);
    /**
     * Creates center-mode polygon preview jig.
     *
     * @param view - Active editor view.
     * @param center - Polygon center point.
     * @param sides - Number of polygon sides.
     * @param modeProvider - Callback for current inscribed/circumscribed mode.
     */
    function AcApPolygonCenterJig(view, center, sides, modeProvider) {
        var _this = _super.call(this, view) || this;
        _this._polyline = new AcDbPolyline();
        _this._center = center;
        _this._sides = sides;
        _this._modeProvider = modeProvider;
        return _this;
    }
    Object.defineProperty(AcApPolygonCenterJig.prototype, "entity", {
        /**
         * Gets transient polygon entity used by this jig.
         */
        get: function () {
            return this._polyline;
        },
        enumerable: false,
        configurable: true
    });
    /**
     * Rebuilds preview polygon from cursor radius point.
     *
     * @param point - Current cursor point.
     */
    AcApPolygonCenterJig.prototype.update = function (point) {
        var vertices = buildPolygonFromCenter(this._center, this._sides, point, this._modeProvider());
        if (!vertices)
            return;
        updatePolylineVertices(this._polyline, vertices);
    };
    return AcApPolygonCenterJig;
}(AcEdPreviewJig));
/**
 * Dynamic preview jig for edge-based polygon creation.
 */
var AcApPolygonEdgeJig = /** @class */ (function (_super) {
    __extends(AcApPolygonEdgeJig, _super);
    /**
     * Creates edge-mode polygon preview jig.
     *
     * @param view - Active editor view.
     * @param firstPoint - First endpoint of the seed edge.
     * @param sides - Number of polygon sides.
     */
    function AcApPolygonEdgeJig(view, firstPoint, sides) {
        var _this = _super.call(this, view) || this;
        _this._polyline = new AcDbPolyline();
        _this._firstPoint = firstPoint;
        _this._sides = sides;
        return _this;
    }
    Object.defineProperty(AcApPolygonEdgeJig.prototype, "entity", {
        /**
         * Gets transient polygon entity used by this jig.
         */
        get: function () {
            return this._polyline;
        },
        enumerable: false,
        configurable: true
    });
    /**
     * Rebuilds preview polygon from current second edge point.
     *
     * @param secondPoint - Current cursor point for edge endpoint.
     */
    AcApPolygonEdgeJig.prototype.update = function (secondPoint) {
        var vertices = buildPolygonFromEdge(this._firstPoint, secondPoint, this._sides);
        if (!vertices)
            return;
        updatePolylineVertices(this._polyline, vertices);
    };
    return AcApPolygonEdgeJig;
}(AcEdPreviewJig));
/**
 * Command to create one regular polygon.
 *
 * AutoCAD-like flow:
 * - Enter number of sides (3-1024)
 * - Specify center point or [Edge]
 * - Center branch: specify radius point, with [Inscribed/Circumscribed] switch
 * - Edge branch: specify first and second endpoints of one edge
 */
var AcApPolygonCmd = /** @class */ (function (_super) {
    __extends(AcApPolygonCmd, _super);
    /**
     * Creates POLYGON command instance.
     */
    function AcApPolygonCmd() {
        var _this = _super.call(this) || this;
        _this.mode = AcEdOpenMode.Write;
        return _this;
    }
    /**
     * Command entry point.
     *
     * @param context - Current application/document context.
     */
    AcApPolygonCmd.prototype.execute = function (context) {
        return __awaiter(this, void 0, void 0, function () {
            var sides, centerOrEdgePrompt, centerOrEdgeResult;
            return __generator(this, function (_a) {
                switch (_a.label) {
                    case 0: return [4 /*yield*/, this.promptSides()];
                    case 1:
                        sides = _a.sent();
                        if (sides == null)
                            return [2 /*return*/];
                        centerOrEdgePrompt = new AcEdPromptPointOptions(AcApI18n.t('jig.polygon.centerOrEdge'));
                        this.addKeyword(centerOrEdgePrompt, 'edge');
                        return [4 /*yield*/, AcApDocManager.instance.editor.getPoint(centerOrEdgePrompt)];
                    case 2:
                        centerOrEdgeResult = _a.sent();
                        if (!(centerOrEdgeResult.status === AcEdPromptStatus.Keyword)) return [3 /*break*/, 5];
                        if (!(centerOrEdgeResult.stringResult === 'Edge')) return [3 /*break*/, 4];
                        return [4 /*yield*/, this.runEdgeFlow(context, sides)];
                    case 3:
                        _a.sent();
                        _a.label = 4;
                    case 4: return [2 /*return*/];
                    case 5:
                        if (centerOrEdgeResult.status !== AcEdPromptStatus.OK)
                            return [2 /*return*/];
                        return [4 /*yield*/, this.runCenterFlow(context, sides, centerOrEdgeResult.value)];
                    case 6:
                        _a.sent();
                        return [2 /*return*/];
                }
            });
        });
    };
    /**
     * Adds one localized keyword to a point prompt.
     *
     * @param prompt - Target prompt object.
     * @param key - Keyword translation key suffix.
     */
    AcApPolygonCmd.prototype.addKeyword = function (prompt, key) {
        prompt.keywords.add(AcApI18n.t("jig.polygon.keywords.".concat(key, ".display")), AcApI18n.t("jig.polygon.keywords.".concat(key, ".global")), AcApI18n.t("jig.polygon.keywords.".concat(key, ".local")));
    };
    /**
     * Appends final polygon entity to model space.
     *
     * @param context - Current application/document context.
     * @param vertices - Polygon vertex list in world coordinates.
     */
    AcApPolygonCmd.prototype.appendPolygon = function (context, vertices) {
        var polygon = new AcDbPolyline();
        updatePolylineVertices(polygon, vertices);
        context.doc.database.tables.blockTable.modelSpace.appendEntity(polygon);
    };
    /**
     * Emits warning message for invalid polygon input.
     *
     * @param key - Invalid-input category.
     */
    AcApPolygonCmd.prototype.warnInvalidInput = function (key) {
        eventBus.emit('message', {
            message: AcApI18n.t("jig.polygon.invalid.".concat(key)),
            type: 'warning'
        });
    };
    /**
     * Prompts and validates polygon side count.
     *
     * @returns Valid side count, or `undefined` on cancel.
     */
    AcApPolygonCmd.prototype.promptSides = function () {
        return __awaiter(this, void 0, void 0, function () {
            var defaultSides, sidesPrompt, sidesResult, rawInput, rawValue;
            var _a;
            return __generator(this, function (_b) {
                switch (_b.label) {
                    case 0:
                        if (!true) return [3 /*break*/, 2];
                        defaultSides = AcApPolygonCmd._lastSides;
                        sidesPrompt = new AcEdPromptStringOptions("".concat(AcApI18n.t('jig.polygon.numberOfSides'), " <").concat(defaultSides, ">"));
                        sidesPrompt.allowEmpty = true;
                        sidesPrompt.allowSpaces = false;
                        return [4 /*yield*/, AcApDocManager.instance.editor.getString(sidesPrompt)];
                    case 1:
                        sidesResult = _b.sent();
                        if (sidesResult.status !== AcEdPromptStatus.OK)
                            return [2 /*return*/, undefined];
                        rawInput = ((_a = sidesResult.stringResult) !== null && _a !== void 0 ? _a : '').trim();
                        rawValue = rawInput === '' ? defaultSides : Number(rawInput);
                        if (Number.isInteger(rawValue) &&
                            rawValue >= MIN_SIDES &&
                            rawValue <= MAX_SIDES) {
                            AcApPolygonCmd._lastSides = rawValue;
                            return [2 /*return*/, rawValue];
                        }
                        this.warnInvalidInput('sides');
                        return [3 /*break*/, 0];
                    case 2: return [2 /*return*/];
                }
            });
        });
    };
    /**
     * Runs center-based polygon flow.
     *
     * @param context - Current application/document context.
     * @param sides - Number of polygon sides.
     * @param center - Picked polygon center point.
     */
    AcApPolygonCmd.prototype.runCenterFlow = function (context, sides, center) {
        return __awaiter(this, void 0, void 0, function () {
            var mode, radiusPrompt, radiusResult, keyword, vertices;
            var _a;
            return __generator(this, function (_b) {
                switch (_b.label) {
                    case 0:
                        mode = 'Inscribed';
                        _b.label = 1;
                    case 1:
                        if (!true) return [3 /*break*/, 3];
                        radiusPrompt = new AcEdPromptPointOptions(AcApI18n.t('jig.polygon.radiusOrType'));
                        this.addKeyword(radiusPrompt, 'inscribed');
                        this.addKeyword(radiusPrompt, 'circumscribed');
                        radiusPrompt.useDashedLine = true;
                        radiusPrompt.useBasePoint = true;
                        radiusPrompt.basePoint = new AcGePoint3d(center);
                        radiusPrompt.jig = new AcApPolygonCenterJig(context.view, center, sides, function () { return mode; });
                        return [4 /*yield*/, AcApDocManager.instance.editor.getPoint(radiusPrompt)];
                    case 2:
                        radiusResult = _b.sent();
                        if (radiusResult.status === AcEdPromptStatus.Keyword) {
                            keyword = (_a = radiusResult.stringResult) !== null && _a !== void 0 ? _a : '';
                            if (keyword === 'Inscribed') {
                                mode = 'Inscribed';
                                return [3 /*break*/, 1];
                            }
                            if (keyword === 'Circumscribed') {
                                mode = 'Circumscribed';
                                return [3 /*break*/, 1];
                            }
                            return [2 /*return*/];
                        }
                        if (radiusResult.status !== AcEdPromptStatus.OK)
                            return [2 /*return*/];
                        vertices = buildPolygonFromCenter(center, sides, radiusResult.value, mode);
                        if (!vertices) {
                            this.warnInvalidInput('radius');
                            return [3 /*break*/, 1];
                        }
                        this.appendPolygon(context, vertices);
                        return [2 /*return*/];
                    case 3: return [2 /*return*/];
                }
            });
        });
    };
    /**
     * Runs edge-based polygon flow.
     *
     * @param context - Current application/document context.
     * @param sides - Number of polygon sides.
     */
    AcApPolygonCmd.prototype.runEdgeFlow = function (context, sides) {
        return __awaiter(this, void 0, void 0, function () {
            var firstEdgePrompt, firstEdgeResult, firstPoint, secondEdgePrompt, secondEdgeResult, vertices;
            return __generator(this, function (_a) {
                switch (_a.label) {
                    case 0:
                        firstEdgePrompt = new AcEdPromptPointOptions(AcApI18n.t('jig.polygon.edgeStart'));
                        return [4 /*yield*/, AcApDocManager.instance.editor.getPoint(firstEdgePrompt)];
                    case 1:
                        firstEdgeResult = _a.sent();
                        if (firstEdgeResult.status !== AcEdPromptStatus.OK)
                            return [2 /*return*/];
                        firstPoint = firstEdgeResult.value;
                        secondEdgePrompt = new AcEdPromptPointOptions(AcApI18n.t('jig.polygon.edgeEnd'));
                        secondEdgePrompt.useDashedLine = true;
                        secondEdgePrompt.useBasePoint = true;
                        secondEdgePrompt.basePoint = new AcGePoint3d(firstPoint);
                        secondEdgePrompt.jig = new AcApPolygonEdgeJig(context.view, firstPoint, sides);
                        return [4 /*yield*/, AcApDocManager.instance.editor.getPoint(secondEdgePrompt)];
                    case 2:
                        secondEdgeResult = _a.sent();
                        if (secondEdgeResult.status !== AcEdPromptStatus.OK)
                            return [2 /*return*/];
                        vertices = buildPolygonFromEdge(firstPoint, secondEdgeResult.value, sides);
                        if (!vertices) {
                            this.warnInvalidInput('edge');
                            return [2 /*return*/];
                        }
                        this.appendPolygon(context, vertices);
                        return [2 /*return*/];
                }
            });
        });
    };
    /**
     * Last accepted side count used as default for next command run.
     */
    AcApPolygonCmd._lastSides = 4;
    return AcApPolygonCmd;
}(AcEdCommand));
export { AcApPolygonCmd };
//# sourceMappingURL=AcApPolygonCmd.js.map