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.");
};
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;
};
import { AcTrGroup } from '@mlightcad/three-renderer';
import * as THREE from 'three';
import { AcTrHierarchicalSpatialIndex } from '../spatialIndex';
import { AcTrLayer } from './AcTrLayer';
/**
 * This class represents objects contained in one AutoCAD layout (model space or paper space).
 *
 * A layout manages the organization and rendering of CAD entities within a specific coordinate space.
 * It provides functionality for:
 * - Managing entities organized by layers
 * - Spatial indexing for efficient entity queries
 * - Bounding box management for view operations
 * - Entity selection and highlighting
 * - Memory usage tracking and statistics
 *
 * Layouts use a spatial index (R-tree) for fast entity lookup operations and maintain
 * a hierarchical structure where entities are grouped by layers for efficient rendering
 * and visibility management.
 *
 * @example
 * ```typescript
 * const layout = new AcTrLayout();
 * layout.addEntity(entity);
 * const entities = layout.search(boundingBox);
 * layout.select(['entity1', 'entity2']);
 * ```
 */
var AcTrLayout = /** @class */ (function () {
    /**
     * Creates a new layout instance.
     * Initializes the layout with empty collections and a spatial index.
     */
    function AcTrLayout() {
        this._group = new THREE.Group();
        this._spatialIndex = new AcTrHierarchicalSpatialIndex();
        this._box = new THREE.Box3();
        this._layers = new Map();
        this._isLoaded = false;
    }
    Object.defineProperty(AcTrLayout.prototype, "internalObject", {
        /**
         * The internal THREE.js object to use by scene. This is internally used only. Try to avoid using it.
         * @internal
         */
        get: function () {
            return this._group;
        },
        enumerable: false,
        configurable: true
    });
    Object.defineProperty(AcTrLayout.prototype, "layers", {
        /**
         * Gets the map of layers in this layout.
         *
         * @returns Map of layer names to layer objects
         */
        get: function () {
            return this._layers;
        },
        enumerable: false,
        configurable: true
    });
    Object.defineProperty(AcTrLayout.prototype, "box", {
        /**
         * Gets the bounding box that contains all entities in this layout.
         *
         * @returns The layout's bounding box
         */
        get: function () {
            return this._box;
        },
        enumerable: false,
        configurable: true
    });
    Object.defineProperty(AcTrLayout.prototype, "isLoaded", {
        /**
         * The flag indicating whether the layout is loaded/activated.
         *
         * @returns The flag indicating whether the layout is loaded/activated.
         */
        get: function () {
            return this._isLoaded;
        },
        /**
         * Sets the flag indicating whether the layout is loaded/activated.
         *
         * @param value - The flag indicating whether the layout is loaded/activated.
         */
        set: function (value) {
            this._isLoaded = value;
        },
        enumerable: false,
        configurable: true
    });
    Object.defineProperty(AcTrLayout.prototype, "visible", {
        /**
         * The visibility of this layout.
         * When set to false, the entire layout and all its contents are hidden.
         */
        get: function () {
            return this._group.visible;
        },
        set: function (value) {
            this._group.visible = value;
        },
        enumerable: false,
        configurable: true
    });
    Object.defineProperty(AcTrLayout.prototype, "entityCount", {
        /**
         * The number of entities stored in this layout.
         * Calculates the total by summing entities across all layers.
         */
        get: function () {
            var count = 0;
            this._layers.forEach(function (layer) { return (count += layer.entityCount); });
            return count;
        },
        enumerable: false,
        configurable: true
    });
    Object.defineProperty(AcTrLayout.prototype, "stats", {
        /**
         * The statistics of this layout.
         * Provides detailed information about memory usage and entity counts.
         */
        get: function () {
            var layers = [];
            var totalGeometrySize = 0;
            var totalMappingSize = 0;
            var lineTotalSize = 0;
            var meshTotalSize = 0;
            var pointTotalSize = 0;
            var unbatchedTotalSize = 0;
            var unbatchedTotalCount = 0;
            var unbatchedByType = {
                line: 0,
                mesh: 0,
                point: 0,
                other: 0
            };
            this._layers.forEach(function (layer) {
                var stats = layer.stats;
                layers.push(stats);
                lineTotalSize +=
                    stats.line.indexed.geometrySize + stats.line.nonIndexed.geometrySize;
                meshTotalSize +=
                    stats.mesh.indexed.geometrySize + stats.mesh.nonIndexed.geometrySize;
                pointTotalSize +=
                    stats.point.indexed.geometrySize + stats.point.nonIndexed.geometrySize;
                unbatchedTotalSize += stats.unbatched.geometrySize;
                unbatchedTotalCount += stats.unbatched.count;
                unbatchedByType.line += stats.unbatched.byType.line;
                unbatchedByType.mesh += stats.unbatched.byType.mesh;
                unbatchedByType.point += stats.unbatched.byType.point;
                unbatchedByType.other += stats.unbatched.byType.other;
                totalGeometrySize += stats.summary.totalGeometrySize;
                totalMappingSize += stats.summary.totalMappingSize;
            });
            return {
                layers: layers,
                summary: {
                    entityCount: this.entityCount,
                    totalSize: {
                        line: lineTotalSize,
                        mesh: meshTotalSize,
                        point: pointTotalSize,
                        unbatched: unbatchedTotalSize,
                        geometry: totalGeometrySize,
                        mapping: totalMappingSize,
                        unbatchedCount: unbatchedTotalCount,
                        unbatchedByType: unbatchedByType
                    }
                }
            };
        },
        enumerable: false,
        configurable: true
    });
    /**
     * Clears all entities from the layout.
     * Removes all layers, resets the bounding box, and clears the spatial index.
     *
     * @returns This layout instance for method chaining
     */
    AcTrLayout.prototype.clear = function () {
        this._layers.forEach(function (layer) {
            layer.clear();
        });
        this._layers.clear();
        this._box.makeEmpty();
        this._spatialIndex.clear();
        return this;
    };
    /**
     * Re-render points with latest point style settings.
     * Updates the visual representation of all point entities across all layers.
     *
     * @param displayMode - Input display mode of points
     */
    AcTrLayout.prototype.rerenderPoints = function (displayMode) {
        this._layers.forEach(function (layer) {
            layer.rerenderPoints(displayMode);
        });
    };
    /**
     * Return true if the object with the specified object id is intersected with the ray by using raycast.
     *
     * @param objectId - Input object id of object to check for intersection with the ray.
     * @param raycaster - Input raycaster to check intersection
     * @returns True if the object intersects with the ray, false otherwise
     */
    AcTrLayout.prototype.isIntersectWith = function (objectId, raycaster) {
        var layers = this.getLayersByObjectId(objectId);
        for (var index = 0; index < layers.length; ++index) {
            var layer = layers[index];
            if (layer && layer.isIntersectWith(objectId, raycaster))
                return true;
        }
        return false;
    };
    /**
     * Add one AutoCAD entity into this layout. If layer group referenced by the entity doesn't exist, create one
     * layer group and add this entity this group.
     *
     * @param entity - Input AutoCAD entity to be added into this layout.
     * @param extendBbox - Input the flag whether to extend the bounding box of the scene by union the bounding box
     * of the specified entity. Defaults to true.
     * @returns This layout instance for method chaining
     *
     * @throws {Error} When entity is missing required objectId or layerName
     */
    AcTrLayout.prototype.addEntity = function (entity, extendBbox) {
        if (extendBbox === void 0) { extendBbox = true; }
        if (!entity.objectId) {
            throw new Error('Object id is required to add one entity!');
        }
        if (!entity.layerName) {
            throw new Error('Layer name is required to add one entity!');
        }
        var layer = this._layers.get(entity.layerName);
        if (!layer) {
            throw new Error("layer '".concat(entity.layerName, "' doesn't exist!"));
        }
        layer.addEntity(entity);
        var box = entity.box;
        // For infinitive line such as ray and xline, they are not used to extend box
        if (extendBbox)
            this._box.union(box);
        this._spatialIndex.insert({
            minX: box.min.x,
            minY: box.min.y,
            maxX: box.max.x,
            maxY: box.max.y,
            id: entity.objectId
        });
        // If it is one block, we need to build spatial index for entities in this block
        if (entity instanceof AcTrGroup) {
            this._spatialIndex.createChildIndex(entity);
        }
        return this;
    };
    /**
     * Remove the specified entity from this layout.
     *
     * @param objectId - Input the object id of the entity to remove
     * @returns Return true if remove the specified entity successfully. Otherwise, return false.
     */
    AcTrLayout.prototype.removeEntity = function (objectId) {
        var e_1, _a;
        var result = false;
        try {
            for (var _b = __values(this._layers), _c = _b.next(); !_c.done; _c = _b.next()) {
                var _d = __read(_c.value, 2), _1 = _d[0], layer = _d[1];
                if (layer.removeEntity(objectId)) {
                    result = true;
                }
            }
        }
        catch (e_1_1) { e_1 = { error: e_1_1 }; }
        finally {
            try {
                if (_c && !_c.done && (_a = _b.return)) _a.call(_b);
            }
            finally { if (e_1) throw e_1.error; }
        }
        this._spatialIndex.removeById(objectId);
        return result;
    };
    /**
     * Update the specified entity in this layout.
     *
     * @param entity - Input the entity to update
     * @returns Return true if update the specified entity successfully. Otherwise, return false.
     */
    AcTrLayout.prototype.updateEntity = function (entity) {
        var e_2, _a;
        try {
            for (var _b = __values(this._layers), _c = _b.next(); !_c.done; _c = _b.next()) {
                var _d = __read(_c.value, 2), _2 = _d[0], layer = _d[1];
                if (layer.updateEntity(entity))
                    return true;
            }
        }
        catch (e_2_1) { e_2 = { error: e_2_1 }; }
        finally {
            try {
                if (_c && !_c.done && (_a = _b.return)) _a.call(_b);
            }
            finally { if (e_2) throw e_2.error; }
        }
        // TODO: Uodate spatial index
        return false;
    };
    /**
     * Gets the layer with the specified name from this layout
     * @param name - Layer name
     * @returns - The layer with the specified name in this layout
     */
    AcTrLayout.prototype.getLayer = function (name) {
        return this._layers.get(name);
    };
    /**
     * Adds layer into this layout. If the layer already exist, do nothing.
     *
     * @param name - Input layer name
     * @returns Return added layer or the existing layer in this layout if one layer
     * group already exists in this layout.
     */
    AcTrLayout.prototype.addLayer = function (info) {
        var name = info.name;
        var layer = this._layers.get(name);
        if (layer === undefined) {
            layer = new AcTrLayer(info);
            this._layers.set(name, layer);
            this._group.add(layer.internalObject);
        }
        return layer;
    };
    /**
     * Updates layer information (such as visibility). If the layer doesn't exist, do nothing.
     * @param info - The layer information
     * @returns Returns the updated layer group.
     */
    AcTrLayout.prototype.updateLayer = function (info) {
        var layer = this._layers.get(info.name);
        if (layer) {
            // TODO: Handle layer name changes
            layer.update(info);
        }
        return layer;
    };
    /**
     * Hover the specified entities.
     * Applies hover highlighting to the entities with the given IDs.
     *
     * @param ids - Array of entity object IDs to hover
     */
    AcTrLayout.prototype.hover = function (ids) {
        var _this = this;
        ids.forEach(function (id) {
            var layers = _this.getLayersByObjectId(id);
            layers.forEach(function (layer) { return layer.hover([id]); });
        });
    };
    /**
     * Unhover the specified entities.
     * Removes hover highlighting from the entities with the given IDs.
     *
     * @param ids - Array of entity object IDs to unhover
     */
    AcTrLayout.prototype.unhover = function (ids) {
        var _this = this;
        ids.forEach(function (id) {
            var layers = _this.getLayersByObjectId(id);
            layers.forEach(function (layer) { return layer.unhover([id]); });
        });
    };
    /**
     * Select the specified entities.
     * Applies selection highlighting to the entities with the given IDs.
     *
     * @param ids - Array of entity object IDs to select
     */
    AcTrLayout.prototype.select = function (ids) {
        var _this = this;
        ids.forEach(function (id) {
            var layers = _this.getLayersByObjectId(id);
            layers.forEach(function (layer) { return layer.select([id]); });
        });
    };
    /**
     * Unselect the specified entities.
     * Removes selection highlighting from the entities with the given IDs.
     *
     * @param ids - Array of entity object IDs to unselect
     */
    AcTrLayout.prototype.unselect = function (ids) {
        var _this = this;
        ids.forEach(function (id) {
            var layers = _this.getLayersByObjectId(id);
            layers.forEach(function (layer) { return layer.unselect([id]); });
        });
    };
    /**
     * Search entities intersected or contained in the specified bounding box.
     * Uses the spatial index for efficient querying of entities within the given bounds.
     *
     * @param box - Input the query bounding box (2D or 3D)
     * @returns Return query results containing entity IDs and their bounds
     */
    AcTrLayout.prototype.search = function (box) {
        var results = this._spatialIndex.search({
            minX: box.min.x,
            minY: box.min.y,
            maxX: box.max.x,
            maxY: box.max.y
        });
        return results;
    };
    /**
     * Returns all layers that contain renderable entities associated with
     * the specified AutoCAD object ID.
     *
     * 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, and this method returns all such layers.
     *
     * @param objectId - The AutoCAD object ID to search for (e.g. an INSERT entity ID)
     * @returns An array of layers containing entities associated with the given object ID;
     *          returns an empty array if no matching layers are found
     */
    AcTrLayout.prototype.getLayersByObjectId = function (objectId) {
        var e_3, _a;
        var layers = [];
        try {
            for (var _b = __values(this._layers), _c = _b.next(); !_c.done; _c = _b.next()) {
                var _d = __read(_c.value, 2), _3 = _d[0], layer = _d[1];
                if (layer.hasEntity(objectId)) {
                    layers.push(layer);
                }
            }
        }
        catch (e_3_1) { e_3 = { error: e_3_1 }; }
        finally {
            try {
                if (_c && !_c.done && (_a = _b.return)) _a.call(_b);
            }
            finally { if (e_3) throw e_3.error; }
        }
        return layers;
    };
    return AcTrLayout;
}());
export { AcTrLayout };
//# sourceMappingURL=AcTrLayout.js.map