'use strict';

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

/**
 * @file Live2DImporter.ts
 * @description Live2D Cubism Model Importer
 * PRO-1.3: Scene Format Interoperability
 *
 * Live2D is a technology for animating 2D characters.
 * Supports Cubism 3.0+ model format.
 *
 * File Types:
 * - *.model3.json - Model settings
 * - *.moc3 - Model data (binary)
 * - *.physics3.json - Physics settings
 * - *.pose3.json - Pose settings
 * - *.exp3.json - Expression settings
 * - *.motion3.json - Motion data
 * - *.cdi3.json - Display info
 */
// ============================================================
// Live2D Importer
// ============================================================
class Live2DImporter extends SceneImporter.SceneImporter {
    constructor() {
        super('live2d');
        this.model = null;
        this.physics = null;
        this.pose = null;
        this.displayInfo = null;
        this.expressions = new Map();
        this.motions = new Map();
    }
    canHandle(filePath) {
        const name = filePath.toLowerCase();
        return (name.endsWith('.model3.json') ||
            name.endsWith('.moc3') ||
            name.endsWith('.physics3.json') ||
            name.endsWith('.pose3.json') ||
            name.endsWith('.exp3.json') ||
            name.endsWith('.motion3.json') ||
            name.endsWith('.cdi3.json'));
    }
    async import(options) {
        var _a;
        const startTime = Date.now();
        this.options = options;
        this.clearMessages();
        const scenes = [];
        const assets = [];
        try {
            const fileName = options.sourcePath.toLowerCase();
            const content = await this.readFile(options.sourcePath);
            if (fileName.endsWith('.model3.json')) {
                this.model = JSON.parse(content);
                // Load dependent files if requested
                if (options.resolveDependencies) {
                    await this.loadDependencies(options.sourcePath);
                }
                // Create scene from model
                const scene = this.convertModelToScene(options.sourcePath);
                scenes.push(scene);
                // Add texture assets
                for (const texture of this.model.FileReferences.Textures) {
                    assets.push({
                        id: this.generateId(),
                        name: this.extractFileName(texture),
                        type: 'texture',
                        path: texture,
                        data: {},
                    });
                }
                // Add moc asset
                assets.push({
                    id: this.generateId(),
                    name: this.extractFileName(this.model.FileReferences.Moc),
                    type: 'data',
                    path: this.model.FileReferences.Moc,
                    data: { type: 'moc3' },
                });
                // Add motion assets
                if (this.model.FileReferences.Motions) {
                    for (const [group, motionRefs] of Object.entries(this.model.FileReferences.Motions)) {
                        for (const motionRef of motionRefs) {
                            assets.push({
                                id: this.generateId(),
                                name: this.extractFileName(motionRef.File),
                                type: 'animation',
                                path: motionRef.File,
                                data: {
                                    group,
                                    fadeInTime: motionRef.FadeInTime,
                                    fadeOutTime: motionRef.FadeOutTime,
                                    sound: motionRef.Sound,
                                },
                            });
                        }
                    }
                }
                // Add expression assets
                if (this.model.FileReferences.Expressions) {
                    for (const expRef of this.model.FileReferences.Expressions) {
                        assets.push({
                            id: this.generateId(),
                            name: expRef.Name,
                            type: 'data',
                            path: expRef.File,
                            data: { type: 'expression' },
                        });
                    }
                }
            }
            else if (fileName.endsWith('.motion3.json')) {
                const motion = JSON.parse(content);
                assets.push({
                    id: this.generateId(),
                    name: this.extractFileName(options.sourcePath),
                    type: 'animation',
                    path: options.sourcePath,
                    data: {
                        duration: motion.Meta.Duration,
                        fps: motion.Meta.Fps,
                        loop: motion.Meta.Loop,
                        curveCount: motion.Meta.CurveCount,
                    },
                });
            }
            else if (fileName.endsWith('.exp3.json')) {
                const expression = JSON.parse(content);
                assets.push({
                    id: this.generateId(),
                    name: this.extractFileName(options.sourcePath),
                    type: 'data',
                    path: options.sourcePath,
                    data: {
                        type: 'expression',
                        fadeInTime: expression.FadeInTime,
                        fadeOutTime: expression.FadeOutTime,
                        parameterCount: ((_a = expression.Parameters) === null || _a === void 0 ? void 0 : _a.length) || 0,
                    },
                });
            }
        }
        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 {
            if (filePath.toLowerCase().endsWith('.model3.json')) {
                this.model = JSON.parse(stringContent);
                return this.convertModelToParsed();
            }
        }
        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 Live2D file`,
                file: sourcePath,
                fatal: true,
            });
        }
        return errors;
    }
    // ----------------------------------------
    // Load Dependencies
    // ----------------------------------------
    async loadDependencies(modelPath) {
        if (!this.model)
            return;
        const baseDir = this.getBaseDirectory(modelPath);
        // Load physics
        if (this.model.FileReferences.Physics) {
            try {
                const physicsContent = await this.readFile(baseDir + this.model.FileReferences.Physics);
                this.physics = JSON.parse(physicsContent);
            }
            catch (_a) {
                this.addWarning('PHYSICS_NOT_FOUND', 'Physics file not found', this.model.FileReferences.Physics);
            }
        }
        // Load pose
        if (this.model.FileReferences.Pose) {
            try {
                const poseContent = await this.readFile(baseDir + this.model.FileReferences.Pose);
                this.pose = JSON.parse(poseContent);
            }
            catch (_b) {
                this.addWarning('POSE_NOT_FOUND', 'Pose file not found', this.model.FileReferences.Pose);
            }
        }
        // Load display info
        if (this.model.FileReferences.DisplayInfo) {
            try {
                const displayContent = await this.readFile(baseDir + this.model.FileReferences.DisplayInfo);
                this.displayInfo = JSON.parse(displayContent);
            }
            catch (_c) {
                this.addWarning('DISPLAY_NOT_FOUND', 'Display info file not found', this.model.FileReferences.DisplayInfo);
            }
        }
        // Load expressions
        if (this.model.FileReferences.Expressions) {
            for (const expRef of this.model.FileReferences.Expressions) {
                try {
                    const expContent = await this.readFile(baseDir + expRef.File);
                    this.expressions.set(expRef.Name, JSON.parse(expContent));
                }
                catch (_d) {
                    this.addWarning('EXPRESSION_NOT_FOUND', `Expression not found: ${expRef.Name}`, expRef.File);
                }
            }
        }
        // Load motions (limited to avoid too many requests)
        if (this.model.FileReferences.Motions) {
            for (const [group, motionRefs] of Object.entries(this.model.FileReferences.Motions)) {
                for (const motionRef of motionRefs.slice(0, 5)) {
                    // Limit to first 5 per group
                    try {
                        const motionContent = await this.readFile(baseDir + motionRef.File);
                        const motionName = `${group}_${this.extractFileName(motionRef.File)}`;
                        this.motions.set(motionName, JSON.parse(motionContent));
                    }
                    catch (_e) {
                        this.addWarning('MOTION_NOT_FOUND', `Motion not found: ${motionRef.File}`, motionRef.File);
                    }
                }
            }
        }
    }
    // ----------------------------------------
    // Model Conversion
    // ----------------------------------------
    convertModelToScene(sourcePath) {
        var _a, _b, _c;
        if (!this.model) {
            throw new Error('No model loaded');
        }
        const name = this.extractFileName(sourcePath).replace('.model3', '');
        const entities = [];
        // Create root entity
        const rootEntity = {
            id: this.generateId(),
            name,
            components: [
                {
                    type: 'Transform',
                    data: {
                        position: { x: 0, y: 0 },
                        rotation: 0,
                        scale: { x: 1, y: 1 },
                    },
                },
                {
                    type: 'Live2DModel',
                    data: {
                        version: this.model.Version,
                        moc: this.model.FileReferences.Moc,
                        textures: this.model.FileReferences.Textures,
                        hasPhysics: !!this.model.FileReferences.Physics,
                        hasPose: !!this.model.FileReferences.Pose,
                        expressionCount: ((_a = this.model.FileReferences.Expressions) === null || _a === void 0 ? void 0 : _a.length) || 0,
                        motionGroups: this.model.FileReferences.Motions
                            ? Object.keys(this.model.FileReferences.Motions)
                            : [],
                    },
                },
            ],
            children: [],
            tags: ['live2d', 'model'],
            active: true,
        };
        // Add hit areas
        if (this.model.HitAreas) {
            for (const hitArea of this.model.HitAreas) {
                rootEntity.components.push({
                    type: 'HitArea',
                    data: {
                        id: hitArea.Id,
                        name: hitArea.Name,
                    },
                });
            }
        }
        // Add parameter groups
        if (this.model.Groups) {
            for (const group of this.model.Groups) {
                rootEntity.components.push({
                    type: 'ParameterGroup',
                    data: {
                        target: group.Target,
                        name: group.Name,
                        ids: group.Ids,
                    },
                });
            }
        }
        // Add physics component
        if (this.physics) {
            rootEntity.components.push({
                type: 'Live2DPhysics',
                data: {
                    settingCount: this.physics.Meta.PhysicsSettingCount,
                    gravity: (_b = this.physics.Meta.EffectiveForces) === null || _b === void 0 ? void 0 : _b.Gravity,
                    wind: (_c = this.physics.Meta.EffectiveForces) === null || _c === void 0 ? void 0 : _c.Wind,
                    settings: this.physics.PhysicsSettings.map((s) => ({
                        id: s.Id,
                        inputCount: s.Input.length,
                        outputCount: s.Output.length,
                        vertexCount: s.Vertices.length,
                    })),
                },
            });
        }
        // Add pose component
        if (this.pose) {
            rootEntity.components.push({
                type: 'Live2DPose',
                data: {
                    fadeInTime: this.pose.FadeInTime,
                    groupCount: this.pose.Groups.length,
                    groups: this.pose.Groups,
                },
            });
        }
        // Add expressions
        for (const [expName, exp] of this.expressions) {
            rootEntity.components.push({
                type: 'Expression',
                data: {
                    name: expName,
                    fadeInTime: exp.FadeInTime,
                    fadeOutTime: exp.FadeOutTime,
                    parameters: exp.Parameters,
                },
            });
        }
        // Add motions
        for (const [motionName, motion] of this.motions) {
            rootEntity.components.push({
                type: 'Animation',
                data: {
                    name: motionName,
                    duration: motion.Meta.Duration,
                    fps: motion.Meta.Fps,
                    loop: motion.Meta.Loop,
                    curveCount: motion.Meta.CurveCount,
                    curves: this.convertMotionCurves(motion),
                },
            });
        }
        entities.push(rootEntity);
        return {
            id: this.generateId(),
            name,
            entities,
            settings: {
                width: 800, // Default canvas size
                height: 800,
                backgroundColor: '#1a1a2e',
            },
        };
    }
    convertModelToParsed() {
        var _a, _b, _c;
        if (!this.model) {
            throw new Error('No model loaded');
        }
        const entities = [];
        // Create model entity
        entities.push({
            id: 'model',
            name: 'Live2D Model',
            type: 'Live2DModel',
            x: 0,
            y: 0,
            width: 800,
            height: 800,
            rotation: 0,
            scaleX: 1,
            scaleY: 1,
            visible: true,
            components: [],
            children: [],
            properties: {
                version: this.model.Version,
                moc: this.model.FileReferences.Moc,
                textures: this.model.FileReferences.Textures,
                physics: this.model.FileReferences.Physics,
                pose: this.model.FileReferences.Pose,
                expressions: (_a = this.model.FileReferences.Expressions) === null || _a === void 0 ? void 0 : _a.map((e) => e.Name),
                motionGroups: this.model.FileReferences.Motions
                    ? Object.keys(this.model.FileReferences.Motions)
                    : [],
                hitAreas: (_b = this.model.HitAreas) === null || _b === void 0 ? void 0 : _b.map((h) => h.Name),
                groups: (_c = this.model.Groups) === null || _c === void 0 ? void 0 : _c.map((g) => g.Name),
            },
        });
        // Add hit area entities
        if (this.model.HitAreas) {
            for (const hitArea of this.model.HitAreas) {
                entities.push({
                    id: hitArea.Id,
                    name: hitArea.Name,
                    type: 'HitArea',
                    x: 0,
                    y: 0,
                    width: 100,
                    height: 100,
                    rotation: 0,
                    scaleX: 1,
                    scaleY: 1,
                    visible: true,
                    components: [],
                    children: [],
                    properties: {
                        meshId: hitArea.Id,
                    },
                });
            }
        }
        return {
            name: 'Live2D Model',
            width: 800,
            height: 800,
            backgroundColor: '#1a1a2e',
            layers: [
                {
                    name: 'Model',
                    type: 'object',
                    visible: true,
                    opacity: 1,
                    offsetX: 0,
                    offsetY: 0,
                    zIndex: 0,
                    properties: {},
                },
            ],
            entities,
            properties: {
                version: this.model.Version,
                textureCount: this.model.FileReferences.Textures.length,
            },
        };
    }
    // ----------------------------------------
    // Motion Conversion
    // ----------------------------------------
    convertMotionCurves(motion) {
        return motion.Curves.map((curve) => ({
            target: curve.Target,
            id: curve.Id,
            fadeInTime: curve.FadeInTime,
            fadeOutTime: curve.FadeOutTime,
            segmentType: this.getSegmentTypes(curve.Segments),
            keyframeCount: this.countKeyframes(curve.Segments),
        }));
    }
    getSegmentTypes(segments) {
        const types = [];
        let i = 0;
        while (i < segments.length) {
            const type = segments[i];
            switch (type) {
                case 0: // Linear
                    types.push('linear');
                    i += 3; // type + point
                    break;
                case 1: // Bezier
                    types.push('bezier');
                    i += 7; // type + 2 control points + point
                    break;
                case 2: // Stepped
                    types.push('stepped');
                    i += 3;
                    break;
                case 3: // Inverse stepped
                    types.push('inverseStep');
                    i += 3;
                    break;
                default:
                    i++;
            }
        }
        return [...new Set(types)];
    }
    countKeyframes(segments) {
        let count = 1; // First keyframe
        let i = 0;
        while (i < segments.length) {
            const type = segments[i];
            switch (type) {
                case 0:
                case 2:
                case 3:
                    count++;
                    i += 3;
                    break;
                case 1:
                    count++;
                    i += 7;
                    break;
                default:
                    i++;
            }
        }
        return count;
    }
    // ----------------------------------------
    // 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');
    }
    getBaseDirectory(path) {
        const lastSlash = Math.max(path.lastIndexOf('/'), path.lastIndexOf('\\'));
        return lastSlash >= 0 ? path.substring(0, lastSlash + 1) : '';
    }
    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 createLive2DImporter() {
    return new Live2DImporter();
}

exports.Live2DImporter = Live2DImporter;
exports.createLive2DImporter = createLive2DImporter;
//# sourceMappingURL=Live2DImporter.js.map
