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 __assign = (this && this.__assign) || function () {
    __assign = Object.assign || function(t) {
        for (var s, i = 1, n = arguments.length; i < n; i++) {
            s = arguments[i];
            for (var p in s) if (Object.prototype.hasOwnProperty.call(s, p))
                t[p] = s[p];
        }
        return t;
    };
    return __assign.apply(this, arguments);
};
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 { acdbHostApplicationServices, AcDbRasterImage, AcDbRay, AcDbSysVarManager, AcDbViewport, AcDbXline, AcGeBox2d, AcGePoint2d, log } from '@mlightcad/data-model';
import { AcDbSystemVariables } from '@mlightcad/data-model';
import { AcTrEntity, AcTrGroup, AcTrRenderer, AcTrViewportView } from '@mlightcad/three-renderer';
import * as THREE from 'three';
import Stats from 'three/examples/jsm/libs/stats.module';
import { CSS2DRenderer } from 'three/examples/jsm/renderers/CSS2DRenderer.js';
import { AcApDocManager, AcApSettingManager } from '../app';
import { AcEdBaseView, AcEdConditionWaiter, AcEdCorsorType, AcEdViewMode, applyUiThemeFromBackground, eventBus } from '../editor';
import { AcTrGeometryUtil } from '../util';
import { AcTrLayoutView } from './AcTrLayoutView';
import { AcTrLayoutViewManager } from './AcTrLayoutViewManager';
import { AcTrScene } from './AcTrScene';
/**
 * Default view option values
 */
export var DEFAULT_VIEW_2D_OPTIONS = {
    background: 0x000000
};
/**
 * A 2D CAD viewer component that renders CAD drawings using Three.js.
 *
 * This class extends {@link AcEdBaseView} and provides functionality for:
 * - Rendering 2D CAD drawings with Three.js WebGL renderer
 * - Handling user interactions (pan, zoom, select)
 * - Managing layouts, layers, and entities
 * - Supporting various CAD file formats (DWG, DXF)
 *
 * @example
 * ```typescript
 * const viewer = new AcTrView2d({
 *   canvas: document.getElementById('canvas') as HTMLCanvasElement,
 *   background: 0x000000,
 *   calculateSizeCallback: () => ({
 *     width: window.innerWidth,
 *     height: window.innerHeight
 *   })
 * });
 * ```
 */
var AcTrView2d = /** @class */ (function (_super) {
    __extends(AcTrView2d, _super);
    /**
     * Creates a new 2D CAD viewer instance.
     *
     * @param options - Configuration options for the viewer
     * @param options.container - Optional HTML container element. If not provided, a new container will be created
     * @param options.calculateSizeCallback - Optional callback function to calculate canvas size on window resize
     * @param options.background - Optional background color as hex number (default: 0x000000)
     */
    function AcTrView2d(options) {
        if (options === void 0) { options = DEFAULT_VIEW_2D_OPTIONS; }
        var _this = this;
        var _a;
        var mergedOptions = __assign(__assign({}, DEFAULT_VIEW_2D_OPTIONS), options);
        var container = (_a = mergedOptions.container) !== null && _a !== void 0 ? _a : document.createElement('div');
        mergedOptions.container = container;
        var renderer = new THREE.WebGLRenderer({
            antialias: true,
            alpha: true
        });
        container.appendChild(renderer.domElement);
        _this = _super.call(this, renderer.domElement, container) || this;
        /**
         * ID of the currently scheduled requestAnimationFrame callback.
         *
         * This value is used to:
         * - Track whether the animation loop is currently running
         * - Prevent scheduling multiple animation loops
         * - Cancel the animation loop when the view is paused, hidden, or disposed
         *
         * A value of `null` indicates that no animation frame is currently scheduled.
         */
        _this._rafId = null;
        _this.animate = function () {
            var _a;
            _this._rafId = requestAnimationFrame(_this.animate);
            _this.events.renderFrame.dispatch({
                render: _this._renderer,
                camera: _this.internalCamera
            });
            if (!_this._isDirty)
                return;
            _this._layoutViewManager.render(_this._scene);
            if (_this.internalCamera) {
                _this._css2dRenderer.render(_this._scene.internalScene, _this.internalCamera);
            }
            (_a = _this._stats) === null || _a === void 0 ? void 0 : _a.update();
            _this._isDirty = false;
        };
        if (options.calculateSizeCallback) {
            _this.setCalculateSizeCallback(options.calculateSizeCallback);
        }
        renderer.setPixelRatio(Math.min(window.devicePixelRatio, 2));
        renderer.setSize(_this.width, _this.height);
        _this._renderer = new AcTrRenderer(renderer);
        var fontMapping = AcApSettingManager.instance.fontMapping;
        _this._renderer.setFontMapping(fontMapping);
        _this._renderer.events.fontNotFound.addEventListener(function (args) {
            var _a;
            eventBus.emit('font-not-found', {
                fontName: args.fontName,
                count: (_a = args.count) !== null && _a !== void 0 ? _a : 0
            });
        });
        _this._scene = _this.createScene();
        // Initialize background color through setter to keep renderer/cursor/foreground in sync.
        _this.backgroundColor = mergedOptions.background || 0x000000;
        _this._stats = _this.createStats(AcApSettingManager.instance.isShowStats);
        AcDbSysVarManager.instance().events.sysVarChanged.addEventListener(function (args) {
            if (args.name === AcDbSystemVariables.WHITEBKCOLOR.toLowerCase()) {
                var useWhiteBackgroundColor = args.newVal;
                _this.backgroundColor = useWhiteBackgroundColor ? 0xffffff : 0;
            }
        });
        AcApSettingManager.instance.events.modified.addEventListener(function (args) {
            if (args.key == 'isShowStats') {
                _this.toggleStatsVisibility(_this._stats, args.value);
            }
        });
        var selectionStartWcs = null;
        var selectionStartCanvas = null;
        var selectionPreviewEl = null;
        var canHandleSelectionGesture = function () {
            return _this.mode === AcEdViewMode.SELECTION && !_this.editor.isActive;
        };
        var clearSelectionPreview = function () {
            selectionPreviewEl === null || selectionPreviewEl === void 0 ? void 0 : selectionPreviewEl.remove();
            selectionPreviewEl = null;
        };
        _this.canvas.addEventListener('mousedown', function (e) {
            if (e.button !== 0)
                return;
            if (!canHandleSelectionGesture())
                return;
            selectionStartCanvas = _this.viewportToCanvas({
                x: e.clientX,
                y: e.clientY
            });
            selectionStartWcs = _this.screenToWorld(selectionStartCanvas);
            selectionPreviewEl = document.createElement('div');
            selectionPreviewEl.className = 'ml-jig-preview-rect';
            _this.container.appendChild(selectionPreviewEl);
        });
        _this.canvas.addEventListener('mousemove', function (e) {
            if (!selectionStartWcs || !selectionPreviewEl || !selectionStartCanvas) {
                return;
            }
            var curCanvas = _this.viewportToCanvas({ x: e.clientX, y: e.clientY });
            var curWcs = _this.screenToWorld(curCanvas);
            var p1 = _this.worldToScreen(selectionStartWcs);
            var p2 = _this.worldToScreen(curWcs);
            var left = Math.min(p1.x, p2.x);
            var top = Math.min(p1.y, p2.y);
            var width = Math.abs(p1.x - p2.x);
            var height = Math.abs(p1.y - p2.y);
            var mode = _this.getSelectionMode(selectionStartCanvas, curCanvas);
            var action = _this.getSelectionActionFromEvent(e);
            var style = _this.getSelectionPreviewStyle(mode, action);
            Object.assign(selectionPreviewEl.style, {
                left: "".concat(left, "px"),
                top: "".concat(top, "px"),
                width: "".concat(width, "px"),
                height: "".concat(height, "px"),
                borderStyle: style.borderStyle,
                background: style.background
            });
            selectionPreviewEl.style.setProperty('--line-color', style.lineColor);
        });
        _this.canvas.addEventListener('mouseup', function (e) {
            if (!selectionStartWcs || !selectionStartCanvas)
                return;
            var endCanvas = _this.viewportToCanvas({
                x: e.clientX,
                y: e.clientY
            });
            var endWcs = _this.screenToWorld(endCanvas);
            clearSelectionPreview();
            var action = _this.getSelectionActionFromEvent(e);
            if (_this.isSelectionClick(selectionStartCanvas, endCanvas)) {
                var picked = _this.pick(endWcs);
                if (picked.length > 0) {
                    _this.applySelection([picked[0].id], action);
                }
                else if (action === 'replace') {
                    _this.selectionSet.clear();
                }
            }
            else {
                var box = new AcGeBox2d()
                    .expandByPoint(selectionStartWcs)
                    .expandByPoint(endWcs);
                var mode = _this.getSelectionMode(selectionStartCanvas, endCanvas);
                _this.selectByBoxWithMode(box, mode, action);
            }
            selectionStartWcs = null;
            selectionStartCanvas = null;
        });
        // When using OrbitControls in THREE.js, it attaches its own event listeners to the DOM elements,
        // such as the canvas or the entire document. This can interfere with other event listeners you
        // add, including the keydown event.
        document.addEventListener('keydown', function (e) {
            // Ignore global shortcuts while typing or during IME composition.
            var target = e.target;
            var isEditableTarget = target instanceof HTMLInputElement ||
                target instanceof HTMLTextAreaElement ||
                (target === null || target === void 0 ? void 0 : target.isContentEditable) === true;
            // keyCode 229 is commonly reported by IME composing key events.
            var isImeComposing = e.isComposing || e.keyCode === 229;
            if (isEditableTarget || isImeComposing) {
                return;
            }
            switch (e.code) {
                case 'Escape':
                    _this.selectionSet.clear();
                    break;
                case 'Delete':
                case 'Backspace':
                    // Only dispatch erase when no command is currently active.
                    // Dispatching erase mid-command (e.g. while LINE awaits the next
                    // point) corrupts the active command's input pipeline because
                    // sendStringToExecute clears scripted inputs unconditionally.
                    if (!_this.editor.isActive) {
                        AcApDocManager.instance.sendStringToExecute('erase');
                    }
                    break;
            }
        });
        acdbHostApplicationServices().layoutManager.events.layoutSwitched.addEventListener(function (args) {
            _this.activeLayoutBtrId = args.layout.blockTableRecordId;
            _this.createLayoutViewIfNeeded(args.layout.blockTableRecordId);
            _this.loadLayoutEntitiesIfNeeded(args.layout.blockTableRecordId);
            _this._isDirty = true;
        });
        _this._css2dRenderer = new CSS2DRenderer();
        _this._css2dRenderer.setSize(_this.width, _this.height);
        _this._css2dRenderer.domElement.style.position = 'absolute';
        _this._css2dRenderer.domElement.style.top = '0px';
        _this._css2dRenderer.domElement.style.left = '0px';
        _this._css2dRenderer.domElement.style.pointerEvents = 'none';
        _this._css2dRenderer.domElement.style.zIndex = '99998';
        container.appendChild(_this._css2dRenderer.domElement);
        _this._missedImages = new Map();
        _this._layoutViewManager = new AcTrLayoutViewManager();
        _this.initialize();
        _this.onWindowResize();
        _this._isDirty = true;
        _this.startAnimationLoop();
        _this._numOfEntitiesToProcess = 0;
        return _this;
    }
    /**
     * Initializes the viewer after renderer and camera are created.
     *
     * This method sets up the initial cursor and can be overridden by child classes
     * to add custom initialization logic.
     *
     * @protected
     */
    AcTrView2d.prototype.initialize = function () {
        // This method is called after camera and render are created.
        // Children class can override this method to add its own logic
        this.setCursor(AcEdCorsorType.Crosshair);
    };
    Object.defineProperty(AcTrView2d.prototype, "mode", {
        /**
         * Gets the current view mode (selection or pan).
         *
         * @returns The current view mode
         * @inheritdoc
         */
        get: function () {
            var activeLayoutView = this.activeLayoutView;
            return activeLayoutView ? activeLayoutView.mode : AcEdViewMode.SELECTION;
        },
        /**
         * Sets the view mode (selection or pan).
         *
         * @param value - The view mode to set
         */
        set: function (value) {
            this.activeLayoutView.mode = value;
        },
        enumerable: false,
        configurable: true
    });
    Object.defineProperty(AcTrView2d.prototype, "renderer", {
        /**
         * Gets the Three.js renderer wrapper used for CAD rendering.
         *
         * @returns The renderer instance
         */
        get: function () {
            return this._renderer;
        },
        enumerable: false,
        configurable: true
    });
    Object.defineProperty(AcTrView2d.prototype, "isDirty", {
        /**
         * Gets whether the view needs to be re-rendered.
         *
         * @returns True if the view is dirty and needs re-rendering
         */
        get: function () {
            return this._isDirty;
        },
        /**
         * Sets whether the view needs to be re-rendered.
         *
         * @param value - True to mark the view as needing re-rendering
         */
        set: function (value) {
            this._isDirty = value;
        },
        enumerable: false,
        configurable: true
    });
    Object.defineProperty(AcTrView2d.prototype, "missedData", {
        /**
         * Gets information about missing data during rendering (fonts and images).
         *
         * @returns Object containing maps of missing fonts and images
         */
        get: function () {
            return {
                fonts: this._renderer.missedFonts,
                images: this._missedImages
            };
        },
        enumerable: false,
        configurable: true
    });
    Object.defineProperty(AcTrView2d.prototype, "center", {
        get: function () {
            return this.activeLayoutView.center;
        },
        set: function (value) {
            this.activeLayoutView.center = value;
        },
        enumerable: false,
        configurable: true
    });
    Object.defineProperty(AcTrView2d.prototype, "backgroundColor", {
        /**
         * Gets the background color of the view.
         *
         * The color is represented as a 24-bit hexadecimal RGB number, e.g.,
         * `0x000000` for black.
         */
        get: function () {
            return this._renderer.getClearColor();
        },
        /**
         * Sets the background color of the view.
         *
         * @param value - The background color as a 24-bit hexadecimal RGB number
         */
        set: function (value) {
            this._renderer.setClearColor(value);
            this._renderer.changeForeground(value == 0 ? 0xffffff : 0);
            this.editor.setCursorColor(value == 0 ? 'white' : 'black');
            applyUiThemeFromBackground(value);
            this._isDirty = true;
        },
        enumerable: false,
        configurable: true
    });
    Object.defineProperty(AcTrView2d.prototype, "modelSpaceBtrId", {
        /**
         * The block table record id of the model space
         */
        get: function () {
            return this._scene.modelSpaceBtrId;
        },
        set: function (value) {
            this._scene.modelSpaceBtrId = value;
        },
        enumerable: false,
        configurable: true
    });
    Object.defineProperty(AcTrView2d.prototype, "activeLayoutBtrId", {
        /**
         * The block table record id associated with the active layout
         */
        get: function () {
            return this._scene.activeLayoutBtrId;
        },
        set: function (value) {
            this._layoutViewManager.activeLayoutBtrId = value;
            this._scene.activeLayoutBtrId = value;
            this._isDirty = true;
        },
        enumerable: false,
        configurable: true
    });
    Object.defineProperty(AcTrView2d.prototype, "activeLayoutView", {
        /**
         * The active layout view
         */
        get: function () {
            return this._layoutViewManager.activeLayoutView;
        },
        enumerable: false,
        configurable: true
    });
    Object.defineProperty(AcTrView2d.prototype, "stats", {
        /**
         * The statistics of the current scene
         */
        get: function () {
            return this._scene.stats;
        },
        enumerable: false,
        configurable: true
    });
    Object.defineProperty(AcTrView2d.prototype, "internalScene", {
        /**
         * The internal THREE scene used by this view.
         */
        get: function () {
            return this._scene.internalScene;
        },
        enumerable: false,
        configurable: true
    });
    Object.defineProperty(AcTrView2d.prototype, "htmlTransientManager", {
        /**
         * The HTML transient elements manager for placing overlays anchored to world coordinates.
         */
        get: function () {
            return this._scene.htmlTransientManager;
        },
        enumerable: false,
        configurable: true
    });
    Object.defineProperty(AcTrView2d.prototype, "internalCamera", {
        /**
         * The internal THREE camera used by current active layout.
         */
        get: function () {
            var _a;
            return (_a = this.activeLayoutView) === null || _a === void 0 ? void 0 : _a.internalCamera;
        },
        enumerable: false,
        configurable: true
    });
    Object.defineProperty(AcTrView2d.prototype, "ltscale", {
        /**
         * Sets global ltscale
         */
        set: function (scale) {
            this._renderer.ltscale = scale;
        },
        enumerable: false,
        configurable: true
    });
    Object.defineProperty(AcTrView2d.prototype, "celtscale", {
        /**
         * Sets global celtscale
         */
        set: function (scale) {
            this._renderer.celtscale = scale;
        },
        enumerable: false,
        configurable: true
    });
    /**
     * @inheritdoc
     */
    AcTrView2d.prototype.screenToWorld = function (point) {
        var activeLayoutView = this.activeLayoutView;
        return activeLayoutView
            ? activeLayoutView.screenToWorld(point)
            : new AcGePoint2d(point);
    };
    /**
     * @inheritdoc
     */
    AcTrView2d.prototype.worldToScreen = function (point) {
        var activeLayoutView = this.activeLayoutView;
        return activeLayoutView
            ? activeLayoutView.worldToScreen(point)
            : new AcGePoint2d(point);
    };
    /**
     * @inheritdoc
     */
    AcTrView2d.prototype.zoomTo = function (box, margin) {
        if (margin === void 0) { margin = 1.1; }
        this.activeLayoutView.zoomTo(box, margin);
        this._isDirty = true;
    };
    /**
     * Re-render points with latest point style settings
     * @param displayMode Input display mode of points
     */
    AcTrView2d.prototype.rerenderPoints = function (displayMode) {
        var activeLayout = this._scene.activeLayout;
        if (activeLayout) {
            activeLayout.rerenderPoints(displayMode);
            this._isDirty = true;
        }
    };
    /**
     * @inheritdoc
     */
    AcTrView2d.prototype.zoomToFitDrawing = function (timeout) {
        var _this = this;
        if (timeout === void 0) { timeout = 0; }
        var waiter = new AcEdConditionWaiter(function () { return _this._numOfEntitiesToProcess <= 0; }, function () {
            if (_this._scene.box) {
                var box = AcTrGeometryUtil.threeBox3dToGeBox2d(_this._scene.box);
                _this.zoomTo(box);
                _this._isDirty = true;
            }
        }, 300, // check every 300 ms
        timeout);
        waiter.start();
    };
    /**
     * @inheritdoc
     */
    AcTrView2d.prototype.zoomToFitLayer = function (layerName) {
        var activeLayout = this._scene.activeLayout;
        if (activeLayout) {
            var layer = activeLayout.getLayer(layerName);
            if (layer && !layer.box.isEmpty()) {
                var box = AcTrGeometryUtil.threeBox3dToGeBox2d(layer.box);
                this.zoomTo(box);
                this._isDirty = true;
                return true;
            }
        }
        return false;
    };
    /**
     * @inheritdoc
     */
    AcTrView2d.prototype.flyTo = function (point, scale) {
        this.activeLayoutView.flyTo(point, scale);
        this._isDirty = true;
    };
    /**
     * @inheritdoc
     */
    AcTrView2d.prototype.pick = function (point, hitRadius, pickOneOnly) {
        if (point == null)
            point = this.curPos;
        var results = [];
        var activeLayout = this._scene.activeLayout;
        if (activeLayout) {
            var activeLayoutView = this.activeLayoutView;
            var box = activeLayoutView.pointToBox(point, hitRadius !== null && hitRadius !== void 0 ? hitRadius : this.selectionBoxSize);
            var firstQueryResults = this._scene.search(box);
            var threshold = Math.max(box.size.width / 2, box.size.height / 2);
            var raycaster_1 = activeLayoutView.resetRaycaster(point, threshold);
            if (pickOneOnly) {
                firstQueryResults.some(function (item) {
                    var objectId = item.id;
                    if (activeLayout.isIntersectWith(objectId, raycaster_1)) {
                        results.push(item);
                        return true;
                    }
                    return false;
                });
            }
            else {
                firstQueryResults.forEach(function (item) {
                    var objectId = item.id;
                    if (activeLayout.isIntersectWith(objectId, raycaster_1)) {
                        results.push(item);
                    }
                });
            }
        }
        return results;
    };
    /**
     * @inheritdoc
     */
    AcTrView2d.prototype.search = function (box) {
        return this._scene.search(box);
    };
    /**
     * @inheritdoc
     */
    AcTrView2d.prototype.select = function (point) {
        var idsAdded = [];
        var results = this.pick(point);
        results.forEach(function (item) { return idsAdded.push(item.id); });
        if (idsAdded.length > 0)
            this.selectionSet.add(idsAdded);
    };
    /**
     * @inheritdoc
     */
    AcTrView2d.prototype.selectByBox = function (box) {
        this.selectByBoxWithMode(box, 'crossing', 'add');
    };
    /**
     * @inheritdoc
     */
    AcTrView2d.prototype.addLayer = function (layer) {
        this._scene.addLayer({
            name: layer.name,
            isFrozen: layer.isFrozen,
            isOff: layer.isOff,
            color: layer.color
        });
        this._isDirty = true;
    };
    /**
     * @inheritdoc
     */
    AcTrView2d.prototype.updateLayer = function (layer, changes) {
        var updatedLayers = this._scene.updateLayer({
            name: layer.name,
            isFrozen: layer.isFrozen,
            isOff: layer.isOff,
            color: layer.color
        });
        var traits = {};
        if (changes.color) {
            traits.color = changes.color.clone();
            traits.rgbColor = changes.color.RGB;
        }
        if (changes.lineStyle) {
            traits.lineType = layer.lineStyle;
        }
        if (changes.lineWeight !== undefined) {
            traits.lineWeight = changes.lineWeight;
        }
        if (changes.transparency !== undefined) {
            traits.transparency = changes.transparency;
        }
        traits.layer = layer.name; // always present
        var materials = this._renderer.updateLayerMaterial(layer.name, traits);
        updatedLayers.forEach(function (layer) {
            for (var id in materials) {
                var material = materials[id];
                layer.updateMaterial(Number(id), material);
            }
        });
        this._isDirty = true;
    };
    /**
     * Add the specified transient entity or entities into this view
     * @param entity Input one or multiple transient entities
     */
    AcTrView2d.prototype.addTransientEntity = function (entity) {
        var entities = Array.isArray(entity) ? entity : [entity];
        for (var i = 0; i < entities.length; ++i) {
            var entity_1 = entities[i];
            var threeEntity = this.drawEntity(entity_1, true);
            if (threeEntity) {
                threeEntity.objectId = entity_1.objectId;
                this._scene.addTransientEntity(threeEntity);
                this._isDirty = true;
            }
        }
    };
    /**
     * Remove the specified transient entity from this view
     * @param objectId Input the object id of the transient entity to remove
     */
    AcTrView2d.prototype.removeTransientEntity = function (objectId) {
        this._scene.removeTransientEntity(objectId);
        this._isDirty = true;
    };
    /**
     * @inheritdoc
     */
    AcTrView2d.prototype.addEntity = function (entity) {
        var _this = this;
        var entities = Array.isArray(entity) ? entity : [entity];
        this._numOfEntitiesToProcess += entities.length;
        setTimeout(function () { return __awaiter(_this, void 0, void 0, function () {
            return __generator(this, function (_a) {
                switch (_a.label) {
                    case 0: return [4 /*yield*/, this.batchConvert(entities)];
                    case 1:
                        _a.sent();
                        return [2 /*return*/];
                }
            });
        }); });
        this._isDirty = true;
    };
    /**
     * @inheritdoc
     */
    AcTrView2d.prototype.removeEntity = function (entity) {
        var _this = this;
        var entities = Array.isArray(entity) ? entity : [entity];
        entities.forEach(function (entity) { return _this._scene.removeEntity(entity.objectId); });
        this._isDirty = true;
    };
    /**
     * @inheritdoc
     */
    AcTrView2d.prototype.updateEntity = function (entity) {
        var _this = this;
        var entities = [];
        if (Array.isArray(entity)) {
            entities = entity;
        }
        else {
            entities.push(entity);
        }
        for (var i = 0; i < entities.length; ++i) {
            var entity_2 = entities[i];
            var threeEntity = entity_2.worldDraw(this._renderer);
            if (threeEntity) {
                threeEntity.objectId = entity_2.objectId;
                threeEntity.ownerId = entity_2.ownerId;
                threeEntity.layerName = entity_2.layer;
                threeEntity.visible = entity_2.visibility;
                this._scene.updateEntity(threeEntity);
            }
        }
        this._isDirty = true;
        // Not sure why texture for image entity isn't updated even if 'isDirty' flag is already set to true.
        // So add one timeout event to set 'isDirty' flag to true again to make it work
        setTimeout(function () {
            _this._isDirty = true;
        }, 100);
    };
    /**
     * @inheritdoc
     */
    AcTrView2d.prototype.addLayout = function (layout) {
        this._scene.addEmptyLayout(layout.blockTableRecordId);
        this.createLayoutViewIfNeeded(layout.blockTableRecordId);
        this._isDirty = true;
    };
    /**
     * @inheritdoc
     */
    AcTrView2d.prototype.clear = function () {
        this._scene.clear();
        this._isDirty = true;
        this._missedImages.clear();
        this._renderer.dispose();
    };
    /**
     * @inheritdoc
     */
    AcTrView2d.prototype.highlight = function (ids) {
        this._isDirty = this._scene.select(ids);
    };
    /**
     * @inheritdoc
     */
    AcTrView2d.prototype.unhighlight = function (ids) {
        this._isDirty = this._scene.unselect(ids);
    };
    AcTrView2d.prototype.stopAnimationLoop = function () {
        if (this._rafId != null) {
            cancelAnimationFrame(this._rafId);
            this._rafId = null;
        }
    };
    /**
     * @inheritdoc
     */
    AcTrView2d.prototype.onHover = function (id) {
        this._isDirty = this._scene.hover([id]);
    };
    /**
     * @inheritdoc
     */
    AcTrView2d.prototype.onUnhover = function (id) {
        this._isDirty = this._scene.unhover([id]);
    };
    AcTrView2d.prototype.createScene = function () {
        return new AcTrScene();
    };
    AcTrView2d.prototype.createStats = function (show) {
        var stats = new Stats();
        document.body.appendChild(stats.dom);
        // Show Stats component at the right-bottom corner of the window
        var statsDom = stats.dom;
        statsDom.style.position = 'fixed';
        statsDom.style.inset = 'unset';
        statsDom.style.bottom = '30px';
        statsDom.style.right = '0px';
        this.toggleStatsVisibility(stats, show);
        return stats;
    };
    AcTrView2d.prototype.onWindowResize = function () {
        _super.prototype.onWindowResize.call(this);
        this._renderer.setSize(this.width, this.height);
        this._css2dRenderer.setSize(this.width, this.height);
        this._layoutViewManager.resize(this.width, this.height);
        this._isDirty = true;
    };
    AcTrView2d.prototype.startAnimationLoop = function () {
        if (this._rafId == null) {
            this._rafId = requestAnimationFrame(this.animate);
        }
    };
    /**
     * Create the layout view with the specified block table record id.
     * @param layoutBtrId Input the block table record id associated with the layout view.
     */
    AcTrView2d.prototype.createLayoutViewIfNeeded = function (layoutBtrId) {
        var _this = this;
        var layoutView = this._layoutViewManager.getAt(layoutBtrId);
        if (layoutView == null) {
            layoutView = new AcTrLayoutView(this._renderer, layoutBtrId, this.width, this.height);
            layoutView.events.viewChanged.addEventListener(function () {
                _this._isDirty = true;
                _this.events.viewChanged.dispatch();
                _this.clearHover();
            });
            this._layoutViewManager.add(layoutView);
        }
        return layoutView;
    };
    /**
     * Load entities from the specified layout if they haven't been loaded yet.
     * This ensures that when switching to a layout, all its entities are available for rendering.
     * @param layoutBtrId Input the block table record id of the layout
     */
    AcTrView2d.prototype.loadLayoutEntitiesIfNeeded = function (layoutBtrId) {
        var e_1, _a;
        var _this = this;
        try {
            var db = AcApDocManager.instance.curDocument.database;
            var blockTableRecord = db.tables.blockTable.getIdAt(layoutBtrId);
            if (!blockTableRecord) {
                return;
            }
            var layout = this._scene.activeLayout;
            if (layout && layout.isLoaded) {
                return;
            }
            // Collect all entities from this layout
            var entities_1 = [];
            var iterator = blockTableRecord.newIterator();
            try {
                for (var iterator_1 = __values(iterator), iterator_1_1 = iterator_1.next(); !iterator_1_1.done; iterator_1_1 = iterator_1.next()) {
                    var entity = iterator_1_1.value;
                    entities_1.push(entity);
                }
            }
            catch (e_1_1) { e_1 = { error: e_1_1 }; }
            finally {
                try {
                    if (iterator_1_1 && !iterator_1_1.done && (_a = iterator_1.return)) _a.call(iterator_1);
                }
                finally { if (e_1) throw e_1.error; }
            }
            // Ensure layout exists in scene
            this._scene.addEmptyLayout(layoutBtrId);
            if (entities_1.length > 0) {
                // Load entities asynchronously
                this._numOfEntitiesToProcess += entities_1.length;
                setTimeout(function () { return __awaiter(_this, void 0, void 0, function () {
                    var layout;
                    return __generator(this, function (_a) {
                        switch (_a.label) {
                            case 0: return [4 /*yield*/, this.batchConvert(entities_1)];
                            case 1:
                                _a.sent();
                                layout = this._scene.layouts.get(layoutBtrId);
                                if (layout) {
                                    layout.isLoaded = true;
                                }
                                return [2 /*return*/];
                        }
                    });
                }); });
            }
        }
        catch (error) {
            log.error('[AcTrView2d] Error loading layout entities:', error);
        }
    };
    /**
     * Show or hide stats component
     * @param show If it is true, show stats component. Otherwise, hide stats component.
     * Default value is false.
     */
    AcTrView2d.prototype.toggleStatsVisibility = function (stats, show) {
        if (show) {
            stats.dom.style.display = 'block'; // Show the stats
        }
        else {
            stats.dom.style.display = 'none'; // Hide the stats
        }
    };
    AcTrView2d.prototype.drawEntity = function (entity, delay) {
        return entity.worldDraw(this._renderer, delay);
    };
    /**
     * Converts the specified database entities to three entities
     * @param entities - The database entities
     * @returns The converted three entities
     */
    AcTrView2d.prototype.batchConvert = function (entities) {
        return __awaiter(this, void 0, void 0, function () {
            var _loop_1, this_1, i;
            var _this = this;
            return __generator(this, function (_a) {
                switch (_a.label) {
                    case 0:
                        _loop_1 = function (i) {
                            var entity, threeEntity, isExtendBbox_1, layoutView, viewportView, fileName;
                            return __generator(this, function (_b) {
                                switch (_b.label) {
                                    case 0:
                                        entity = entities[i];
                                        threeEntity = this_1.drawEntity(entity, true);
                                        if (!threeEntity) return [3 /*break*/, 4];
                                        threeEntity.objectId = entity.objectId;
                                        threeEntity.ownerId = entity.ownerId;
                                        threeEntity.layerName = entity.layer;
                                        threeEntity.visible = entity.visibility;
                                        if (!(threeEntity instanceof AcTrGroup &&
                                            !threeEntity.isOnTheSameLayer)) return [3 /*break*/, 1];
                                        this_1.handleGroup(threeEntity);
                                        this_1.decreaseNumOfEntitiesToProcess();
                                        return [3 /*break*/, 3];
                                    case 1:
                                        isExtendBbox_1 = !(entity instanceof AcDbRay || entity instanceof AcDbXline);
                                        return [4 /*yield*/, threeEntity
                                                .draw()
                                                .then(function () {
                                                _this._scene.addEntity(threeEntity, isExtendBbox_1);
                                                // Release memory occupied by this entity
                                                threeEntity.dispose();
                                                _this._isDirty = true;
                                            })
                                                .finally(function () {
                                                _this.decreaseNumOfEntitiesToProcess();
                                            })];
                                    case 2:
                                        _b.sent();
                                        _b.label = 3;
                                    case 3:
                                        if (entity instanceof AcDbViewport) {
                                            // In paper space layouts, there is always a system-defined "default" viewport that exists as
                                            // the bottom-most item. This viewport doesn't show any entities and is mainly for internal
                                            // AutoCAD purposes. The viewport id number of this system-defined "default" viewport is 1.
                                            if (entity.number !== 1) {
                                                layoutView = this_1._layoutViewManager.getAt(entity.ownerId);
                                                if (layoutView) {
                                                    viewportView = new AcTrViewportView(layoutView, entity.toGiViewport(), this_1._renderer);
                                                    layoutView.addViewport(viewportView);
                                                }
                                            }
                                        }
                                        else if (entity instanceof AcDbRasterImage) {
                                            fileName = entity.imageFileName;
                                            if (fileName)
                                                this_1._missedImages.set(entity.objectId, fileName);
                                        }
                                        return [3 /*break*/, 5];
                                    case 4:
                                        this_1.decreaseNumOfEntitiesToProcess();
                                        _b.label = 5;
                                    case 5: return [2 /*return*/];
                                }
                            });
                        };
                        this_1 = this;
                        i = 0;
                        _a.label = 1;
                    case 1:
                        if (!(i < entities.length)) return [3 /*break*/, 4];
                        return [5 /*yield**/, _loop_1(i)];
                    case 2:
                        _a.sent();
                        _a.label = 3;
                    case 3:
                        ++i;
                        return [3 /*break*/, 1];
                    case 4: return [2 /*return*/];
                }
            });
        });
    };
    AcTrView2d.prototype.handleGroup = function (group) {
        var e_2, _a;
        var _this = this;
        var children = group.children;
        var objectsGroupByLayer = new Map();
        children.forEach(function (child) {
            var _a;
            var layerName = child.userData.layerName;
            if (!objectsGroupByLayer.has(layerName)) {
                objectsGroupByLayer.set(layerName, []);
            }
            (_a = objectsGroupByLayer.get(layerName)) === null || _a === void 0 ? void 0 : _a.push(child);
        });
        // Important:
        // Sometimes one group may contain huge amount of objects (> 100,000). So it is important
        // to re-parent object with the fast approach. Calling add/remove method in THREE.Object3D
        // is very slow because it do lots of things
        // - Remove children from old group
        // - Insert them into new group
        // - Reset parent pointer
        // - Do one updateMatrixWorld() at the end (optional)
        // So we operate its children directly.
        group.children = [];
        try {
            for (var children_1 = __values(children), children_1_1 = children_1.next(); !children_1_1.done; children_1_1 = children_1.next()) {
                var child = children_1_1.value;
                child.parent = null;
            }
        }
        catch (e_2_1) { e_2 = { error: e_2_1 }; }
        finally {
            try {
                if (children_1_1 && !children_1_1.done && (_a = children_1.return)) _a.call(children_1);
            }
            finally { if (e_2) throw e_2.error; }
        }
        var styleManager = group.styleManager;
        var groupObjectId = group.objectId;
        var groupLayerName = group.layerName;
        var groupBox = group.box;
        objectsGroupByLayer.forEach(function (objects, layerName) {
            // In AutoCAD, an INSERT entity may reference multiple child entities that
            // reside on different layers. During rendering, this engine groups entities
            // by layer and assigns each group the INSERT entity's object ID.
            // As a result, a single object ID (typically from an INSERT entity) may
            // correspond to multiple layers. However, in this layer its object id is still
            // uniqiue.
            var entity = new AcTrEntity(styleManager);
            entity.applyMatrix4(group.matrix);
            entity.objectId = groupObjectId;
            entity.ownerId = group.ownerId;
            // Here one group represents one block reference. If the layer name of entities in block
            // definition is '0', it should be put on layer where the group exist.
            entity.layerName = layerName === '0' ? groupLayerName : layerName;
            entity.box = groupBox;
            // Important:
            // DO NOT USE spread operator when adding objects because it may be one very large array
            // and can result in maximum call stack size exceeded
            for (var i = 0; i < objects.length; i++) {
                entity.add(objects[i]);
            }
            _this._scene.addEntity(entity, true);
            entity.dispose();
        });
        group.dispose();
        this._isDirty = true;
    };
    AcTrView2d.prototype.decreaseNumOfEntitiesToProcess = function () {
        this._numOfEntitiesToProcess--;
        if (this._numOfEntitiesToProcess < 0) {
            this._numOfEntitiesToProcess = 0;
            log.warn('Something wrong! The number of entities to process should not be less than 0.');
        }
    };
    return AcTrView2d;
}(AcEdBaseView));
export { AcTrView2d };
//# sourceMappingURL=AcTrView2d.js.map