/**
 * MCWorld.ts
 *
 * ARCHITECTURE DOCUMENTATION
 * ==========================
 *
 * MCWorld is the primary class for managing Minecraft world data, including:
 * - World metadata (level.dat, level name, spawn position)
 * - World chunks and blocks (via LevelDB)
 * - Behavior/resource pack registrations
 * - Dynamic properties and experiments
 *
 * REAL-TIME SYNCHRONIZATION:
 * --------------------------
 * MCWorld can listen to IStorage events to automatically update when external
 * changes occur (e.g., file changes from a remote server):
 *
 * 1. Call startListeningToStorage() to subscribe to storage events
 * 2. When onFileContentsUpdated fires, MCWorld reloads affected data
 * 3. MCWorld fires appropriate events (onChunkUpdated, onDataReloaded, etc.)
 * 4. WorldView and other UI components update in response
 *
 * EVENTS:
 * -------
 * - onLoaded: Fired when world initially loads
 * - onDataLoaded: Fired when world data (chunks) finishes loading
 * - onChunkUpdated: Fired when a specific chunk is updated/reloaded
 * - onWorldDataReloaded: Fired when world files are externally modified and reloaded
 * - onPropertyChanged: Fired when a world property changes
 *
 * DATA FLOW:
 * ----------
 * HttpStorage (WebSocket notifications) -> MCWorld (this) ->
 *   onChunkUpdated/onWorldDataReloaded -> WorldView (React update)
 *
 * RELATED FILES:
 * --------------
 * - IStorage.ts: Storage events (onFileContentsUpdated, etc.)
 * - HttpStorage.ts: Client-side storage with WebSocket notifications
 * - WorldView.tsx: UI component that displays world data
 * - WorldChunk.ts: Individual chunk data
 * - LevelDb.ts: LevelDB file parser
 */
import IFile from "../storage/IFile";
import ZipStorage from "../storage/ZipStorage";
import { IEventHandler } from "ste-events";
import IPackRegistration from "./IPackRegistration";
import IFolder from "./../storage/IFolder";
import IPackHistory from "./IPackHistory";
import WorldLevelDat from "./WorldLevelDat";
import IGetSetPropertyObject from "../dataform/IGetSetPropertyObject";
import IWorldManifest from "./IWorldManifest";
import LevelDb from "./LevelDb";
import WorldChunk from "./WorldChunk";
import BlockLocation from "./BlockLocation";
import BlockVolume from "./BlockVolume";
import IDimension from "./IDimension";
import Block from "./Block";
import Entity from "./Entity";
import { IPackageReference, IWorldSettings } from "./IWorldSettings";
import { StorageErrorStatus } from "../storage/IStorage";
import AnchorSet from "./AnchorSet";
import Project from "../app/Project";
import ActorItem from "./ActorItem";
import { IErrorMessage, IErrorable } from "../core/IErrorable";
import ProjectItem from "../app/ProjectItem";
import WorldChunkCache from "./WorldChunkCache";
export interface IWorldProcessingOptions {
    maxNumberOfRecordsToProcess?: number;
    progressCallback?: (phase: string, current: number, total: number) => void;
    /**
     * If true, unloads raw file content after parsing to reduce memory usage.
     * Recommended for large worlds to prevent out-of-memory errors.
     * Default: true (optimized for memory)
     */
    unloadFilesAfterParse?: boolean;
    /**
     * If true, deletes LevelDB keys from the keys Map after they are processed
     * and handed off to WorldChunks. This significantly reduces memory usage
     * for large worlds by eliminating duplicate references.
     * Default: true (optimized for memory)
     */
    clearKeysAfterProcess?: boolean;
    /**
     * If true, uses lazy loading mode for LevelDB.
     * Only manifest metadata is loaded initially; files are loaded on-demand.
     * This dramatically reduces initial memory usage for large worlds.
     *
     * When enabled:
     * - Initial load only parses manifest files
     * - LDB/LOG files are loaded only when their keys are needed
     * - Chunk cache manages memory by evicting least-recently-used chunks
     *
     * Default: false (full load for backwards compatibility)
     */
    lazyLoad?: boolean;
    /**
     * Maximum number of chunks to keep in the LRU cache when using lazy loading.
     * When exceeded, least-recently-used chunks have their parsed data cleared
     * (but can be re-parsed on demand from raw LevelKeyValue data).
     *
     * Default: 20000
     */
    maxChunksInCache?: number;
    /**
     * If true, skips the full "Phase 2" world data processing that creates
     * WorldChunk objects for every chunk upfront. Instead, chunks will be
     * created on-demand when they are first accessed.
     *
     * This is ideal for map viewing where only visible chunks need to be loaded.
     * It dramatically reduces memory usage for very large worlds (100k+ chunks).
     *
     * When enabled:
     * - World bounds (minX, maxX, minZ, maxZ) are calculated from key names
     * - Chunk objects are created lazily when getChunkAt() is called
     * - Full chunk data is only parsed when accessed
     *
     * Default: false (full processing for backwards compatibility)
     */
    skipFullProcessing?: boolean;
}
export interface IRegion {
    minX: number;
    minZ: number;
    maxX: number;
    maxZ: number;
}
export default class MCWorld implements IGetSetPropertyObject, IDimension, IErrorable {
    private _zipStorage?;
    private _file?;
    private _folder?;
    private _project?;
    private _autogenTsFile?;
    private _anchors;
    private _dynamicProperties;
    private _levelNameText?;
    private _manifest?;
    private _isLoaded;
    private _isDataLoaded;
    private _onLoaded;
    private _onDataLoaded;
    private _onChunkUpdated;
    /** Event fired when world data is externally modified and reloaded */
    private _onWorldDataReloaded;
    /** Whether we're listening to storage events for automatic updates */
    private _isListeningToStorage;
    private _hasDynamicProps;
    private _hasCustomProps;
    private _onPropertyChanged;
    private _biomeData;
    private _overworldData;
    private _levelChunkMetaData;
    private _generationSeed;
    isInErrorState?: boolean;
    errorMessages?: IErrorMessage[];
    worldBehaviorPacks?: IPackRegistration[];
    worldResourcePacks?: IPackRegistration[];
    worldBehaviorPackHistory?: IPackHistory;
    worldResourcePackHistory?: IPackHistory;
    chunkCount: number;
    private _chunkMinY;
    imageBase64?: string;
    levelDb?: LevelDb;
    actorsById: {
        [identifier: string]: ActorItem;
    };
    levelData?: WorldLevelDat;
    private _minX;
    private _maxX;
    private _minZ;
    private _maxZ;
    regionsByDimension: {
        [dim: number]: IRegion[];
    };
    chunks: Map<number, Map<number, Map<number, WorldChunk>>>;
    /**
     * All dimension IDs found in LevelDB chunk keys, including custom dimensions (>= 1000).
     * Populated during processWorldData or buildMinimalWorldIndex.
     */
    private _dimensionIdsInChunks;
    /**
     * Parsed DimensionNameIdTable from LevelDB: maps dimension name to numeric ID.
     * Undefined if the DimensionNameIdTable key was not found.
     */
    private _dimensionNameIdTable;
    /** Whether the DimensionNameIdTable key exists in the LevelDB */
    private _hasDimensionNameIdTable;
    /** LRU cache for chunk data - manages memory by evicting old chunks */
    private _chunkCache?;
    /** Whether lazy loading mode is enabled for this world */
    private _isLazyLoadMode;
    /**
     * Set of chunk keys that exist in the world (format: "dim_x_z").
     * Built during buildMinimalWorldIndex for O(1) chunk existence checking.
     * This allows fast filtering of empty/non-existent chunks without scanning LevelDB keys.
     */
    private _chunkExistsSet;
    /**
     * Returns the set of all known chunk keys (format: "dim_x_z").
     * Used by WorldMap to ensure sparse worlds render all known chunks,
     * not just those hit by the sampling grid.
     */
    get knownChunkKeys(): ReadonlySet<string>;
    /**
     * Index mapping chunk keys ("dim_x_z") to the list of LevelDB key names for that chunk.
     * Built during buildMinimalWorldIndex for O(1) chunk key lookup.
     * This eliminates the O(N) full-scan of LevelDB keys in getOrCreateChunk.
     */
    private _chunkKeyIndex;
    get project(): Project | undefined;
    set project(newProject: Project | undefined);
    get anchors(): AnchorSet;
    get chunkMinY(): number;
    set chunkMinY(newY: number);
    get effectiveRootFolder(): IFolder | import("../storage/ZipFolder").default;
    get manifest(): IWorldManifest;
    get hasDynamicProps(): boolean;
    get hasCustomProps(): boolean;
    get minX(): number;
    get maxX(): number;
    get minZ(): number;
    get maxZ(): number;
    /** Get whether lazy loading mode is enabled */
    get isLazyLoadMode(): boolean;
    /** Get the chunk cache (only available when using chunk caching) */
    get chunkCache(): WorldChunkCache | undefined;
    /** All dimension IDs found in LevelDB chunk keys, including custom dimensions (>= 1000). */
    get dimensionIdsInChunks(): ReadonlySet<number>;
    /** Whether the DimensionNameIdTable key was found in the LevelDB. */
    get hasDimensionNameIdTable(): boolean;
    /** Parsed DimensionNameIdTable: maps dimension name to numeric ID. Undefined if not found. */
    get dimensionNameIdTable(): ReadonlyMap<string, number> | undefined;
    /** Parse DimensionNameIdTable NBT bytes into the name-to-ID map. */
    private _parseDimensionNameIdTable;
    /**
     * Check if chunk data exists at the specified coordinates without loading it.
     * This is O(1) when buildMinimalWorldIndex has been called (skipFullProcessing mode).
     * Returns true if the chunk exists in the world's LevelDB data.
     * Returns undefined if existence cannot be determined (index not built).
     */
    hasChunkData(dim: number, x: number, z: number): boolean | undefined;
    /**
     * Get a chunk by coordinates.
     * If chunk caching is enabled, marks the chunk as recently accessed.
     */
    getChunkAt(dim: number, x: number, z: number): WorldChunk | undefined;
    /**
     * Get a chunk by cache key (format: "dim_x_z").
     * Used by WorldChunkCache for eviction callbacks.
     */
    getChunkByKey(key: string): WorldChunk | undefined;
    get generationSeed(): string;
    copyAsFolderTo(targetFolder: IFolder): Promise<void>;
    get storage(): import("../storage/IStorage").default | ZipStorage;
    ensureZipStorage(): void;
    get onPropertyChanged(): import("ste-events").IEvent<MCWorld, string>;
    get storageErrorStatus(): StorageErrorStatus;
    get storageErrorMessage(): string;
    get storageFullPath(): string;
    get deferredTechnicalPreviewExperiment(): boolean;
    set deferredTechnicalPreviewExperiment(newVal: boolean);
    get betaApisExperiment(): boolean;
    set betaApisExperiment(newVal: boolean);
    get dataDrivenItemsExperiment(): boolean;
    set dataDrivenItemsExperiment(newVal: boolean);
    get name(): string;
    set name(newValue: string);
    get file(): IFile | undefined;
    set file(newFile: IFile | undefined);
    get folder(): IFolder | undefined;
    set folder(newFolder: IFolder | undefined);
    get isLoaded(): boolean;
    get spawnX(): number | undefined;
    set spawnX(newX: number | undefined);
    get spawnY(): number | undefined;
    set spawnY(newY: number | undefined);
    get spawnZ(): number | undefined;
    set spawnZ(newZ: number | undefined);
    get onLoaded(): import("ste-events").IEvent<MCWorld, MCWorld>;
    get onDataLoaded(): import("ste-events").IEvent<MCWorld, MCWorld>;
    get onChunkUpdated(): import("ste-events").IEvent<MCWorld, WorldChunk>;
    /**
     * Event fired when world data is externally modified and reloaded.
     * Subscribe to this event to update UI when remote changes occur.
     */
    get onWorldDataReloaded(): import("ste-events").IEvent<MCWorld, string>;
    /**
     * Whether this world is listening to storage events for automatic updates.
     */
    get isListeningToStorage(): boolean;
    /**
     * Start listening to storage events for automatic world data updates.
     * When file changes are detected (via WebSocket or fs watcher), the world
     * will automatically reload affected data and fire appropriate events.
     */
    startListeningToStorage(): void;
    /**
     * Stop listening to storage events.
     */
    stopListeningToStorage(): void;
    /**
     * Handle a file update from storage.
     */
    private _handleStorageFileUpdate;
    /**
     * Handle a new file being added to storage.
     */
    private _handleStorageFileAdded;
    /**
     * Handle a file being removed from storage.
     */
    private _handleStorageFileRemoved;
    /**
     * Handle incremental LevelDB file updates.
     *
     * When a new .ldb or .log file is detected, this method:
     * 1. Parses just that file to extract new keys
     * 2. Identifies which chunks are affected by those keys
     * 3. Updates only those chunks with the new data
     * 4. Fires onChunkUpdated for each affected chunk (for UI updates)
     *
     * This is much more efficient than reloading the entire world.
     */
    private _handleIncrementalLevelDbUpdate;
    /**
     * Update a single chunk from LevelDB keys.
     *
     * This finds all keys for the specified chunk coordinates and either:
     * - Updates an existing chunk with the new data
     * - Creates a new chunk if one doesn't exist
     *
     * After updating, fires onChunkUpdated for UI refresh.
     */
    private _updateChunkFromLevelDb;
    /**
     * Called by WorldChunk when chunk data is superceded by newer LevelDB keys.
     * This notifies subscribers (like WorldMap) that they may need to redraw affected tiles.
     */
    notifyChunkUpdated(chunk: WorldChunk): void;
    static ensureMCWorldOnFolder(folder: IFolder, project?: Project, handler?: IEventHandler<MCWorld, MCWorld>): Promise<MCWorld>;
    static ensureOnItem(projectItem: ProjectItem): Promise<MCWorld>;
    static ensureOnFile(file: IFile, project?: Project, handler?: IEventHandler<MCWorld, MCWorld>): Promise<MCWorld>;
    loadAnchorsFromDynamicProperties(): void;
    _updateMeta(): void;
    private _coalesceRegions;
    private _pushError;
    save(): Promise<void>;
    private saveWorldManifest;
    private saveLevelnameTxt;
    private saveLevelDat;
    getBytes(): Promise<Uint8Array<ArrayBufferLike>>;
    syncFolderTo(folder: IFolder): Promise<void>;
    saveToFile(): Promise<void>;
    ensurePackReferenceSet(packRefSet: IPackageReference): void;
    ensurePackReferenceInCollection(packRef: {
        uuid: string;
        version: number[];
        priority?: number;
    }, packRefs: IPackRegistration[]): void;
    ensurePackReferenceInHistory(packRef: {
        uuid: string;
        version: number[];
        priority?: number;
    }, packHistory: IPackHistory, name: string): void;
    private _loadFromNbt;
    getProperty(id: string): any;
    getBaseValue(): any;
    setBaseValue(value: any): void;
    setProperty(id: string, newVal: any): any;
    loadMetaFiles(force?: boolean): Promise<void>;
    ensureResourcePacksFromString(packStr: string): void;
    ensureBehaviorPacksFromString(packStr: string): void;
    ensureBehaviorPack(packId: string, version: number[], packName: string, packPriority?: number): boolean;
    getBehaviorPack(packId: string): IPackRegistration;
    getBehaviorPackHistory(packId: string): import("./IPackHistoryItem").default;
    static sortPackRegByPriority(a: IPackRegistration, b: IPackRegistration): number;
    static sortPackCollectionByPriority(packRefs: IPackRegistration[]): IPackRegistration[];
    static freezePackRegistrationOrder(packRefs: IPackRegistration[]): void;
    saveWorldBehaviorPacks(): Promise<void>;
    static freezeAndStripPriorities(coll: IPackRegistration[]): IPackRegistration[];
    saveWorldBehaviorPackHistory(): Promise<void>;
    ensureResourcePack(packId: string, version: number[], packName: string, packPriority?: number): boolean;
    getResourcePack(packId: string): IPackRegistration;
    getResourcePackHistory(packId: string): import("./IPackHistoryItem").default;
    saveWorldResourcePacks(): Promise<void>;
    saveWorldResourcePackHistory(): Promise<void>;
    loadFromBytes(content: Uint8Array): Promise<void>;
    applyWorldSettings(worldSettings?: IWorldSettings): Promise<void>;
    ensureLevelData(): WorldLevelDat;
    loadFromFolder(rootFolder: IFolder): Promise<void>;
    loadLevelDb(force?: boolean, options?: IWorldProcessingOptions): Promise<boolean>;
    loadFromLevelDb(levelDb: LevelDb, options?: IWorldProcessingOptions): Promise<boolean>;
    /**
     * Builds a minimal world index without creating WorldChunk objects.
     * This calculates world bounds and chunk count from key names only.
     * Chunks are created on-demand when getChunkAt() or getOrCreateChunk() is called.
     *
     * This dramatically reduces memory usage for large worlds (100k+ chunks).
     *
     * IMPORTANT: Key filtering must use the same approach as processWorldData():
     * explicit named-key prefix checks + keyname.length checks. Do NOT filter by
     * checking if the first byte of keyBytes is in printable ASCII range, because
     * chunk coordinate keys are binary little-endian integers whose low byte can
     * legitimately be any value 0-255 (e.g., chunk X=32 → first byte 0x20 = space).
     */
    private buildMinimalWorldIndex;
    /**
     * Gets or creates a chunk at the specified coordinates.
     * If the chunk doesn't exist, creates it and populates it from LevelDB keys.
     * This is used for on-demand chunk loading when skipFullProcessing is enabled.
     */
    getOrCreateChunk(dim: number, x: number, z: number): WorldChunk | undefined;
    /**
     * Iterates over all chunks in a memory-efficient manner, calling the processor function
     * for each chunk and optionally clearing chunk data after processing.
     *
     * @param processor - Async function to process each chunk. Receives the chunk and its coordinates.
     * @param options - Optional configuration for iteration behavior.
     * @param options.clearCacheAfterProcess - If true, clears parsed/cached data after processing but preserves
     *                                          raw LevelKeyValue data, allowing chunks to be re-parsed on demand.
     *                                          This is the recommended option for memory optimization.
     * @param options.clearAllAfterProcess - If true, aggressively clears ALL chunk data including raw bytes.
     *                                        WARNING: Chunks cannot be re-parsed after this. Only use when
     *                                        the world data will never be accessed again.
     * @param options.dimensionFilter - If specified, only iterate chunks in this dimension (0=overworld, 1=nether, 2=end).
     * @param options.progressCallback - Optional callback for progress updates during iteration.
     */
    forEachChunk(processor: (chunk: WorldChunk, x: number, z: number, dimension: number) => Promise<void>, options?: {
        clearCacheAfterProcess?: boolean;
        clearAllAfterProcess?: boolean;
        dimensionFilter?: number;
        progressCallback?: (processed: number, total: number) => Promise<void>;
    }): Promise<void>;
    /**
     * Clears parsed/cached data from all chunks to free memory while preserving the ability
     * to re-parse chunks on demand. This is the recommended approach for memory optimization
     * when you may need to access chunk data again (e.g., for map rendering).
     */
    clearAllChunkCaches(): void;
    /**
     * Clears the raw LevelDB data to free memory after world data has been processed.
     * This can significantly reduce memory usage for large worlds.
     * WARNING: After calling this, the world cannot be re-loaded from the LevelDb.
     */
    clearLevelDbData(): void;
    /**
     * Clears all world data to free memory.
     * Use this when the world is no longer needed.
     * WARNING: The world cannot be used after calling this without reloading.
     */
    clearAllData(): void;
    /**
     * Get statistics about memory usage for this world.
     * Useful for debugging memory issues with large worlds.
     */
    getMemoryStats(): {
        chunkCount: number;
        levelDbKeyCount: number;
        isLazyMode: boolean;
        chunkCacheSize?: number;
        chunkCacheMaxSize?: number;
    };
    /**
     * Clears all chunk data to free memory.
     * WARNING: After calling this, chunk data cannot be accessed without reloading.
     */
    clearAllChunkData(): void;
    getTopBlockY(x: number, z: number, dim?: number): number;
    getTopBlock(x: number, z: number, dim?: number): Block;
    spawnEntity(entityTypeId: string, location: BlockLocation): Entity;
    getBlock(blockLocation: BlockLocation, dim?: number): Block;
    private processWorldData;
    private notifyLoadEnded;
    private saveAutoGenItems;
    private getAutoGenScript;
    getCube(from: BlockLocation, to: BlockLocation, dim?: number): BlockVolume;
    getSubChunkCube(x: number, y: number, z: number, dim?: number): BlockVolume;
}
