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

/**
 * @file GameMakerImporter.ts
 * @description GameMaker Studio 2 Room Importer
 * PRO-1.3: Scene Format Interoperability
 *
 * Supports GameMaker Studio 2 .yy format (JSON-based).
 *
 * File Types:
 * - *.yy - Room, object, sprite, script files
 * - *.yyp - Project file
 */
// ============================================================
// GameMaker Importer
// ============================================================
class GameMakerImporter extends SceneImporter {
    constructor() {
        super('gamemaker');
        this.objects = new Map();
        this.sprites = new Map();
    }
    canHandle(filePath) {
        const ext = filePath.substring(filePath.lastIndexOf('.')).toLowerCase();
        return ext === '.yy' || ext === '.yyp';
    }
    async import(options) {
        var _a;
        const startTime = Date.now();
        this.options = options;
        this.clearMessages();
        const scenes = [];
        const assets = [];
        try {
            const content = await this.readFile(options.sourcePath);
            const data = JSON.parse(content);
            if (this.isRoomFile(data)) {
                const room = data;
                const scene = this.convertRoomToScene(room);
                scenes.push(scene);
                // Extract assets from layers
                this.extractAssetsFromLayers(room.layers, assets);
            }
            else if (this.isSpriteFile(data)) {
                const sprite = data;
                assets.push(this.convertSpriteToAsset(sprite));
            }
            else if (this.isObjectFile(data)) {
                const object = data;
                // Objects become prefabs/templates
                assets.push({
                    id: this.generateId(),
                    name: object.name,
                    type: 'prefab',
                    path: options.sourcePath,
                    data: {
                        spriteId: (_a = object.spriteId) === null || _a === void 0 ? void 0 : _a.name,
                        solid: object.solid,
                        visible: object.visible,
                        persistent: object.persistent,
                        physics: object.physicsObject
                            ? {
                                sensor: object.physicsSensor,
                                shape: object.physicsShape,
                                density: object.physicsDensity,
                                friction: object.physicsFriction,
                                restitution: object.physicsRestitution,
                            }
                            : undefined,
                    },
                });
            }
        }
        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);
            if (this.isRoomFile(data)) {
                return this.convertRoomToParsed(data);
            }
        }
        catch (_a) {
            return null;
        }
        return null;
    }
    async validate(sourcePath) {
        const errors = [];
        if (!this.canHandle(sourcePath)) {
            errors.push({
                code: 'INVALID_FORMAT',
                message: `File ${sourcePath} is not a valid GameMaker file`,
                file: sourcePath,
                fatal: true,
            });
        }
        return errors;
    }
    // ----------------------------------------
    // Type Checks
    // ----------------------------------------
    isRoomFile(data) {
        const obj = data;
        return (obj === null || obj === void 0 ? void 0 : obj.resourceType) === 'GMRoom';
    }
    isSpriteFile(data) {
        const obj = data;
        return (obj === null || obj === void 0 ? void 0 : obj.resourceType) === 'GMSprite';
    }
    isObjectFile(data) {
        const obj = data;
        return (obj === null || obj === void 0 ? void 0 : obj.resourceType) === 'GMObject';
    }
    // ----------------------------------------
    // Room Conversion
    // ----------------------------------------
    convertRoomToScene(room) {
        const entities = [];
        // Convert layers recursively
        for (const layer of room.layers) {
            const layerEntities = this.convertLayer(layer);
            entities.push(...layerEntities);
        }
        return {
            id: this.generateId(),
            name: room.name,
            entities,
            settings: {
                width: room.roomSettings.Width,
                height: room.roomSettings.Height,
                backgroundColor: '#000000',
                persistent: room.roomSettings.persistent,
                physics: room.physicsSettings.PhysicsWorld
                    ? {
                        gravityX: room.physicsSettings.PhysicsWorldGravityX,
                        gravityY: room.physicsSettings.PhysicsWorldGravityY,
                        pixelsPerMeter: room.physicsSettings.PhysicsWorldPixToMetres,
                    }
                    : undefined,
                views: room.viewSettings.enableViews,
            },
        };
    }
    convertRoomToParsed(room) {
        const layers = [];
        const entities = [];
        // Convert layers
        for (const layer of room.layers) {
            const [parsedLayer, layerEntities] = this.convertLayerToParsed(layer);
            if (parsedLayer) {
                layers.push(parsedLayer);
            }
            entities.push(...layerEntities);
        }
        return {
            name: room.name,
            width: room.roomSettings.Width,
            height: room.roomSettings.Height,
            backgroundColor: '#000000',
            layers,
            entities,
            properties: {
                persistent: room.roomSettings.persistent,
                physics: room.physicsSettings.PhysicsWorld,
                views: room.viewSettings.enableViews,
            },
        };
    }
    // ----------------------------------------
    // Layer Conversion
    // ----------------------------------------
    convertLayer(layer) {
        const entities = [];
        switch (layer.resourceType) {
            case 'GMRLayer':
                // Instance layer or folder
                if ('instances' in layer && layer.instances) {
                    for (const instance of layer.instances) {
                        entities.push(this.convertInstance(instance, layer.depth));
                    }
                }
                // Process sublayers
                if (layer.layers) {
                    for (const sublayer of layer.layers) {
                        entities.push(...this.convertLayer(sublayer));
                    }
                }
                break;
            case 'GMRTileLayer':
                entities.push(this.convertTileLayer(layer));
                break;
            case 'GMRBackgroundLayer':
                entities.push(this.convertBackgroundLayer(layer));
                break;
            case 'GMRAssetLayer':
                for (const asset of layer.assets) {
                    entities.push(this.convertAsset(asset, layer.depth));
                }
                break;
        }
        return entities;
    }
    convertLayerToParsed(layer) {
        var _a, _b;
        const entities = [];
        let parsedLayer = null;
        switch (layer.resourceType) {
            case 'GMRTileLayer': {
                const tileLayer = layer;
                parsedLayer = {
                    name: layer.name,
                    type: 'tile',
                    visible: layer.visible,
                    opacity: 1,
                    offsetX: layer.x,
                    offsetY: layer.y,
                    zIndex: -layer.depth, // GM uses depth (negative = front)
                    tileData: tileLayer.tiles
                        ? {
                            width: tileLayer.tiles.SerialiseWidth,
                            height: tileLayer.tiles.SerialiseHeight,
                            tiles: tileLayer.tiles.TileSerialiseData || this.decompressTileData(tileLayer.tiles),
                            tileWidth: 32, // Default, would need tileset info
                            tileHeight: 32,
                        }
                        : undefined,
                    properties: {
                        tileset: (_a = tileLayer.tilesetId) === null || _a === void 0 ? void 0 : _a.name,
                    },
                };
                break;
            }
            case 'GMRLayer':
                if ('instances' in layer && layer.instances) {
                    for (const instance of layer.instances) {
                        entities.push(this.convertInstanceToParsed(instance, layer.depth));
                    }
                }
                parsedLayer = {
                    name: layer.name,
                    type: 'object',
                    visible: layer.visible,
                    opacity: 1,
                    offsetX: 0,
                    offsetY: 0,
                    zIndex: -layer.depth,
                    properties: {},
                };
                break;
            case 'GMRBackgroundLayer': {
                const bgLayer = layer;
                parsedLayer = {
                    name: layer.name,
                    type: 'image',
                    visible: layer.visible,
                    opacity: 1,
                    offsetX: layer.x,
                    offsetY: layer.y,
                    zIndex: -layer.depth,
                    properties: {
                        sprite: (_b = bgLayer.spriteId) === null || _b === void 0 ? void 0 : _b.name,
                        htiled: bgLayer.htiled,
                        vtiled: bgLayer.vtiled,
                        stretch: bgLayer.stretch,
                    },
                };
                break;
            }
        }
        return [parsedLayer, entities];
    }
    // ----------------------------------------
    // Instance Conversion
    // ----------------------------------------
    convertInstance(instance, depth) {
        const components = [
            {
                type: 'Transform',
                data: {
                    position: { x: instance.x, y: instance.y },
                    rotation: instance.rotation,
                    scale: { x: instance.scaleX, y: instance.scaleY },
                },
            },
            {
                type: 'Sprite',
                data: {
                    imageIndex: instance.imageIndex,
                    imageSpeed: instance.imageSpeed,
                    color: this.colorToHex(instance.colour),
                    depth,
                },
            },
        ];
        // Add properties
        if (instance.properties.length > 0) {
            const props = {};
            for (const prop of instance.properties) {
                props[prop.propertyId.name] = prop.value;
            }
            components.push({
                type: 'Properties',
                data: props,
            });
        }
        return {
            id: instance.name,
            name: instance.objectId.name,
            components,
            children: [],
            tags: ['instance', ...(instance.tags || [])],
            active: !instance.ignore,
        };
    }
    convertInstanceToParsed(instance, depth) {
        return {
            id: instance.name,
            name: instance.objectId.name,
            type: instance.objectId.name,
            x: instance.x,
            y: instance.y,
            width: 32, // Would need sprite info
            height: 32,
            rotation: instance.rotation,
            scaleX: instance.scaleX,
            scaleY: instance.scaleY,
            visible: !instance.ignore,
            components: [],
            children: [],
            properties: {
                objectId: instance.objectId.path,
                imageIndex: instance.imageIndex,
                imageSpeed: instance.imageSpeed,
                color: this.colorToHex(instance.colour),
                depth,
                tags: instance.tags,
            },
        };
    }
    // ----------------------------------------
    // Tile Layer Conversion
    // ----------------------------------------
    convertTileLayer(layer) {
        var _a;
        const tileData = layer.tiles.TileSerialiseData || this.decompressTileData(layer.tiles);
        return {
            id: this.generateId(),
            name: layer.name,
            components: [
                {
                    type: 'Transform',
                    data: {
                        position: { x: layer.x, y: layer.y },
                        rotation: 0,
                        scale: { x: 1, y: 1 },
                    },
                },
                {
                    type: 'Tilemap',
                    data: {
                        tileset: (_a = layer.tilesetId) === null || _a === void 0 ? void 0 : _a.name,
                        width: layer.tiles.SerialiseWidth,
                        height: layer.tiles.SerialiseHeight,
                        data: tileData,
                        depth: layer.depth,
                    },
                },
            ],
            children: [],
            tags: ['tilemap', layer.name],
            active: layer.visible,
        };
    }
    decompressTileData(tiles) {
        if (tiles.TileSerialiseData) {
            return tiles.TileSerialiseData;
        }
        // RLE decompression for compressed data
        if (tiles.TileCompressedData && tiles.TileDataFormat === 1) {
            const result = [];
            const data = tiles.TileCompressedData;
            for (let i = 0; i < data.length; i += 2) {
                const count = data[i];
                const value = data[i + 1];
                for (let j = 0; j < count; j++) {
                    result.push(value);
                }
            }
            return result;
        }
        return [];
    }
    // ----------------------------------------
    // Background Layer Conversion
    // ----------------------------------------
    convertBackgroundLayer(layer) {
        var _a;
        return {
            id: this.generateId(),
            name: layer.name,
            components: [
                {
                    type: 'Transform',
                    data: {
                        position: { x: layer.x, y: layer.y },
                        rotation: 0,
                        scale: { x: 1, y: 1 },
                    },
                },
                {
                    type: 'Background',
                    data: {
                        sprite: (_a = layer.spriteId) === null || _a === void 0 ? void 0 : _a.name,
                        color: this.colorToHex(layer.colour),
                        stretch: layer.stretch,
                        htiled: layer.htiled,
                        vtiled: layer.vtiled,
                        animationFPS: layer.animationFPS,
                        depth: layer.depth,
                        hspeed: layer.hspeed,
                        vspeed: layer.vspeed,
                    },
                },
            ],
            children: [],
            tags: ['background'],
            active: layer.visible,
        };
    }
    // ----------------------------------------
    // Asset Conversion
    // ----------------------------------------
    convertAsset(asset, depth) {
        var _a, _b;
        return {
            id: this.generateId(),
            name: asset.name,
            components: [
                {
                    type: 'Transform',
                    data: {
                        position: { x: asset.x, y: asset.y },
                        rotation: asset.rotation,
                        scale: { x: asset.scaleX, y: asset.scaleY },
                    },
                },
                {
                    type: 'Sprite',
                    data: {
                        texture: ((_a = asset.spriteId) === null || _a === void 0 ? void 0 : _a.name) || ((_b = asset.sequenceId) === null || _b === void 0 ? void 0 : _b.name),
                        color: this.colorToHex(asset.colour),
                        animationSpeed: asset.animationSpeed,
                        depth,
                    },
                },
            ],
            children: [],
            tags: ['asset', asset.resourceType],
            active: !asset.ignore,
        };
    }
    convertSpriteToAsset(sprite) {
        return {
            id: this.generateId(),
            name: sprite.name,
            type: 'texture',
            path: `sprites/${sprite.name}/${sprite.name}.png`,
            data: {
                width: sprite.width,
                height: sprite.height,
                origin: { x: sprite.xorigin, y: sprite.yorigin },
                bbox: {
                    left: sprite.bbox_left,
                    top: sprite.bbox_top,
                    right: sprite.bbox_right,
                    bottom: sprite.bbox_bottom,
                },
                frameCount: sprite.frames.length,
                collisionKind: sprite.collisionKind,
                nineSlice: sprite.nineSlice,
            },
        };
    }
    extractAssetsFromLayers(layers, assets) {
        for (const layer of layers) {
            if (layer.resourceType === 'GMRTileLayer') {
                const tileLayer = layer;
                if (tileLayer.tilesetId) {
                    assets.push({
                        id: this.generateId(),
                        name: tileLayer.tilesetId.name,
                        type: 'tileset',
                        path: tileLayer.tilesetId.path,
                        data: {},
                    });
                }
            }
            else if (layer.resourceType === 'GMRBackgroundLayer') {
                const bgLayer = layer;
                if (bgLayer.spriteId) {
                    assets.push({
                        id: this.generateId(),
                        name: bgLayer.spriteId.name,
                        type: 'texture',
                        path: bgLayer.spriteId.path,
                        data: {},
                    });
                }
            }
            // Process sublayers
            if (layer.layers) {
                this.extractAssetsFromLayers(layer.layers, assets);
            }
        }
    }
    // ----------------------------------------
    // Utilities
    // ----------------------------------------
    colorToHex(color) {
        // GM uses ARGB format
        const a = (color >> 24) & 0xff;
        const r = (color >> 16) & 0xff;
        const g = (color >> 8) & 0xff;
        const b = color & 0xff;
        if (a < 255) {
            return `rgba(${r}, ${g}, ${b}, ${a / 255})`;
        }
        return `#${r.toString(16).padStart(2, '0')}${g.toString(16).padStart(2, '0')}${b.toString(16).padStart(2, '0')}`;
    }
    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');
    }
}
// ============================================================
// Factory Function
// ============================================================
function createGameMakerImporter() {
    return new GameMakerImporter();
}

export { GameMakerImporter, createGameMakerImporter };
//# sourceMappingURL=GameMakerImporter.js.map
