import { SceneImporter } from './SceneImporter.js';

/**
 * @file LDtkImporter.ts
 * @description Import LDtk (Level Designer Toolkit) files
 * PRO-1.3: Scene Format Interoperability
 *
 * Supports:
 * - LDtk project files (.ldtk)
 * - LDtk level files (.ldtkl)
 *
 * Features:
 * - World/Level hierarchy
 * - IntGrid layers
 * - Tile layers
 * - Entity layers
 * - Auto-layer rules
 * - Enums
 * - Level backgrounds
 */
// ============================================================
// LDtk Importer
// ============================================================
class LDtkImporter extends SceneImporter {
    constructor() {
        super('ldtk');
        this.project = null;
        this.tilesets = new Map();
        this.entityDefs = new Map();
        this.layerDefs = new Map();
        this.enums = new Map();
    }
    async import(options) {
        const startTime = Date.now();
        this.options = options;
        this.clearMessages();
        this.tilesets.clear();
        this.entityDefs.clear();
        this.layerDefs.clear();
        this.enums.clear();
        const errors = await this.validate(options.sourcePath);
        if (errors.some((e) => e.fatal)) {
            return {
                ...this.createEmptyResult(),
                errors: errors,
            };
        }
        const scenes = [];
        const assets = [];
        try {
            const content = await this.readFile(options.sourcePath);
            this.project = JSON.parse(content);
            // Index definitions
            this.indexDefinitions();
            // Load external levels if needed
            if (this.project.externalLevels) {
                await this.loadExternalLevels(options.sourcePath);
            }
            // Convert each level to a scene
            const levels = this.getAllLevels();
            for (const level of levels) {
                const parsed = this.convertLevelToScene(level);
                const niceScene = this.convertParsedToNiceScene(parsed);
                scenes.push(niceScene);
            }
            // Extract assets
            assets.push(...this.extractAssets());
        }
        catch (error) {
            this.addError('IMPORT_ERROR', `Failed to import: ${error}`, options.sourcePath, undefined, true);
        }
        return this.createResult(scenes, assets, startTime);
    }
    async parseFile(content, filePath) {
        const stringContent = typeof content === 'string' ? content : new TextDecoder().decode(content);
        try {
            const data = JSON.parse(stringContent);
            // Check if it's a project file or a single level
            if (data.jsonVersion) {
                // Full project
                this.project = data;
                this.indexDefinitions();
                const levels = this.getAllLevels();
                if (levels.length > 0) {
                    return this.convertLevelToScene(levels[0]);
                }
            }
            else if (data.layerInstances) {
                // Single external level
                return this.convertLevelToScene(data);
            }
        }
        catch (error) {
            this.addError('JSON_PARSE_ERROR', `Failed to parse JSON: ${error}`);
        }
        return null;
    }
    async validate(sourcePath) {
        const errors = [];
        if (!this.canHandle(sourcePath)) {
            errors.push({
                code: 'INVALID_FORMAT',
                message: `File ${sourcePath} is not a valid LDtk file`,
                file: sourcePath,
                fatal: true,
            });
        }
        return errors;
    }
    // ----------------------------------------
    // Index Definitions
    // ----------------------------------------
    indexDefinitions() {
        if (!this.project)
            return;
        for (const tileset of this.project.defs.tilesets) {
            this.tilesets.set(tileset.uid, tileset);
        }
        for (const entity of this.project.defs.entities) {
            this.entityDefs.set(entity.uid, entity);
        }
        for (const layer of this.project.defs.layers) {
            this.layerDefs.set(layer.uid, layer);
        }
        for (const enumDef of [...this.project.defs.enums, ...this.project.defs.externalEnums]) {
            this.enums.set(enumDef.identifier, enumDef);
        }
    }
    getAllLevels() {
        if (!this.project)
            return [];
        if (this.project.worlds && this.project.worlds.length > 0) {
            // Multi-world project
            return this.project.worlds.flatMap((w) => w.levels);
        }
        return this.project.levels;
    }
    async loadExternalLevels(projectPath) {
        if (!this.project)
            return;
        const basePath = projectPath.substring(0, projectPath.lastIndexOf('/') + 1);
        for (const level of this.project.levels) {
            if (level.externalRelPath && !level.layerInstances) {
                try {
                    const levelPath = basePath + level.externalRelPath;
                    const content = await this.readFile(levelPath);
                    const externalLevel = JSON.parse(content);
                    level.layerInstances = externalLevel.layerInstances;
                }
                catch (error) {
                    this.addWarning('LEVEL_LOAD_ERROR', `Failed to load external level: ${level.externalRelPath}`, level.externalRelPath);
                }
            }
        }
    }
    // ----------------------------------------
    // Conversion
    // ----------------------------------------
    convertLevelToScene(level) {
        var _a;
        const layers = [];
        const entities = [];
        if (level.layerInstances) {
            for (let i = 0; i < level.layerInstances.length; i++) {
                const layerInstance = level.layerInstances[i];
                const layerDef = this.layerDefs.get(layerInstance.layerDefUid);
                // Convert layer
                const parsedLayer = this.convertLayer(layerInstance, i, layerDef);
                layers.push(parsedLayer);
                // Extract entities from entity layers
                if (layerInstance.__type === 'Entities') {
                    for (const entityInstance of layerInstance.entityInstances) {
                        entities.push(this.convertEntity(entityInstance, layerInstance.__identifier, i));
                    }
                }
            }
        }
        // Reverse layers (LDtk stores bottom-to-top, we want top-to-bottom z-order)
        layers.reverse();
        return {
            name: level.identifier,
            width: level.pxWid,
            height: level.pxHei,
            backgroundColor: level.bgColor || ((_a = this.project) === null || _a === void 0 ? void 0 : _a.defaultLevelBgColor) || '#000000',
            layers,
            entities,
            properties: {
                iid: level.iid,
                uid: level.uid,
                worldX: level.worldX,
                worldY: level.worldY,
                worldDepth: level.worldDepth,
                bgRelPath: level.bgRelPath,
                neighbours: level.__neighbours,
                ...this.convertFieldInstances(level.fieldInstances),
            },
        };
    }
    convertLayer(layer, zIndex, def) {
        var _a, _b;
        const parsedLayer = {
            name: layer.__identifier,
            type: this.mapLayerType(layer.__type),
            visible: layer.visible,
            opacity: layer.__opacity,
            offsetX: layer.pxOffsetX + layer.__pxTotalOffsetX,
            offsetY: layer.pxOffsetY + layer.__pxTotalOffsetY,
            parallaxX: (_a = def === null || def === void 0 ? void 0 : def.parallaxFactorX) !== null && _a !== void 0 ? _a : 1,
            parallaxY: (_b = def === null || def === void 0 ? void 0 : def.parallaxFactorY) !== null && _b !== void 0 ? _b : 1,
            zIndex,
            properties: {
                gridSize: layer.__gridSize,
                seed: layer.seed,
                tilesetPath: layer.__tilesetRelPath,
                tilesetUid: layer.__tilesetDefUid,
            },
        };
        // IntGrid or Tile layer data
        if (layer.__type === 'IntGrid' || layer.__type === 'AutoLayer') {
            parsedLayer.data = {
                width: layer.__cWid,
                height: layer.__cHei,
                tileWidth: layer.__gridSize,
                tileHeight: layer.__gridSize,
                tiles: layer.intGridCsv,
            };
            // Auto-layer tiles
            if (layer.autoLayerTiles.length > 0) {
                parsedLayer.properties = {
                    ...parsedLayer.properties,
                    autoTiles: layer.autoLayerTiles.map((t) => this.convertTile(t)),
                };
            }
        }
        // Regular tile layer
        if (layer.__type === 'Tiles') {
            parsedLayer.data = {
                width: layer.__cWid,
                height: layer.__cHei,
                tileWidth: layer.__gridSize,
                tileHeight: layer.__gridSize,
                tiles: this.gridTilesToArray(layer.gridTiles, layer.__cWid, layer.__cHei),
            };
            parsedLayer.properties = {
                ...parsedLayer.properties,
                gridTiles: layer.gridTiles.map((t) => this.convertTile(t)),
            };
        }
        return parsedLayer;
    }
    mapLayerType(type) {
        switch (type) {
            case 'IntGrid':
            case 'AutoLayer':
            case 'Tiles':
                return 'tile';
            case 'Entities':
                return 'object';
            default:
                return 'object';
        }
    }
    convertTile(tile) {
        var _a;
        return {
            px: tile.px,
            src: tile.src,
            tileId: tile.t,
            flip: tile.f,
            depth: tile.d,
            alpha: (_a = tile.a) !== null && _a !== void 0 ? _a : 1,
        };
    }
    gridTilesToArray(tiles, width, height) {
        const result = new Array(width * height).fill(0);
        for (const tile of tiles) {
            const x = Math.floor(tile.px[0] / (tile.src[0] || 16)); // Approximate grid position
            const y = Math.floor(tile.px[1] / (tile.src[1] || 16));
            if (x >= 0 && x < width && y >= 0 && y < height) {
                result[y * width + x] = tile.t + 1; // +1 because 0 means empty
            }
        }
        return result;
    }
    convertEntity(entity, layerName, zIndex) {
        const def = this.entityDefs.get(entity.defUid);
        const components = [];
        // Add visual component if entity has a tile
        if (entity.__tile) {
            const tileset = this.tilesets.get(entity.__tile.tilesetUid);
            components.push({
                type: 'Sprite',
                data: {
                    tileset: (tileset === null || tileset === void 0 ? void 0 : tileset.identifier) || 'unknown',
                    tileRect: {
                        x: entity.__tile.x,
                        y: entity.__tile.y,
                        width: entity.__tile.w,
                        height: entity.__tile.h,
                    },
                },
            });
        }
        // Add render mode component
        if (def) {
            components.push({
                type: 'EntityVisual',
                data: {
                    renderMode: def.renderMode,
                    color: entity.__smartColor || def.color,
                    hollow: def.hollow,
                    fillOpacity: def.fillOpacity,
                    lineOpacity: def.lineOpacity,
                },
            });
        }
        // Convert field instances to components
        for (const field of entity.fieldInstances) {
            const component = this.convertFieldToComponent(field);
            if (component) {
                components.push(component);
            }
        }
        return {
            id: entity.iid,
            name: entity.__identifier,
            type: entity.__identifier,
            x: entity.px[0],
            y: entity.px[1],
            width: entity.width,
            height: entity.height,
            rotation: 0,
            scaleX: 1,
            scaleY: 1,
            visible: true,
            components,
            children: [],
            properties: {
                layer: layerName,
                zIndex,
                tags: entity.__tags,
                pivot: entity.__pivot,
                grid: entity.__grid,
                defUid: entity.defUid,
                worldX: entity.__worldX,
                worldY: entity.__worldY,
                smartColor: entity.__smartColor,
                ...this.convertFieldInstances(entity.fieldInstances),
            },
        };
    }
    convertFieldToComponent(field) {
        // Convert special field types to components
        const type = field.__type;
        if (type === 'Point' || type === 'Array<Point>') {
            return {
                type: 'Points',
                data: {
                    identifier: field.__identifier,
                    points: field.__value,
                },
            };
        }
        if (type.startsWith('EntityRef')) {
            return {
                type: 'EntityReference',
                data: {
                    identifier: field.__identifier,
                    ref: field.__value,
                },
            };
        }
        if (type === 'Tile' || type === 'Array<Tile>') {
            return {
                type: 'TileReference',
                data: {
                    identifier: field.__identifier,
                    tile: field.__tile,
                    value: field.__value,
                },
            };
        }
        // Regular fields don't need a dedicated component
        return null;
    }
    convertFieldInstances(fields) {
        const result = {};
        for (const field of fields) {
            result[field.__identifier] = field.__value;
        }
        return result;
    }
    extractAssets() {
        const assets = [];
        if (!this.project)
            return assets;
        // Extract tilesets
        for (const tileset of this.project.defs.tilesets) {
            if (tileset.relPath) {
                assets.push({
                    id: this.generateId(),
                    name: tileset.identifier,
                    type: 'tileset',
                    path: tileset.relPath,
                    data: {
                        uid: tileset.uid,
                        width: tileset.pxWid,
                        height: tileset.pxHei,
                        tileSize: tileset.tileGridSize,
                        spacing: tileset.spacing,
                        padding: tileset.padding,
                        tags: tileset.tags,
                        enumTags: tileset.enumTags,
                        customData: tileset.customData,
                    },
                });
            }
        }
        // Extract level backgrounds
        for (const level of this.getAllLevels()) {
            if (level.bgRelPath) {
                assets.push({
                    id: this.generateId(),
                    name: `${level.identifier}_bg`,
                    type: 'texture',
                    path: level.bgRelPath,
                    data: {
                        levelId: level.identifier,
                        bgPos: level.bgPos,
                    },
                });
            }
        }
        // Extract enum icons
        for (const enumDef of this.project.defs.enums) {
            if (enumDef.iconTilesetUid) {
                const tileset = this.tilesets.get(enumDef.iconTilesetUid);
                if (tileset === null || tileset === void 0 ? void 0 : tileset.relPath) ;
            }
        }
        return assets;
    }
    // ----------------------------------------
    // Helper Methods
    // ----------------------------------------
    async readFile(path) {
        var _a;
        if (typeof globalThis.process !== 'undefined' && ((_a = globalThis.process.versions) === null || _a === void 0 ? void 0 : _a.node)) {
            const fs = await import('fs');
            return fs.readFileSync(path, 'utf-8');
        }
        else if (typeof fetch !== 'undefined') {
            const response = await fetch(path);
            return response.text();
        }
        throw new Error('Unable to read file in current environment');
    }
    /** Get IntGrid value definition by value */
    getIntGridValue(layerUid, value) {
        var _a;
        const layer = this.layerDefs.get(layerUid);
        return (_a = layer === null || layer === void 0 ? void 0 : layer.intGridValues) === null || _a === void 0 ? void 0 : _a.find((v) => v.value === value);
    }
    /** Get enum definition and values */
    getEnum(identifier) {
        return this.enums.get(identifier);
    }
    /** Get entity definition */
    getEntityDef(uid) {
        return this.entityDefs.get(uid);
    }
    /** Get tileset definition */
    getTileset(uid) {
        return this.tilesets.get(uid);
    }
}

export { LDtkImporter };
//# sourceMappingURL=LDtkImporter.js.map
