'use strict';

var SceneImporter = require('./SceneImporter.js');

/**
 * @file SpriterImporter.ts
 * @description Spriter Pro Animation Importer
 * PRO-1.3: Scene Format Interoperability
 *
 * Spriter is a 2D animation tool for games.
 * Supports SCML (XML) and SCON (JSON) formats.
 *
 * File Types:
 * - *.scml - Spriter Character Map Lxml (XML format)
 * - *.scon - Spriter Character Object Notation (JSON format)
 */
// ============================================================
// Spriter Importer
// ============================================================
class SpriterImporter extends SceneImporter.SceneImporter {
    constructor() {
        super('spriter');
        this.project = null;
        this.fileMap = new Map();
        this.folderMap = new Map();
    }
    canHandle(filePath) {
        const ext = filePath.toLowerCase();
        return ext.endsWith('.scml') || ext.endsWith('.scon');
    }
    async import(options) {
        const startTime = Date.now();
        this.options = options;
        this.clearMessages();
        const scenes = [];
        const assets = [];
        try {
            const content = await this.readFile(options.sourcePath);
            const isXml = options.sourcePath.toLowerCase().endsWith('.scml');
            if (isXml) {
                this.project = this.parseScml(content);
            }
            else {
                const parsed = JSON.parse(content);
                this.project = parsed.spriter_data;
            }
            if (!this.project) {
                throw new Error('Failed to parse Spriter project');
            }
            // Build folder/file maps
            this.buildMaps();
            // Add file assets
            for (const folder of this.project.folder) {
                for (const file of folder.file) {
                    assets.push({
                        id: this.generateId(),
                        name: this.extractFileName(file.name),
                        type: file.name.match(/\.(wav|ogg|mp3)$/i) ? 'audio' : 'texture',
                        path: file.name,
                        data: {
                            folder: folder.name || `folder_${folder.id}`,
                            width: file.width,
                            height: file.height,
                            pivotX: file.pivot_x,
                            pivotY: file.pivot_y,
                        },
                    });
                }
            }
            // Convert each entity to a scene
            for (const entity of this.project.entity) {
                const scene = this.convertEntityToScene(entity);
                scenes.push(scene);
            }
        }
        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 isXml = filePath.toLowerCase().endsWith('.scml');
            if (isXml) {
                this.project = this.parseScml(stringContent);
            }
            else {
                const parsed = JSON.parse(stringContent);
                this.project = parsed.spriter_data;
            }
            if (this.project && this.project.entity.length > 0) {
                this.buildMaps();
                return this.convertEntityToParsed(this.project.entity[0]);
            }
        }
        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 Spriter file`,
                file: sourcePath,
                fatal: true,
            });
        }
        return errors;
    }
    // ----------------------------------------
    // SCML (XML) Parser
    // ----------------------------------------
    parseScml(xml) {
        // Simple XML attribute parser
        const getAttrs = (tag) => {
            const attrs = {};
            const attrRegex = /(\w+)="([^"]*)"/g;
            let match;
            while ((match = attrRegex.exec(tag)) !== null) {
                attrs[match[1]] = match[2];
            }
            return attrs;
        };
        // Parse folders
        const folders = [];
        const folderRegex = /<folder\s+([^>]*)>([\s\S]*?)<\/folder>/g;
        let folderMatch;
        while ((folderMatch = folderRegex.exec(xml)) !== null) {
            const folderAttrs = getAttrs(folderMatch[1]);
            const files = [];
            const fileRegex = /<file\s+([^/]*?)\/>/g;
            let fileMatch;
            const folderContent = folderMatch[2];
            while ((fileMatch = fileRegex.exec(folderContent)) !== null) {
                const fileAttrs = getAttrs(fileMatch[1]);
                files.push({
                    id: parseInt(fileAttrs.id) || 0,
                    name: fileAttrs.name || '',
                    width: parseInt(fileAttrs.width) || 0,
                    height: parseInt(fileAttrs.height) || 0,
                    pivot_x: fileAttrs.pivot_x !== undefined ? parseFloat(fileAttrs.pivot_x) : undefined,
                    pivot_y: fileAttrs.pivot_y !== undefined ? parseFloat(fileAttrs.pivot_y) : undefined,
                });
            }
            folders.push({
                id: parseInt(folderAttrs.id) || 0,
                name: folderAttrs.name,
                file: files,
            });
        }
        // Parse entities
        const entities = [];
        const entityRegex = /<entity\s+([^>]*)>([\s\S]*?)<\/entity>/g;
        let entityMatch;
        while ((entityMatch = entityRegex.exec(xml)) !== null) {
            const entityAttrs = getAttrs(entityMatch[1]);
            const entityContent = entityMatch[2];
            // Parse animations
            const animations = [];
            const animRegex = /<animation\s+([^>]*)>([\s\S]*?)<\/animation>/g;
            let animMatch;
            while ((animMatch = animRegex.exec(entityContent)) !== null) {
                const animAttrs = getAttrs(animMatch[1]);
                const animContent = animMatch[2];
                // Parse mainline
                const mainlineMatch = /<mainline>([\s\S]*?)<\/mainline>/.exec(animContent);
                const mainlineKeys = [];
                if (mainlineMatch) {
                    const keyRegex = /<key\s+([^>]*)>([\s\S]*?)<\/key>/g;
                    let keyMatch;
                    while ((keyMatch = keyRegex.exec(mainlineMatch[1])) !== null) {
                        const keyAttrs = getAttrs(keyMatch[1]);
                        const keyContent = keyMatch[2];
                        // Parse bone_ref and object_ref
                        const boneRefs = [];
                        const objRefs = [];
                        const boneRefRegex = /<bone_ref\s+([^/]*?)\/>/g;
                        let boneRefMatch;
                        while ((boneRefMatch = boneRefRegex.exec(keyContent)) !== null) {
                            const refAttrs = getAttrs(boneRefMatch[1]);
                            boneRefs.push({
                                id: parseInt(refAttrs.id) || 0,
                                parent: refAttrs.parent !== undefined ? parseInt(refAttrs.parent) : undefined,
                                timeline: parseInt(refAttrs.timeline) || 0,
                                key: parseInt(refAttrs.key) || 0,
                            });
                        }
                        const objRefRegex = /<object_ref\s+([^/]*?)\/>/g;
                        let objRefMatch;
                        while ((objRefMatch = objRefRegex.exec(keyContent)) !== null) {
                            const refAttrs = getAttrs(objRefMatch[1]);
                            objRefs.push({
                                id: parseInt(refAttrs.id) || 0,
                                parent: refAttrs.parent !== undefined ? parseInt(refAttrs.parent) : undefined,
                                timeline: parseInt(refAttrs.timeline) || 0,
                                key: parseInt(refAttrs.key) || 0,
                                z_index: refAttrs.z_index !== undefined ? parseInt(refAttrs.z_index) : undefined,
                            });
                        }
                        mainlineKeys.push({
                            id: parseInt(keyAttrs.id) || 0,
                            time: keyAttrs.time !== undefined ? parseInt(keyAttrs.time) : undefined,
                            bone_ref: boneRefs.length > 0 ? boneRefs : undefined,
                            object_ref: objRefs.length > 0 ? objRefs : undefined,
                        });
                    }
                }
                // Parse timelines
                const timelines = [];
                const timelineRegex = /<timeline\s+([^>]*)>([\s\S]*?)<\/timeline>/g;
                let timelineMatch;
                while ((timelineMatch = timelineRegex.exec(animContent)) !== null) {
                    const tlAttrs = getAttrs(timelineMatch[1]);
                    const tlContent = timelineMatch[2];
                    const tlKeys = [];
                    const tlKeyRegex = /<key\s+([^>]*)>([\s\S]*?)<\/key>/g;
                    let tlKeyMatch;
                    while ((tlKeyMatch = tlKeyRegex.exec(tlContent)) !== null) {
                        const tlKeyAttrs = getAttrs(tlKeyMatch[1]);
                        const tlKeyContent = tlKeyMatch[2];
                        const key = {
                            id: parseInt(tlKeyAttrs.id) || 0,
                            time: tlKeyAttrs.time !== undefined ? parseInt(tlKeyAttrs.time) : undefined,
                            spin: tlKeyAttrs.spin !== undefined ? parseInt(tlKeyAttrs.spin) : undefined,
                            curve_type: tlKeyAttrs.curve_type,
                        };
                        // Parse bone
                        const boneMatch = /<bone\s+([^/]*?)\/?>/.exec(tlKeyContent);
                        if (boneMatch) {
                            const boneAttrs = getAttrs(boneMatch[1]);
                            key.bone = {
                                x: boneAttrs.x !== undefined ? parseFloat(boneAttrs.x) : undefined,
                                y: boneAttrs.y !== undefined ? parseFloat(boneAttrs.y) : undefined,
                                angle: boneAttrs.angle !== undefined ? parseFloat(boneAttrs.angle) : undefined,
                                scale_x: boneAttrs.scale_x !== undefined ? parseFloat(boneAttrs.scale_x) : undefined,
                                scale_y: boneAttrs.scale_y !== undefined ? parseFloat(boneAttrs.scale_y) : undefined,
                                a: boneAttrs.a !== undefined ? parseFloat(boneAttrs.a) : undefined,
                            };
                        }
                        // Parse object
                        const objectMatch = /<object\s+([^/]*?)\/?>/.exec(tlKeyContent);
                        if (objectMatch) {
                            const objAttrs = getAttrs(objectMatch[1]);
                            key.object = {
                                folder: objAttrs.folder !== undefined ? parseInt(objAttrs.folder) : undefined,
                                file: objAttrs.file !== undefined ? parseInt(objAttrs.file) : undefined,
                                x: objAttrs.x !== undefined ? parseFloat(objAttrs.x) : undefined,
                                y: objAttrs.y !== undefined ? parseFloat(objAttrs.y) : undefined,
                                angle: objAttrs.angle !== undefined ? parseFloat(objAttrs.angle) : undefined,
                                scale_x: objAttrs.scale_x !== undefined ? parseFloat(objAttrs.scale_x) : undefined,
                                scale_y: objAttrs.scale_y !== undefined ? parseFloat(objAttrs.scale_y) : undefined,
                                pivot_x: objAttrs.pivot_x !== undefined ? parseFloat(objAttrs.pivot_x) : undefined,
                                pivot_y: objAttrs.pivot_y !== undefined ? parseFloat(objAttrs.pivot_y) : undefined,
                                a: objAttrs.a !== undefined ? parseFloat(objAttrs.a) : undefined,
                            };
                        }
                        tlKeys.push(key);
                    }
                    timelines.push({
                        id: parseInt(tlAttrs.id) || 0,
                        name: tlAttrs.name || '',
                        object_type: tlAttrs.object_type,
                        obj: tlAttrs.obj !== undefined ? parseInt(tlAttrs.obj) : undefined,
                        key: tlKeys,
                    });
                }
                animations.push({
                    id: parseInt(animAttrs.id) || 0,
                    name: animAttrs.name || '',
                    length: parseInt(animAttrs.length) || 0,
                    interval: animAttrs.interval !== undefined ? parseInt(animAttrs.interval) : undefined,
                    looping: animAttrs.looping !== 'false',
                    mainline: { key: mainlineKeys },
                    timeline: timelines.length > 0 ? timelines : undefined,
                });
            }
            // Parse character maps
            const characterMaps = [];
            const cmRegex = /<character_map\s+([^>]*)>([\s\S]*?)<\/character_map>/g;
            let cmMatch;
            while ((cmMatch = cmRegex.exec(entityContent)) !== null) {
                const cmAttrs = getAttrs(cmMatch[1]);
                const cmContent = cmMatch[2];
                const maps = [];
                const mapRegex = /<map\s+([^/]*?)\/>/g;
                let mapMatch;
                while ((mapMatch = mapRegex.exec(cmContent)) !== null) {
                    const mapAttrs = getAttrs(mapMatch[1]);
                    maps.push({
                        folder: parseInt(mapAttrs.folder),
                        file: parseInt(mapAttrs.file),
                        target_folder: mapAttrs.target_folder !== undefined ? parseInt(mapAttrs.target_folder) : undefined,
                        target_file: mapAttrs.target_file !== undefined ? parseInt(mapAttrs.target_file) : undefined,
                    });
                }
                characterMaps.push({
                    id: parseInt(cmAttrs.id) || 0,
                    name: cmAttrs.name || '',
                    map: maps.length > 0 ? maps : undefined,
                });
            }
            entities.push({
                id: parseInt(entityAttrs.id) || 0,
                name: entityAttrs.name || '',
                animation: animations,
                character_map: characterMaps.length > 0 ? characterMaps : undefined,
            });
        }
        // Get version info
        const spriterDataMatch = /<spriter_data\s+([^>]*)>/;
        const dataMatch = spriterDataMatch.exec(xml);
        const dataAttrs = dataMatch ? getAttrs(dataMatch[1]) : {};
        return {
            scml_version: dataAttrs.scml_version,
            generator: dataAttrs.generator,
            generator_version: dataAttrs.generator_version,
            folder: folders,
            entity: entities,
        };
    }
    // ----------------------------------------
    // Build Maps
    // ----------------------------------------
    buildMaps() {
        this.fileMap.clear();
        this.folderMap.clear();
        if (!this.project)
            return;
        for (const folder of this.project.folder) {
            this.folderMap.set(folder.id, folder);
            for (const file of folder.file) {
                this.fileMap.set(`${folder.id}_${file.id}`, file);
            }
        }
    }
    // ----------------------------------------
    // Entity Conversion
    // ----------------------------------------
    convertEntityToScene(entity) {
        var _a, _b, _c;
        const entities = [];
        // Create root entity
        const rootEntity = {
            id: this.generateId(),
            name: entity.name,
            components: [
                {
                    type: 'Transform',
                    data: {
                        position: { x: 0, y: 0 },
                        rotation: 0,
                        scale: { x: 1, y: 1 },
                    },
                },
                {
                    type: 'SpriterEntity',
                    data: {
                        animationCount: entity.animation.length,
                        characterMapCount: ((_a = entity.character_map) === null || _a === void 0 ? void 0 : _a.length) || 0,
                        hasVariables: !!entity.var_defs && entity.var_defs.length > 0,
                    },
                },
            ],
            children: [],
            tags: ['spriter', 'entity'],
            active: true,
        };
        // Add object info components
        if (entity.obj_info) {
            for (const objInfo of entity.obj_info) {
                rootEntity.components.push({
                    type: objInfo.type === 'bone' ? 'Bone' : 'SpriteObject',
                    data: {
                        name: objInfo.name,
                        type: objInfo.type,
                        width: objInfo.w,
                        height: objInfo.h,
                        pivotX: objInfo.pivot_x,
                        pivotY: objInfo.pivot_y,
                    },
                });
            }
        }
        // Add character maps
        if (entity.character_map) {
            for (const charMap of entity.character_map) {
                rootEntity.components.push({
                    type: 'CharacterMap',
                    data: {
                        name: charMap.name,
                        mappingCount: ((_b = charMap.map) === null || _b === void 0 ? void 0 : _b.length) || 0,
                        mappings: (_c = charMap.map) === null || _c === void 0 ? void 0 : _c.map((m) => ({
                            sourceFolder: m.folder,
                            sourceFile: m.file,
                            targetFolder: m.target_folder,
                            targetFile: m.target_file,
                        })),
                    },
                });
            }
        }
        // Add variables
        if (entity.var_defs) {
            for (const varDef of entity.var_defs) {
                rootEntity.components.push({
                    type: 'Variable',
                    data: {
                        name: varDef.name,
                        type: varDef.type,
                        defaultValue: varDef.default_value,
                    },
                });
            }
        }
        // Add animations
        for (const anim of entity.animation) {
            const animComponent = this.convertAnimation(anim);
            rootEntity.components.push(animComponent);
        }
        entities.push(rootEntity);
        return {
            id: this.generateId(),
            name: entity.name,
            entities,
            settings: {
                width: 800,
                height: 600,
                backgroundColor: '#1a1a2e',
            },
        };
    }
    convertAnimation(anim) {
        var _a, _b;
        const bones = [];
        const sprites = [];
        // Extract bones and sprites from timelines
        if (anim.timeline) {
            for (const timeline of anim.timeline) {
                if (timeline.object_type === 'bone' || ((_a = timeline.key[0]) === null || _a === void 0 ? void 0 : _a.bone)) {
                    bones.push({
                        name: timeline.name,
                        keyframeCount: timeline.key.length,
                        keys: timeline.key.map((k) => {
                            var _a, _b, _c, _d, _e, _f, _g, _h, _j, _k;
                            return ({
                                time: k.time || 0,
                                x: ((_a = k.bone) === null || _a === void 0 ? void 0 : _a.x) || 0,
                                y: ((_b = k.bone) === null || _b === void 0 ? void 0 : _b.y) || 0,
                                angle: ((_c = k.bone) === null || _c === void 0 ? void 0 : _c.angle) || 0,
                                scaleX: (_e = (_d = k.bone) === null || _d === void 0 ? void 0 : _d.scale_x) !== null && _e !== void 0 ? _e : 1,
                                scaleY: (_g = (_f = k.bone) === null || _f === void 0 ? void 0 : _f.scale_y) !== null && _g !== void 0 ? _g : 1,
                                alpha: (_j = (_h = k.bone) === null || _h === void 0 ? void 0 : _h.a) !== null && _j !== void 0 ? _j : 1,
                                spin: (_k = k.spin) !== null && _k !== void 0 ? _k : 1,
                                curveType: k.curve_type || 'linear',
                            });
                        }),
                    });
                }
                else if ((_b = timeline.key[0]) === null || _b === void 0 ? void 0 : _b.object) {
                    sprites.push({
                        name: timeline.name,
                        keyframeCount: timeline.key.length,
                        keys: timeline.key.map((k) => {
                            var _a, _b, _c, _d, _e, _f, _g, _h, _j, _k, _l, _m, _o, _p, _q, _r, _s, _t;
                            const file = ((_a = k.object) === null || _a === void 0 ? void 0 : _a.folder) !== undefined && ((_b = k.object) === null || _b === void 0 ? void 0 : _b.file) !== undefined
                                ? this.fileMap.get(`${k.object.folder}_${k.object.file}`)
                                : null;
                            return {
                                time: k.time || 0,
                                x: ((_c = k.object) === null || _c === void 0 ? void 0 : _c.x) || 0,
                                y: ((_d = k.object) === null || _d === void 0 ? void 0 : _d.y) || 0,
                                angle: ((_e = k.object) === null || _e === void 0 ? void 0 : _e.angle) || 0,
                                scaleX: (_g = (_f = k.object) === null || _f === void 0 ? void 0 : _f.scale_x) !== null && _g !== void 0 ? _g : 1,
                                scaleY: (_j = (_h = k.object) === null || _h === void 0 ? void 0 : _h.scale_y) !== null && _j !== void 0 ? _j : 1,
                                pivotX: (_m = (_l = (_k = k.object) === null || _k === void 0 ? void 0 : _k.pivot_x) !== null && _l !== void 0 ? _l : file === null || file === void 0 ? void 0 : file.pivot_x) !== null && _m !== void 0 ? _m : 0,
                                pivotY: (_q = (_p = (_o = k.object) === null || _o === void 0 ? void 0 : _o.pivot_y) !== null && _p !== void 0 ? _p : file === null || file === void 0 ? void 0 : file.pivot_y) !== null && _q !== void 0 ? _q : 1,
                                alpha: (_s = (_r = k.object) === null || _r === void 0 ? void 0 : _r.a) !== null && _s !== void 0 ? _s : 1,
                                spin: (_t = k.spin) !== null && _t !== void 0 ? _t : 1,
                                curveType: k.curve_type || 'linear',
                                file: file === null || file === void 0 ? void 0 : file.name,
                            };
                        }),
                    });
                }
            }
        }
        // Extract hierarchy from mainline
        const hierarchy = [];
        if (anim.mainline.key.length > 0) {
            const firstKey = anim.mainline.key[0];
            if (firstKey.bone_ref) {
                for (const boneRef of firstKey.bone_ref) {
                    hierarchy.push({
                        type: 'bone',
                        id: boneRef.id,
                        parent: boneRef.parent,
                        timeline: boneRef.timeline,
                    });
                }
            }
            if (firstKey.object_ref) {
                for (const objRef of firstKey.object_ref) {
                    hierarchy.push({
                        type: 'sprite',
                        id: objRef.id,
                        parent: objRef.parent,
                        timeline: objRef.timeline,
                        zIndex: objRef.z_index,
                    });
                }
            }
        }
        // Events
        const events = [];
        if (anim.eventline) {
            for (const eventline of anim.eventline) {
                events.push({
                    name: eventline.name,
                    times: eventline.key.map((k) => k.time || 0),
                });
            }
        }
        // Sounds
        const sounds = [];
        if (anim.soundline) {
            for (const soundline of anim.soundline) {
                sounds.push({
                    name: soundline.name,
                    keys: soundline.key.map((k) => {
                        var _a, _b;
                        const file = this.fileMap.get(`${k.object.folder}_${k.object.file}`);
                        return {
                            time: k.time || 0,
                            file: file === null || file === void 0 ? void 0 : file.name,
                            volume: (_a = k.object.volume) !== null && _a !== void 0 ? _a : 1,
                            panning: (_b = k.object.panning) !== null && _b !== void 0 ? _b : 0,
                        };
                    }),
                });
            }
        }
        return {
            type: 'Animation',
            data: {
                name: anim.name,
                duration: anim.length,
                interval: anim.interval,
                looping: anim.looping !== false,
                fps: 1000 / (anim.interval || 33), // Convert interval to FPS
                boneCount: bones.length,
                spriteCount: sprites.length,
                bones,
                sprites,
                hierarchy,
                events,
                sounds,
                mainlineKeyCount: anim.mainline.key.length,
            },
        };
    }
    convertEntityToParsed(entity) {
        var _a, _b, _c, _d;
        const entities = [];
        // Create model entity
        entities.push({
            id: String(entity.id),
            name: entity.name,
            type: 'SpriterEntity',
            x: 0,
            y: 0,
            width: 800,
            height: 600,
            rotation: 0,
            scaleX: 1,
            scaleY: 1,
            visible: true,
            components: [],
            children: [],
            properties: {
                animationCount: entity.animation.length,
                animations: entity.animation.map((a) => ({
                    name: a.name,
                    duration: a.length,
                    looping: a.looping !== false,
                })),
                characterMaps: (_a = entity.character_map) === null || _a === void 0 ? void 0 : _a.map((cm) => cm.name),
                variables: (_b = entity.var_defs) === null || _b === void 0 ? void 0 : _b.map((v) => ({
                    name: v.name,
                    type: v.type,
                    defaultValue: v.default_value,
                })),
            },
        });
        return {
            name: entity.name,
            width: 800,
            height: 600,
            backgroundColor: '#1a1a2e',
            layers: [
                {
                    name: 'Animation',
                    type: 'object',
                    visible: true,
                    opacity: 1,
                    offsetX: 0,
                    offsetY: 0,
                    zIndex: 0,
                    properties: {},
                },
            ],
            entities,
            properties: {
                generator: (_c = this.project) === null || _c === void 0 ? void 0 : _c.generator,
                generatorVersion: (_d = this.project) === null || _d === void 0 ? void 0 : _d.generator_version,
            },
        };
    }
    // ----------------------------------------
    // Utilities
    // ----------------------------------------
    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');
    }
    extractFileName(path) {
        const name = path.substring(Math.max(path.lastIndexOf('/'), path.lastIndexOf('\\')) + 1);
        const dotIndex = name.lastIndexOf('.');
        return dotIndex > 0 ? name.substring(0, dotIndex) : name;
    }
}
// ============================================================
// Factory Function
// ============================================================
function createSpriterImporter() {
    return new SpriterImporter();
}

exports.SpriterImporter = SpriterImporter;
exports.createSpriterImporter = createSpriterImporter;
//# sourceMappingURL=SpriterImporter.js.map
