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 __read = (this && this.__read) || function (o, n) {
    var m = typeof Symbol === "function" && o[Symbol.iterator];
    if (!m) return o;
    var i = m.call(o), r, ar = [], e;
    try {
        while ((n === void 0 || n-- > 0) && !(r = i.next()).done) ar.push(r.value);
    }
    catch (error) { e = { error: error }; }
    finally {
        try {
            if (r && !r.done && (m = i["return"])) m.call(i);
        }
        finally { if (e) throw e.error; }
    }
    return ar;
};
var __spreadArray = (this && this.__spreadArray) || function (to, from, pack) {
    if (pack || arguments.length === 2) for (var i = 0, l = from.length, ar; i < l; i++) {
        if (ar || !(i in from)) {
            if (!ar) ar = Array.prototype.slice.call(from, 0, i);
            ar[i] = from[i];
        }
    }
    return to.concat(ar || Array.prototype.slice.call(from));
};
import { AcCmEventManager, AcDbDatabaseConverterManager, AcDbDxfConverter, AcDbFileType, acdbHostApplicationServices, AcDbSysVarManager, AcGeBox2d, log } from '@mlightcad/data-model';
import { AcDbLibreDwgConverter } from '@mlightcad/libredwg-converter';
import { AcTrMTextRenderer } from '@mlightcad/three-renderer';
import { AcApArcCmd, AcApCircleCmd, AcApClearMeasurementsCmd, AcApConvertToDxfCmd, AcApConvertToPngCmd, AcApConvertToSvgCmd, AcApDimLinearCmd, AcApEllipseCmd, AcApEraseCmd, AcApHatchCmd, AcApLayerCmd, AcApLineCmd, AcApLogCmd, AcApMeasureAngleCmd, AcApMeasureArcCmd, AcApMeasureAreaCmd, AcApMeasureDistanceCmd, AcApMoveCmd, AcApMTextCmd, AcApOpenCmd, AcApPanCmd, AcApPolygonCmd, AcApPolylineCmd, AcApQNewCmd, AcApRectCmd, AcApRegenCmd, AcApRevCircleCmd, AcApRevCloudCmd, AcApRevRectCmd, AcApRevVisibilityCmd, AcApSelectCmd, AcApSketchCmd, AcApSplineCmd, AcApSwitchBgCmd, AcApSysVarCmd, AcApZoomCmd, AcEdCommandStack } from '../command';
import { AcEdOpenMode, eventBus } from '../editor';
import { AcApI18n } from '../i18n';
import { AcApPluginManager } from '../plugin/AcApPluginManager';
import { AcTrView2d } from '../view';
import { AcApContext } from './AcApContext';
import { AcApDocument } from './AcApDocument';
import { AcApFontLoader } from './AcApFontLoader';
import { AcApProgress } from './AcApProgress';
var DEFAULT_BASE_URL = 'https://mlightcad.gitlab.io/cad-data/';
/**
 * Built-in command alias table used when users do not provide explicit alias overrides.
 *
 * Rules:
 * - Key is the command global name in uppercase.
 * - Value is one or more aliases in uppercase.
 * - This table is intentionally partial; commands not listed here simply have no default aliases.
 *
 * Notes:
 * - Runtime lookup is case-insensitive because all aliases are normalized to uppercase.
 * - User-provided aliases in `AcApDocManagerOptions.commandAliases` have higher priority
 *   and will fully replace the defaults for the same command name.
 */
var DEFAULT_COMMAND_ALIASES = {
    ARC: ['A'],
    CIRCLE: ['C'],
    ELLIPSE: ['EL'],
    ERASE: ['E'],
    DIMLINEAR: ['DLI'],
    MEASUREDISTANCE: ['DI', 'DIST'],
    MEASUREAREA: ['AA', 'AREA'],
    MEASUREANGLE: ['ANG'],
    HATCH: ['H'],
    '-LAYER': ['LA'],
    LINE: ['L'],
    MTEXT: ['T'],
    MOVE: ['M'],
    OPEN: ['OP'],
    PAN: ['P'],
    POLYGON: ['POL'],
    PLINE: ['PL'],
    RECTANGLE: ['REC'],
    REGEN: ['RE'],
    SELECT: ['SE'],
    SPLINE: ['SPL'],
    ZOOM: ['Z']
};
/**
 * Document manager that handles CAD document lifecycle and provides the main entry point for the CAD viewer.
 *
 * This singleton class manages:
 * - Document creation and opening (from URLs or file content)
 * - View and context management
 * - Command registration and execution
 * - Font loading for text rendering
 * - Event handling for document lifecycle
 *
 * The manager follows a singleton pattern to ensure only one instance manages the application state.
 */
var AcApDocManager = /** @class */ (function () {
    /**
     * Private constructor for singleton pattern.
     *
     * Creates an empty document with a 2D view and sets up the application context.
     * Registers default commands and creates an example document.
     *
     * @param options -Options for creating AcApDocManager instance
     * @private
     */
    function AcApDocManager(options) {
        if (options === void 0) { options = {}; }
        var _this = this;
        var _a, _b, _c;
        /** Events fired during document lifecycle */
        this.events = {
            /** Fired when a new document is created */
            documentCreated: new AcCmEventManager(),
            /** Fired when a document becomes active */
            documentActivated: new AcCmEventManager()
        };
        this._baseUrl = (_a = options.baseUrl) !== null && _a !== void 0 ? _a : DEFAULT_BASE_URL;
        this._commandAliasOverrides = this.normalizeCommandAliasConfig(options.commandAliases);
        if (options.useMainThreadDraw) {
            AcTrMTextRenderer.getInstance().setRenderMode('main');
        }
        else {
            AcTrMTextRenderer.getInstance().setRenderMode('worker');
        }
        // Create one empty drawing
        var doc = new AcApDocument();
        doc.database.events.openProgress.addEventListener(function (args) {
            var progress = {
                database: doc.database,
                percentage: args.percentage,
                stage: args.stage,
                subStage: args.subStage,
                subStageStatus: args.subStageStatus,
                data: args.data
            };
            eventBus.emit('open-file-progress', progress);
            _this.updateProgress(progress);
            // After doc header is loaded, need to set global ltscale and celtscale
            // It's too late when subStage is 'END'
            if (args.subStage === 'HEADER') {
                _this.curView.ltscale = doc.database.ltscale;
                _this.curView.celtscale = doc.database.celtscale;
                _this.curView.renderer.showLineWeight = doc.database.lwdisplay;
            }
        });
        var initialSize = (_c = (_b = options.container) === null || _b === void 0 ? void 0 : _b.getBoundingClientRect()) !== null && _c !== void 0 ? _c : {
            width: 300,
            height: 150
        };
        var callback = function () {
            var _a, _b, _c, _d, _e;
            if (options.autoResize) {
                var box = (_a = options.container) === null || _a === void 0 ? void 0 : _a.getBoundingClientRect();
                return {
                    width: (_b = box === null || box === void 0 ? void 0 : box.width) !== null && _b !== void 0 ? _b : initialSize.width,
                    height: (_c = box === null || box === void 0 ? void 0 : box.height) !== null && _c !== void 0 ? _c : initialSize.height
                };
            }
            else {
                return {
                    width: (_d = options.width) !== null && _d !== void 0 ? _d : initialSize.width,
                    height: (_e = options.height) !== null && _e !== void 0 ? _e : initialSize.height
                };
            }
        };
        var view = new AcTrView2d({
            container: options.container,
            calculateSizeCallback: callback
        });
        this._context = new AcApContext(view, doc);
        this._fontLoader = new AcApFontLoader();
        this._fontLoader.baseUrl = this._baseUrl + 'fonts/';
        acdbHostApplicationServices().workingDatabase = doc.database;
        this._commandManager = new AcEdCommandStack();
        this.registerCommands();
        this._pluginManager = new AcApPluginManager(this._context, this._commandManager);
        this._progress = new AcApProgress({ host: view.container });
        this._progress.hide();
        if (!options.notLoadDefaultFonts) {
            this.loadDefaultFonts();
        }
        this.registerWorkers(options.webworkerFileUrls);
        // Load plugins asynchronously (don't await to avoid blocking initialization)
        this.loadPlugins(options.plugins).catch(function (error) {
            log.error('[AcApDocManager] Error loading plugins:', error);
        });
    }
    /**
     * Creates the singleton instance with an optional canvas element.
     *
     * This method should be called before accessing the `instance` property
     * if you want to provide a specific canvas element.
     *
     * @param options -Options for creating AcApDocManager instance
     * @returns The singleton instance
     */
    AcApDocManager.createInstance = function (options) {
        if (options === void 0) { options = {}; }
        if (AcApDocManager._instance == null) {
            AcApDocManager._instance = new AcApDocManager(options);
        }
        return this._instance;
    };
    Object.defineProperty(AcApDocManager, "instance", {
        /**
         * Gets the singleton instance of the document manager.
         *
         * Creates a new instance if one doesn't exist yet.
         *
         * @returns The singleton document manager instance
         */
        get: function () {
            if (!AcApDocManager._instance) {
                AcApDocManager._instance = new AcApDocManager();
            }
            return AcApDocManager._instance;
        },
        enumerable: false,
        configurable: true
    });
    /**
     * Destroy the view and unload all plugins
     */
    AcApDocManager.prototype.destroy = function () {
        return __awaiter(this, void 0, void 0, function () {
            return __generator(this, function (_a) {
                switch (_a.label) {
                    case 0: return [4 /*yield*/, this._pluginManager.unloadAllPlugins()];
                    case 1:
                        _a.sent();
                        AcApDocManager._instance = undefined;
                        return [2 /*return*/];
                }
            });
        });
    };
    Object.defineProperty(AcApDocManager.prototype, "context", {
        /**
         * Gets the current application context.
         *
         * The context binds the current document with its associated view.
         *
         * @returns The current application context
         */
        get: function () {
            return this._context;
        },
        enumerable: false,
        configurable: true
    });
    Object.defineProperty(AcApDocManager.prototype, "curDocument", {
        /**
         * Gets the currently open CAD document.
         *
         * @returns The current document instance
         */
        get: function () {
            return this._context.doc;
        },
        enumerable: false,
        configurable: true
    });
    Object.defineProperty(AcApDocManager.prototype, "mdiActiveDocument", {
        /**
         * Gets the currently active document.
         *
         * For now, this is the same as `curDocument` since only one document
         * can be active at a time.
         *
         * @returns The current active document
         */
        get: function () {
            return this._context.doc;
        },
        enumerable: false,
        configurable: true
    });
    Object.defineProperty(AcApDocManager.prototype, "curView", {
        /**
         * Gets the current 2D view used to display the drawing.
         *
         * @returns The current 2D view instance
         */
        get: function () {
            return this._context.view;
        },
        enumerable: false,
        configurable: true
    });
    Object.defineProperty(AcApDocManager.prototype, "editor", {
        /**
         * Gets the editor instance for handling user input.
         *
         * @returns The current editor instance
         */
        get: function () {
            return this._context.view.editor;
        },
        enumerable: false,
        configurable: true
    });
    Object.defineProperty(AcApDocManager.prototype, "commandManager", {
        /**
         * Gets command manager to look up and register commands
         *
         * @returns The command manager
         */
        get: function () {
            return this._commandManager;
        },
        enumerable: false,
        configurable: true
    });
    Object.defineProperty(AcApDocManager.prototype, "pluginManager", {
        /**
         * Gets plugin manager to load and unload plugins
         *
         * @returns The plugin manager
         */
        get: function () {
            return this._pluginManager;
        },
        enumerable: false,
        configurable: true
    });
    Object.defineProperty(AcApDocManager.prototype, "baseUrl", {
        /**
         * Base URL to load fonts
         */
        get: function () {
            return this._baseUrl;
        },
        enumerable: false,
        configurable: true
    });
    /**
     * Resolves colors for creating new entities.
     *
     * Returns:
     * - `entityColor`: the resolved RGB color (24-bit) to use for newly created entities.
     * - `layerColor`: the current layer's resolved RGB color (24-bit).
     */
    AcApDocManager.prototype.resolveColors = function () {
        var db = this.curDocument.database;
        var layer = db.tables.layerTable.getAt(db.clayer);
        var layerColorValue = this.resolveColorToRgb(layer === null || layer === void 0 ? void 0 : layer.color);
        var resolved = db.cecolor;
        if (resolved === null || resolved === void 0 ? void 0 : resolved.isByLayer) {
            resolved = layer === null || layer === void 0 ? void 0 : layer.color;
        }
        return {
            entityColor: this.resolveColorToRgb(resolved),
            layerColor: layerColorValue
        };
    };
    /**
     * Resolves an AcCmColor into a 24-bit RGB number.
     * Falls back to ACI 7 when the color is ByLayer/ByBlock or undefined.
     */
    AcApDocManager.prototype.resolveColorToRgb = function (color) {
        if (!color ||
            color.isByLayer ||
            color.isByBlock ||
            (color.isByACI && color.colorIndex === 7)) {
            return this.resolveAci7ForBackground();
        }
        var rgbValue = color.RGB;
        return rgbValue !== null && rgbValue !== void 0 ? rgbValue : this.resolveAci7ForBackground();
    };
    /**
     * Resolves ACI 7 based on the current viewer background color.
     * - Light background (white): use black.
     * - Dark background: use white.
     */
    AcApDocManager.prototype.resolveAci7ForBackground = function () {
        var bg = this.curView.backgroundColor;
        return bg === 0xffffff ? 0x000000 : 0xffffff;
    };
    Object.defineProperty(AcApDocManager.prototype, "avaiableFonts", {
        /**
         * Gets the list of available fonts that can be loaded.
         *
         * Note: These fonts are available for loading but may not be loaded yet.
         *
         * @returns Array of available font names
         */
        get: function () {
            return this._fontLoader.avaiableFonts;
        },
        enumerable: false,
        configurable: true
    });
    /**
     * Loads the specified fonts for text rendering.
     *
     * @param fonts - Array of font names to load
     * @returns Promise that resolves when fonts are loaded
     *
     * @example
     * ```typescript
     * await docManager.loadFonts(['Arial', 'Times New Roman']);
     * ```
     */
    AcApDocManager.prototype.loadFonts = function (fonts) {
        return __awaiter(this, void 0, void 0, function () {
            return __generator(this, function (_a) {
                switch (_a.label) {
                    case 0: return [4 /*yield*/, this._fontLoader.load(fonts)];
                    case 1:
                        _a.sent();
                        return [2 /*return*/];
                }
            });
        });
    };
    /**
     * Loads default fonts for CAD text rendering.
     *
     * This method loads either the specified fonts or falls back to default Chinese fonts
     * (specifically 'simkai') if no fonts are provided. The loaded fonts are used for
     * rendering CAD text entities like MText and Text in the viewer.
     *
     * It is better to load default fonts when viewer is initialized so that the viewer can
     * render text correctly if fonts used in the document are not available.
     *
     * @param fonts - Optional array of font names to load. If not provided or null,
     *               defaults to ['simkai'] for Chinese text support
     * @returns Promise that resolves when all specified fonts are loaded
     *
     * @example
     * ```typescript
     * // Load default fonts (simkai)
     * await docManager.loadDefaultFonts();
     *
     * // Load specific fonts
     * await docManager.loadDefaultFonts(['Arial', 'SimSun']);
     *
     * // Load no fonts (empty array)
     * await docManager.loadDefaultFonts([]);
     * ```
     *
     * @see {@link AcApFontLoader.load} - The underlying font loading implementation
     * @see {@link createExampleDoc} - Method that uses this for example document creation
     */
    AcApDocManager.prototype.loadDefaultFonts = function (fonts) {
        return __awaiter(this, void 0, void 0, function () {
            return __generator(this, function (_a) {
                switch (_a.label) {
                    case 0:
                        if (!(fonts == null)) return [3 /*break*/, 2];
                        return [4 /*yield*/, this._fontLoader.load(['simkai'])];
                    case 1:
                        _a.sent();
                        return [3 /*break*/, 4];
                    case 2: return [4 /*yield*/, this._fontLoader.load(fonts)];
                    case 3:
                        _a.sent();
                        _a.label = 4;
                    case 4: return [2 /*return*/];
                }
            });
        });
    };
    /**
     * Opens a CAD document from a URL.
     *
     * This method loads a document from the specified URL and replaces the current document.
     * It handles the complete document lifecycle including before/after open events.
     *
     * @param url - The URL of the CAD file to open
     * @param options - Optional database opening options. If not provided, default options with font loader will be used
     * @returns Promise that resolves to true if the document was successfully opened, false otherwise
     *
     * @example
     * ```typescript
     * const success = await docManager.openUrl('https://example.com/drawing.dwg');
     * if (success) {
     *   log.info('Document opened successfully');
     * }
     * ```
     */
    AcApDocManager.prototype.openUrl = function (url, options) {
        return __awaiter(this, void 0, void 0, function () {
            var isSuccess;
            return __generator(this, function (_a) {
                switch (_a.label) {
                    case 0:
                        this.onBeforeOpenDocument();
                        options = this.setOptions(options);
                        return [4 /*yield*/, this.context.doc.openUri(url, options)];
                    case 1:
                        isSuccess = _a.sent();
                        this.onAfterOpenDocument(isSuccess);
                        return [2 /*return*/, isSuccess];
                }
            });
        });
    };
    /**
     * Opens a CAD document from file content.
     *
     * This method loads a document from the provided file content (binary data)
     * and replaces the current document. It handles the complete document lifecycle
     * including before/after open events.
     *
     * @param fileName - The name of the file being opened (used for format detection)
     * @param content - The file content
     * @param options - Database opening options including font loader settings
     * @returns Promise that resolves to true if the document was successfully opened, false otherwise
     *
     * @example
     * ```typescript
     * const fileContent = await file.arrayBuffer();
     * const success = await docManager.openDocument('drawing.dwg', fileContent, options);
     * ```
     */
    AcApDocManager.prototype.openDocument = function (fileName, content, options) {
        return __awaiter(this, void 0, void 0, function () {
            var isSuccess;
            return __generator(this, function (_a) {
                switch (_a.label) {
                    case 0:
                        this.onBeforeOpenDocument();
                        options = this.setOptions(options);
                        return [4 /*yield*/, this.context.doc.openDocument(fileName, content, options)];
                    case 1:
                        isSuccess = _a.sent();
                        this.onAfterOpenDocument(isSuccess);
                        return [2 /*return*/, isSuccess];
                }
            });
        });
    };
    /**
     * Redraws the current view. Currently it is used once you modified font mapping
     * for missed fonts so that the drawing can apply new fonts.
     */
    AcApDocManager.prototype.regen = function () {
        this.curView.clear();
        this.context.doc.database.regen();
    };
    /**
     * Search through all of the local and translated names in all of the command groups in the command stack
     * starting at the top of the stack trying to find a match with cmdName. If a match is found, the matched
     * AcEdCommand object is returned. Otherwise undefined is returned to indicate that the command could not
     * be found. If more than one command of the same name is present in the command stack (that is, in
     * separate command groups), then the first one found is used.
     *
     * The command which is compatible with the open mode of the current document is only returned
     *
     * @param cmdName - Input the command name to search for
     * @returns Return the matched AcEdCommand object if a match is found and compatible with the open mode of
     * the current document. Otherwise, return undefined.
     */
    AcApDocManager.prototype.lookupLocalCmd = function (cmdName) {
        return this._commandManager.lookupLocalCmd(cmdName, this.curDocument.openMode);
    };
    /**
     * Search through all of the global and untranslated names in all of the command groups in the command
     * stack starting at the top of the stack trying to find a match with cmdName. If a match is found, the
     * matched AcEdCommand object is returned. Otherwise undefined is returned to indicate that the command
     * could not be found. If more than one command of the same name is present in the command stack (that
     * is, in separate command groups), then the first one found is used.
     *
     * The command is only returned if it is compatible with that open mode of the current document.
     * Higher value modes are compatible with lower value modes.
     *
     * @param cmdName - Input the command name to search for
     * @returns Return the matched AcEdCommand object if a match is found and compatible with the open mode
     * of the current document. Otherwise, return undefined.
     */
    AcApDocManager.prototype.lookupGlobalCmd = function (cmdName) {
        return this._commandManager.lookupGlobalCmd(cmdName, this.curDocument.openMode);
    };
    /**
     * Fuzzy search for commands by prefix using the command iterator.
     *
     * This method iterates through all commands in all command groups and returns those
     * whose global or local names start with the provided prefix. The search is case-insensitive.
     * Only commands which are compatible with that open mode of the current document are returned.
     * Higher value modes are compatible with lower value modes.
     *
     * @param prefix - The prefix string to search for. Case-insensitive.
     * @returns An array of objects containing matched commands and their corresponding group names.
     */
    AcApDocManager.prototype.searchCommandsByPrefix = function (prefix) {
        return this._commandManager.searchCommandsByPrefix(prefix, this.curDocument.openMode);
    };
    /**
     * Registers all default commands available in the CAD viewer.
     *
     * This method sets up the command system by registering built-in commands including:
     * - cdxf: Convert to DXF
     * - csvg: Convert to SVG
     * - log: Output debug information in console
     * - open: Open document
     * - qnew: Quick new document
     * - pan: Pan/move the view
     * - select: Select entities
     * - zoom: Zoom in/out
     *
     * All commands are registered under the system command group.
     */
    AcApDocManager.prototype.registerCommands = function () {
        var _this = this;
        var register = this._commandManager;
        /**
         * Helper for registering one built-in system command with resolved aliases.
         *
         * Alias resolution order:
         * 1. User override from `options.commandAliases`
         * 2. Built-in defaults from `DEFAULT_COMMAND_ALIASES`
         * 3. Empty list (no alias)
         *
         * @param cmdGlobalName - Global command name (language-neutral)
         * @param cmdLocalName - Localized/display command name
         * @param cmd - Command implementation instance
         */
        var addSystemCommand = function (cmdGlobalName, cmdLocalName, cmd) {
            var _a;
            var defaults = (_a = DEFAULT_COMMAND_ALIASES[cmdGlobalName.toUpperCase()]) !== null && _a !== void 0 ? _a : [];
            register.addCommand(AcEdCommandStack.SYSTEMT_COMMAND_GROUP_NAME, cmdGlobalName, cmdLocalName, cmd, _this.resolveCommandAliases(cmdGlobalName, defaults));
        };
        addSystemCommand('arc', 'arc', new AcApArcCmd());
        addSystemCommand('circle', 'circle', new AcApCircleCmd());
        addSystemCommand('cdxf', 'cdxf', new AcApConvertToDxfCmd());
        addSystemCommand('csvg', 'csvg', new AcApConvertToSvgCmd());
        addSystemCommand('pngout', 'pngout', new AcApConvertToPngCmd());
        addSystemCommand('ellipse', 'ellipse', new AcApEllipseCmd());
        addSystemCommand('erase', 'erase', new AcApEraseCmd());
        addSystemCommand('dimlinear', 'dimlinear', new AcApDimLinearCmd());
        addSystemCommand('measuredistance', 'measuredistance', new AcApMeasureDistanceCmd());
        addSystemCommand('measurearea', 'measurearea', new AcApMeasureAreaCmd());
        addSystemCommand('measureangle', 'measureangle', new AcApMeasureAngleCmd());
        addSystemCommand('measurearc', 'measurearc', new AcApMeasureArcCmd());
        addSystemCommand('clearmeasurements', 'clearmeasurements', new AcApClearMeasurementsCmd());
        addSystemCommand('hatch', 'hatch', new AcApHatchCmd());
        addSystemCommand('-layer', '-layer', new AcApLayerCmd());
        addSystemCommand('line', 'line', new AcApLineCmd());
        addSystemCommand('mtext', 'mtext', new AcApMTextCmd());
        addSystemCommand('move', 'move', new AcApMoveCmd());
        addSystemCommand('log', 'log', new AcApLogCmd());
        addSystemCommand('open', 'open', new AcApOpenCmd());
        addSystemCommand('pan', 'pan', new AcApPanCmd());
        addSystemCommand('polygon', 'polygon', new AcApPolygonCmd());
        addSystemCommand('pline', 'pline', new AcApPolylineCmd());
        addSystemCommand('qnew', 'qnew', new AcApQNewCmd());
        addSystemCommand('rectangle', 'rectangle', new AcApRectCmd());
        addSystemCommand('regen', 'regen', new AcApRegenCmd());
        addSystemCommand('revcircle', 'revcircle', new AcApRevCircleCmd());
        addSystemCommand('revcloud', 'revcloud', new AcApRevCloudCmd());
        addSystemCommand('revrect', 'revrect', new AcApRevRectCmd());
        addSystemCommand('revvis', 'revvis', new AcApRevVisibilityCmd());
        addSystemCommand('select', 'select', new AcApSelectCmd());
        addSystemCommand('sketch', 'sketch', new AcApSketchCmd());
        addSystemCommand('spline', 'spline', new AcApSplineCmd());
        addSystemCommand('switchbg', 'switchbg', new AcApSwitchBgCmd());
        addSystemCommand('zoom', 'zoom', new AcApZoomCmd());
        // Register system variables as commands
        var sysVars = AcDbSysVarManager.instance().getAllDescriptors();
        sysVars.forEach(function (sysVar) {
            register.addCommand(AcEdCommandStack.SYSTEMT_COMMAND_GROUP_NAME, sysVar.name, sysVar.name, new AcApSysVarCmd(), _this.resolveCommandAliases(sysVar.name, []));
        });
    };
    /**
     * Normalizes external command alias configuration into an internal map.
     *
     * Normalization rules:
     * - Command names and aliases are trimmed and converted to uppercase.
     * - Empty command names are ignored.
     * - Alias strings are deduplicated per command.
     * - Alias values identical to the command name are ignored.
     *
     * This method does not validate cross-command conflicts; conflict checks are
     * handled by `AcEdCommandStack.addCommand` during registration.
     *
     * @param config - Optional command alias configuration from user options
     * @returns Normalized alias override map keyed by command global name
     */
    AcApDocManager.prototype.normalizeCommandAliasConfig = function (config) {
        var map = new Map();
        if (!config) {
            return map;
        }
        Object.entries(config).forEach(function (_a) {
            var _b = __read(_a, 2), commandName = _b[0], aliases = _b[1];
            var normalizedCommandName = commandName.trim().toUpperCase();
            if (!normalizedCommandName) {
                return;
            }
            var aliasList = Array.isArray(aliases) ? aliases : [aliases];
            var normalizedAliases = new Set();
            aliasList.forEach(function (alias) {
                var normalizedAlias = alias.trim().toUpperCase();
                if (normalizedAlias && normalizedAlias !== normalizedCommandName) {
                    normalizedAliases.add(normalizedAlias);
                }
            });
            map.set(normalizedCommandName, __spreadArray([], __read(normalizedAliases), false));
        });
        return map;
    };
    /**
     * Resolves the final alias list for a command.
     *
     * Behavior:
     * - If the user configured aliases for this command, return that list directly.
     * - Otherwise, return normalized built-in defaults.
     *
     * All returned aliases are uppercase and deduplicated. Any alias equal to the
     * command name itself is dropped to avoid redundant/ambiguous registration.
     *
     * @param commandName - Command global name
     * @param defaultAliases - Built-in default aliases for this command
     * @returns Final aliases used for command registration
     */
    AcApDocManager.prototype.resolveCommandAliases = function (commandName, defaultAliases) {
        var normalizedCommandName = commandName.trim().toUpperCase();
        var configuredAliases = this._commandAliasOverrides.get(normalizedCommandName);
        if (configuredAliases) {
            return __spreadArray([], __read(configuredAliases), false);
        }
        var normalizedDefaults = new Set();
        defaultAliases.forEach(function (alias) {
            var normalizedAlias = alias.trim().toUpperCase();
            if (normalizedAlias && normalizedAlias !== normalizedCommandName) {
                normalizedDefaults.add(normalizedAlias);
            }
        });
        return __spreadArray([], __read(normalizedDefaults), false);
    };
    /**
     * Executes a command by its string name.
     *
     * This method looks up a registered command by name and executes it with the current context.
     * It checks if the command's required mode is compatible with the document's current mode.
     * If the command is not found or not compatible, an error is thrown.
     *
     * @param cmdStr - The command string to execute (e.g., 'pan', 'zoom', 'select')
     * @throws {Error} If the command is not found or if the command's mode is not compatible with the document's mode
     *
     * @example
     * ```typescript
     * docManager.sendStringToExecute('zoom');
     * docManager.sendStringToExecute('pan');
     * ```
     */
    AcApDocManager.prototype.sendStringToExecute = function (cmdStr) {
        var _this = this;
        var _a;
        var lines = this.splitCommandScript(cmdStr);
        if (!lines.length) {
            throw new Error('Command string is empty');
        }
        var _b = __read(lines), cmdName = _b[0], scriptInputs = _b.slice(1);
        var documentMode = this.context.doc.openMode;
        var cmd = (_a = this._commandManager.lookupGlobalCmd(cmdName)) !== null && _a !== void 0 ? _a : this._commandManager.lookupLocalCmd(cmdName, documentMode);
        if (!cmd) {
            throw new Error("Command '".concat(cmdName, "' not found"));
        }
        // Check mode compatibility: document mode must be >= command mode
        if (documentMode < cmd.mode) {
            throw new Error("Command '".concat(cmdName, "' requires mode '").concat(AcEdOpenMode[cmd.mode], "' but document is in mode '").concat(AcEdOpenMode[documentMode], "'!"));
        }
        this.editor.clearScriptInputs();
        this.editor.enqueueScriptInputs(scriptInputs);
        void cmd.trigger(this.context).finally(function () {
            _this.editor.clearScriptInputs();
        });
    };
    /**
     * Splits command script into Enter-separated values.
     * First line is command name, remaining lines are queued inputs for getXXX.
     */
    AcApDocManager.prototype.splitCommandScript = function (commandScript) {
        var source = commandScript.includes('\n') || commandScript.includes('\r')
            ? commandScript
            : commandScript.replace(/\\n/g, '\n');
        var lines = source.replace(/\r\n/g, '\n').split('\n');
        if (!lines.length)
            return [];
        var cmdName = lines[0].trim();
        if (!cmdName)
            return [];
        return __spreadArray([cmdName], __read(lines.slice(1)), false);
    };
    /**
     * Configures layout information for the current view.
     *
     * Sets up the active layout block table record ID and model space block table
     * record ID based on the current document's space configuration.
     */
    AcApDocManager.prototype.setActiveLayout = function () {
        var currentView = this.curView;
        currentView.activeLayoutBtrId = this.curDocument.database.currentSpaceId;
        currentView.modelSpaceBtrId = this.curDocument.database.currentSpaceId;
    };
    /**
     * Performs cleanup operations before opening a new document.
     *
     * This protected method is called automatically before any document opening operation.
     * It clears the current view to prepare for the new document content.
     *
     * @protected
     */
    AcApDocManager.prototype.onBeforeOpenDocument = function () {
        this.curView.clear();
    };
    /**
     * Performs setup operations after a document opening attempt.
     *
     * This protected method is called automatically after any document opening operation.
     * If the document was successfully opened, it dispatches the documentActivated event,
     * sets up layout information, and zooms the view to fit the content.
     *
     * @param isSuccess - Whether the document was successfully opened
     * @protected
     */
    AcApDocManager.prototype.onAfterOpenDocument = function (isSuccess) {
        if (isSuccess) {
            var doc = this.context.doc;
            this.events.documentActivated.dispatch({ doc: doc });
            this.setActiveLayout();
            var db = doc.database;
            // The extents of drawing database may be empty. Espically dxf files.
            if (db.extents.isEmpty()) {
                this.curView.zoomToFitDrawing();
            }
            else {
                this.curView.zoomTo(new AcGeBox2d(db.extmin, db.extmax));
            }
        }
    };
    /**
     * Sets up or validates database opening options.
     *
     * This private method ensures that the options object has a font loader configured.
     * If no options are provided, creates new options with the font loader.
     * If options are provided but missing a font loader, adds the font loader.
     *
     * @param options - Optional database opening options to validate/modify
     * @returns The validated options object with font loader configured
     * @private
     */
    AcApDocManager.prototype.setOptions = function (options) {
        if (options == null) {
            options = { fontLoader: this._fontLoader };
        }
        else if (options.fontLoader == null) {
            options.fontLoader = this._fontLoader;
        }
        return options;
    };
    /**
     * Shows progress animation and progress message
     * @param data - Progress data
     */
    AcApDocManager.prototype.updateProgress = function (data) {
        if (data.stage === 'CONVERSION') {
            if (data.subStage) {
                var key = 'main.progress.' + data.subStage.replace(/_/g, '').toLowerCase();
                this._progress.setMessage(AcApI18n.t(key));
            }
        }
        else if (data.stage === 'FETCH_FILE') {
            this._progress.setMessage(AcApI18n.t('main.message.fetchingDrawingFile'));
        }
        var percentage = data.percentage;
        if (percentage >= 100) {
            this._progress.hide();
        }
        else {
            this._progress.show();
        }
    };
    /**
     * Registers file format converters for CAD file processing.
     *
     * This function initializes and registers both DXF and DWG converters with the
     * global database converter manager. Each converter is configured to use web workers
     * for improved performance during file parsing operations.
     *
     * The function handles registration errors gracefully by logging them to the console
     * without throwing exceptions, ensuring that the application can continue to function
     * even if one or more converters fail to register.
     */
    AcApDocManager.prototype.registerConverters = function (webworkerFileUrls) {
        // Register DXF converter
        try {
            var converter = new AcDbDxfConverter({
                convertByEntityType: false,
                useWorker: true,
                parserWorkerUrl: webworkerFileUrls && webworkerFileUrls.dxfParser
                    ? webworkerFileUrls.dxfParser
                    : './assets/dxf-parser-worker.js'
            });
            AcDbDatabaseConverterManager.instance.register(AcDbFileType.DXF, converter);
        }
        catch (error) {
            log.error('Failed to register dxf converter: ', error);
        }
        // Register DWG converter
        try {
            var converter = new AcDbLibreDwgConverter({
                convertByEntityType: false,
                useWorker: true,
                parserWorkerUrl: webworkerFileUrls && webworkerFileUrls.dwgParser
                    ? webworkerFileUrls.dwgParser
                    : './assets/libredwg-parser-worker.js'
            });
            AcDbDatabaseConverterManager.instance.register(AcDbFileType.DWG, converter);
        }
        catch (error) {
            log.error('Failed to register dwg converter: ', error);
        }
    };
    /**
     * Initializes background workers used by the viewer runtime.
     *
     * This function performs two tasks:
     * - Ensures DXF/DWG converters are registered with worker-based parsers for
     *   off-main-thread file processing.
     * - Initializes the MText renderer by pointing it to its dedicated Web Worker
     *   script for text layout and shaping.
     *
     * The function is safe to call during application startup. Errors during
     * initialization are handled inside the respective registration routines.
     */
    AcApDocManager.prototype.registerWorkers = function (webworkerFileUrls) {
        this.registerConverters(webworkerFileUrls);
        AcTrMTextRenderer.getInstance().initialize(webworkerFileUrls && webworkerFileUrls.mtextRender
            ? webworkerFileUrls.mtextRender
            : './assets/mtext-renderer-worker.js');
    };
    /**
     * Loads plugins automatically based on the provided configuration.
     *
     * This method is called during initialization if plugins are configured.
     * It supports loading from both configuration arrays and folder paths.
     *
     * @param pluginsConfig - Plugin loading configuration
     * @private
     */
    AcApDocManager.prototype.loadPlugins = function (pluginsConfig) {
        return __awaiter(this, void 0, void 0, function () {
            var result, error_1, result, error_2;
            var _a;
            return __generator(this, function (_b) {
                switch (_b.label) {
                    case 0:
                        if (!pluginsConfig) {
                            return [2 /*return*/];
                        }
                        if (!(pluginsConfig.fromConfig && pluginsConfig.fromConfig.length > 0)) return [3 /*break*/, 4];
                        _b.label = 1;
                    case 1:
                        _b.trys.push([1, 3, , 4]);
                        return [4 /*yield*/, this._pluginManager.loadPluginsFromConfig(pluginsConfig.fromConfig, { continueOnError: true })];
                    case 2:
                        result = _b.sent();
                        if (result.loaded.length > 0) {
                            log.info("[AcApDocManager] Loaded ".concat(result.loaded.length, " plugin(s) from config:"), result.loaded);
                        }
                        if (result.failed.length > 0) {
                            log.warn("[AcApDocManager] Failed to load ".concat(result.failed.length, " plugin(s):"), result.failed.map(function (f) { return "".concat(f.name, ": ").concat(f.error.message); }));
                        }
                        return [3 /*break*/, 4];
                    case 3:
                        error_1 = _b.sent();
                        log.error('[AcApDocManager] Error loading plugins from config:', error_1);
                        return [3 /*break*/, 4];
                    case 4:
                        if (!pluginsConfig.fromFolder) return [3 /*break*/, 8];
                        _b.label = 5;
                    case 5:
                        _b.trys.push([5, 7, , 8]);
                        return [4 /*yield*/, this._pluginManager.loadPluginsFromFolder(pluginsConfig.fromFolder.folderPath, {
                                pluginList: pluginsConfig.fromFolder.pluginList,
                                continueOnError: (_a = pluginsConfig.fromFolder.continueOnError) !== null && _a !== void 0 ? _a : true
                            })];
                    case 6:
                        result = _b.sent();
                        if (result.loaded.length > 0) {
                            log.info("[AcApDocManager] Loaded ".concat(result.loaded.length, " plugin(s) from folder:"), result.loaded);
                        }
                        if (result.failed.length > 0) {
                            log.warn("[AcApDocManager] Failed to load ".concat(result.failed.length, " plugin(s) from folder:"), result.failed.map(function (f) { return "".concat(f.name, ": ").concat(f.error.message); }));
                        }
                        return [3 /*break*/, 8];
                    case 7:
                        error_2 = _b.sent();
                        log.error('[AcApDocManager] Error loading plugins from folder:', error_2);
                        return [3 /*break*/, 8];
                    case 8: return [2 /*return*/];
                }
            });
        });
    };
    return AcApDocManager;
}());
export { AcApDocManager };
//# sourceMappingURL=AcApDocManager.js.map