'use strict';

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

/**
 * @file UnitySceneImporter.ts
 * @description Import Unity scenes (.unity) and prefabs (.prefab)
 * PRO-1.3: Scene Format Interoperability
 *
 * Parses Unity's YAML serialization format and converts to Nice2Dev entities.
 * Supports:
 * - Scene files (.unity)
 * - Prefab files (.prefab)
 * - Asset files (.asset)
 * - Animation clips (.anim)
 * - Materials (.mat)
 */
// ============================================================
// Unity Class IDs
// ============================================================
const UNITY_CLASS_IDS = {
    1: 'GameObject',
    2: 'Component',
    3: 'LevelGameManager',
    4: 'Transform',
    20: 'Camera',
    21: 'Material',
    23: 'MeshRenderer',
    25: 'Renderer',
    28: 'Texture2D',
    33: 'MeshFilter',
    43: 'Mesh',
    47: 'RenderTexture',
    48: 'Shader',
    49: 'TextAsset',
    50: 'Rigidbody2D',
    54: 'Rigidbody',
    56: 'Cloth',
    58: 'CircleCollider2D',
    60: 'PolygonCollider2D',
    61: 'BoxCollider2D',
    64: 'MeshCollider',
    65: 'BoxCollider',
    66: 'CompositeCollider2D',
    68: 'EdgeCollider2D',
    70: 'CapsuleCollider2D',
    72: 'ComputeShader',
    74: 'AnimationClip',
    76: 'ConstantForce',
    78: 'WorldParticleCollider',
    81: 'AudioListener',
    82: 'AudioSource',
    83: 'AudioClip',
    84: 'RenderTexture',
    86: 'MovieTexture',
    89: 'Cubemap',
    90: 'Avatar',
    91: 'AnimatorController',
    92: 'GUILayer',
    93: 'RuntimeAnimatorController',
    94: 'ScriptMapper',
    95: 'Animator',
    96: 'TrailRenderer',
    102: 'TextMesh',
    104: 'RenderSettings',
    108: 'Light',
    109: 'CGProgram',
    110: 'BaseAnimationTrack',
    114: 'MonoBehaviour',
    115: 'MonoScript',
    118: 'Texture3D',
    119: 'Projector',
    120: 'LineRenderer',
    121: 'Flare',
    122: 'Halo',
    123: 'LensFlare',
    124: 'FlareLayer',
    125: 'HaloLayer',
    126: 'NavMeshAgent',
    128: 'Font',
    129: 'NavMeshSettings',
    130: 'ParticleSystem',
    131: 'ParticleSystemRenderer',
    132: 'ShaderVariantCollection',
    133: 'TilemapCollider2D',
    134: 'SpriteMask',
    135: 'CapsuleCollider',
    136: 'SkinnedMeshRenderer',
    137: 'FixedJoint',
    140: 'RaycastCollider',
    145: 'PhysicMaterial',
    146: 'SphereCollider',
    150: 'CharacterController',
    152: 'CharacterJoint',
    153: 'SpringJoint',
    154: 'WheelCollider',
    156: 'HingeJoint',
    157: 'ConfigurableJoint',
    158: 'Grid',
    160: 'Tilemap',
    161: 'TilemapRenderer',
    180: 'BoxCollider2D',
    181: 'CircleCollider2D',
    182: 'EdgeCollider2D',
    183: 'PolygonCollider2D',
    184: 'CapsuleCollider2D',
    185: 'CompositeCollider2D',
    191: 'Rigidbody2D',
    192: 'DistanceJoint2D',
    193: 'HingeJoint2D',
    194: 'SliderJoint2D',
    195: 'SpringJoint2D',
    196: 'WheelJoint2D',
    197: 'RelativeJoint2D',
    198: 'FixedJoint2D',
    199: 'FrictionJoint2D',
    200: 'TargetJoint2D',
    212: 'SpriteRenderer',
    213: 'Sprite',
    215: 'TerrainCollider',
    218: 'TerrainData',
    219: 'LightmapSettings',
    220: 'WebCamTexture',
    221: 'LODGroup',
    223: 'Canvas',
    224: 'RectTransform',
    225: 'CanvasGroup',
    226: 'BillboardRenderer',
    227: 'BillboardAsset',
    228: 'SpeedTreeWindAsset',
    229: 'AnchoredJoint2D',
    230: 'Joint2D',
    231: 'SpringJoint2D',
    232: 'DistanceJoint2D',
    233: 'HingeJoint2D',
    234: 'SliderJoint2D',
    235: 'WheelJoint2D',
    238: 'NavMeshData',
    240: 'AudioMixer',
    241: 'AudioMixerGroup',
    243: 'AudioMixerSnapshot',
    244: 'PhysicsUpdateBehaviour2D',
    245: 'ConstantForce2D',
    246: 'Effector2D',
    247: 'AreaEffector2D',
    248: 'PointEffector2D',
    249: 'PlatformEffector2D',
    250: 'SurfaceEffector2D',
    251: 'BuoyancyEffector2D',
    252: 'RelativeJoint2D',
    253: 'FixedJoint2D',
    254: 'FrictionJoint2D',
    255: 'TargetJoint2D',
    258: 'LightProbeProxyVolume',
    259: 'LightProbes',
    260: 'LightProbeGroup',
    271: 'VideoPlayer',
    290: 'SortingGroup',
    310: 'SpriteMask',
    330: 'VideoClip',
    331: 'TilemapCollider2D',
    363: 'OcclusionCullingData',
    387: 'SpringBoneJob',
    388: 'SpringBoneColliderSphere',
    391: 'SpringBoneColliderCapsule',
    392: 'SpringBoneColliderPlane',
    1001: 'Prefab',
};
// ============================================================
// Unity Scene Importer
// ============================================================
class UnitySceneImporter extends SceneImporter.SceneImporter {
    constructor() {
        super('unity');
        this.documents = new Map();
        this.gameObjects = new Map();
        this.transforms = new Map();
        this.components = new Map();
        this.prefabs = new Map();
    }
    async import(options) {
        const startTime = Date.now();
        this.options = options;
        this.clearMessages();
        this.documents.clear();
        this.gameObjects.clear();
        this.transforms.clear();
        this.components.clear();
        const errors = await this.validate(options.sourcePath);
        if (errors.some((e) => e.fatal)) {
            return {
                ...this.createEmptyResult(),
                errors: errors,
            };
        }
        const scenes = [];
        const assets = [];
        try {
            // Read and parse the Unity file(s)
            const content = await this.readFile(options.sourcePath);
            const parsed = this.parseUnityFile(content, options.sourcePath);
            if (parsed) {
                // Convert to Nice2Dev scene
                const niceScene = this.convertParsedToNiceScene(parsed);
                scenes.push(niceScene);
                // Extract assets
                const extractedAssets = this.extractAssets(parsed);
                assets.push(...extractedAssets);
            }
        }
        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);
        return this.parseUnityFile(stringContent, filePath);
    }
    async validate(sourcePath) {
        const errors = [];
        if (!this.canHandle(sourcePath)) {
            errors.push({
                code: 'INVALID_FORMAT',
                message: `File ${sourcePath} is not a valid Unity file`,
                file: sourcePath,
                fatal: true,
            });
        }
        return errors;
    }
    // ----------------------------------------
    // Unity Parsing
    // ----------------------------------------
    parseUnityFile(content, filePath) {
        // Unity files are multi-document YAML with special header
        const documents = this.parseUnityYAML(content);
        // Store all documents for reference resolution
        for (const doc of documents) {
            this.documents.set(doc.fileID, doc);
            // Categorize by class type
            const className = UNITY_CLASS_IDS[doc.classID];
            if (className === 'GameObject') {
                const go = Object.values(doc.data)[0];
                this.gameObjects.set(doc.fileID, go);
            }
            else if (className === 'Transform' || className === 'RectTransform') {
                const transform = Object.values(doc.data)[0];
                this.transforms.set(doc.fileID, { ...transform, fileID: doc.fileID });
            }
            else {
                // Components
                const component = Object.values(doc.data)[0];
                this.components.set(doc.fileID, { ...component, classID: doc.classID, fileID: doc.fileID });
            }
        }
        // Build scene structure
        const scene = this.buildSceneFromDocuments(filePath);
        return scene;
    }
    parseUnityYAML(content) {
        const documents = [];
        // Split by Unity document headers: --- !u!<classID> &<fileID>
        const docRegex = /--- !u!(\d+) &(\d+)/g;
        const matches = [...content.matchAll(docRegex)];
        for (let i = 0; i < matches.length; i++) {
            const match = matches[i];
            const classID = parseInt(match[1], 10);
            const fileID = match[2];
            // Get content until next document or end
            const startIndex = match.index + match[0].length;
            const endIndex = i < matches.length - 1 ? matches[i + 1].index : content.length;
            const docContent = content.substring(startIndex, endIndex).trim();
            // Parse YAML content
            try {
                const data = SceneImporter.parseYAML(docContent);
                documents.push({ fileID, classID, data });
            }
            catch (error) {
                this.addWarning('YAML_PARSE_ERROR', `Failed to parse YAML for document ${fileID}: ${error}`, undefined);
            }
        }
        return documents;
    }
    buildSceneFromDocuments(filePath) {
        const sceneName = filePath.substring(filePath.lastIndexOf('/') + 1).replace(/\.\w+$/, '');
        // Find root transforms (no parent)
        const rootTransforms = [];
        for (const [fileID, transform] of this.transforms) {
            if (!transform.m_Father || transform.m_Father.fileID === 0) {
                rootTransforms.push(transform);
            }
        }
        // Sort by root order
        rootTransforms.sort((a, b) => (a.m_RootOrder || 0) - (b.m_RootOrder || 0));
        // Build entity tree from root transforms
        const entities = [];
        for (const transform of rootTransforms) {
            const entity = this.buildEntityFromTransform(transform);
            if (entity) {
                entities.push(entity);
            }
        }
        // Extract scene settings
        const sceneSettings = this.extractSceneSettings();
        return {
            name: sceneName,
            width: 1920, // Unity doesn't have fixed scene size
            height: 1080,
            backgroundColor: '#3C3C3C', // Unity default
            layers: [],
            entities,
            properties: sceneSettings,
        };
    }
    buildEntityFromTransform(transform) {
        // Find the GameObject that owns this transform
        let gameObjectFileID = null;
        for (const [compFileID, component] of this.components) {
            if (compFileID === transform.fileID) {
                const gameObjectRef = component
                    .m_GameObject;
                if (gameObjectRef) {
                    gameObjectFileID = gameObjectRef.fileID.toString();
                }
                break;
            }
        }
        // Also check transforms map for m_GameObject reference
        for (const doc of this.documents.values()) {
            if (doc.fileID === transform.fileID) {
                const transformData = Object.values(doc.data)[0];
                if (transformData === null || transformData === void 0 ? void 0 : transformData.m_GameObject) {
                    gameObjectFileID = transformData.m_GameObject.fileID.toString();
                }
                break;
            }
        }
        const gameObject = gameObjectFileID ? this.gameObjects.get(gameObjectFileID) : null;
        // Convert quaternion to euler angles (simplified)
        const rotation = this.quaternionToEulerY(transform.m_LocalRotation.x, transform.m_LocalRotation.y, transform.m_LocalRotation.z, transform.m_LocalRotation.w);
        // Get components attached to this GameObject
        const components = this.getComponentsForGameObject(gameObjectFileID);
        // Get children
        const children = [];
        if (transform.m_Children) {
            for (const childRef of transform.m_Children) {
                const childTransform = this.transforms.get(childRef.fileID.toString());
                if (childTransform) {
                    const childEntity = this.buildEntityFromTransform(childTransform);
                    if (childEntity) {
                        children.push(childEntity);
                    }
                }
            }
        }
        return {
            id: transform.fileID,
            name: (gameObject === null || gameObject === void 0 ? void 0 : gameObject.m_Name) || 'GameObject',
            type: (gameObject === null || gameObject === void 0 ? void 0 : gameObject.m_TagString) || 'Untagged',
            x: transform.m_LocalPosition.x,
            y: transform.m_LocalPosition.y,
            width: 1,
            height: 1,
            rotation: rotation,
            scaleX: transform.m_LocalScale.x,
            scaleY: transform.m_LocalScale.y,
            visible: (gameObject === null || gameObject === void 0 ? void 0 : gameObject.m_IsActive) !== 0,
            components,
            children,
            properties: {
                layer: (gameObject === null || gameObject === void 0 ? void 0 : gameObject.m_Layer) || 0,
                tag: (gameObject === null || gameObject === void 0 ? void 0 : gameObject.m_TagString) || 'Untagged',
                z: transform.m_LocalPosition.z,
                scaleZ: transform.m_LocalScale.z,
                staticFlags: (gameObject === null || gameObject === void 0 ? void 0 : gameObject.m_StaticEditorFlags) || 0,
            },
        };
    }
    getComponentsForGameObject(gameObjectFileID) {
        if (!gameObjectFileID)
            return [];
        const result = [];
        for (const [fileID, component] of this.components) {
            const goRef = component.m_GameObject;
            if (goRef && goRef.fileID.toString() === gameObjectFileID) {
                const converted = this.convertUnityComponent(component);
                if (converted) {
                    result.push(converted);
                }
            }
        }
        return result;
    }
    convertUnityComponent(component) {
        const className = UNITY_CLASS_IDS[component.classID];
        switch (className) {
            case 'Transform':
            case 'RectTransform':
                return null; // Already handled
            case 'SpriteRenderer':
                return {
                    type: 'SpriteRenderer',
                    data: {
                        sprite: component.m_Sprite,
                        color: component.m_Color,
                        flipX: component.m_FlipX,
                        flipY: component.m_FlipY,
                        sortingLayerID: component.m_SortingLayerID,
                        sortingOrder: component.m_SortingOrder,
                        enabled: component.m_Enabled !== 0,
                    },
                };
            case 'Rigidbody2D':
                return {
                    type: 'Rigidbody2D',
                    data: {
                        bodyType: component.m_BodyType,
                        simulated: component.m_Simulated,
                        useFullKinematicContacts: component
                            .m_UseFullKinematicContacts,
                        mass: component.m_Mass,
                        linearDrag: component.m_LinearDrag,
                        angularDrag: component.m_AngularDrag,
                        gravityScale: component.m_GravityScale,
                        constraints: component.m_Constraints,
                        enabled: component.m_Enabled !== 0,
                    },
                };
            case 'BoxCollider2D':
                return {
                    type: 'BoxCollider2D',
                    data: {
                        isTrigger: component.m_IsTrigger,
                        size: component.m_Size,
                        offset: component.m_Offset,
                        enabled: component.m_Enabled !== 0,
                    },
                };
            case 'CircleCollider2D':
                return {
                    type: 'CircleCollider2D',
                    data: {
                        isTrigger: component.m_IsTrigger,
                        radius: component.m_Radius,
                        offset: component.m_Offset,
                        enabled: component.m_Enabled !== 0,
                    },
                };
            case 'PolygonCollider2D':
                return {
                    type: 'PolygonCollider2D',
                    data: {
                        isTrigger: component.m_IsTrigger,
                        points: component.m_Points,
                        offset: component.m_Offset,
                        enabled: component.m_Enabled !== 0,
                    },
                };
            case 'EdgeCollider2D':
                return {
                    type: 'EdgeCollider2D',
                    data: {
                        isTrigger: component.m_IsTrigger,
                        points: component.m_Points,
                        offset: component.m_Offset,
                        enabled: component.m_Enabled !== 0,
                    },
                };
            case 'AudioSource':
                return {
                    type: 'AudioSource',
                    data: {
                        clip: component.m_audioClip,
                        playOnAwake: component.m_PlayOnAwake,
                        loop: component.Loop,
                        volume: component.m_Volume,
                        pitch: component.m_Pitch,
                        spatialBlend: component.SpatialBlend,
                        enabled: component.m_Enabled !== 0,
                    },
                };
            case 'Animator':
                return {
                    type: 'Animator',
                    data: {
                        controller: component.m_Controller,
                        avatar: component.m_Avatar,
                        applyRootMotion: component.m_ApplyRootMotion,
                        updateMode: component.m_UpdateMode,
                        cullingMode: component.m_CullingMode,
                        enabled: component.m_Enabled !== 0,
                    },
                };
            case 'Camera':
                return {
                    type: 'Camera',
                    data: {
                        clearFlags: component.m_ClearFlags,
                        backgroundColor: component.m_BackGroundColor,
                        orthographic: component.m_Orthographic,
                        orthographicSize: component.m_OrthographicSize,
                        fieldOfView: component.m_FieldOfView,
                        nearClipPlane: component.m_NearClipPlane,
                        farClipPlane: component.m_FarClipPlane,
                        depth: component.m_Depth,
                        cullingMask: component.m_CullingMask,
                        enabled: component.m_Enabled !== 0,
                    },
                };
            case 'Light':
                return {
                    type: 'Light',
                    data: {
                        type: component.m_Type,
                        color: component.m_Color,
                        intensity: component.m_Intensity,
                        range: component.m_Range,
                        spotAngle: component.m_SpotAngle,
                        shadows: component.m_Shadows,
                        enabled: component.m_Enabled !== 0,
                    },
                };
            case 'ParticleSystem':
                return {
                    type: 'ParticleSystem',
                    data: {
                        duration: component.duration,
                        looping: component.looping,
                        startLifetime: component.startLifetime,
                        startSpeed: component.startSpeed,
                        startSize: component.startSize,
                        startColor: component.startColor,
                        gravityModifier: component.gravityModifier,
                        maxParticles: component.maxParticles,
                        enabled: component.m_Enabled !== 0,
                    },
                };
            case 'Canvas':
                return {
                    type: 'Canvas',
                    data: {
                        renderMode: component.m_RenderMode,
                        sortingOrder: component.m_SortingOrder,
                        targetDisplay: component.m_TargetDisplay,
                        pixelPerfect: component.m_PixelPerfect,
                        enabled: component.m_Enabled !== 0,
                    },
                };
            case 'Tilemap':
                return {
                    type: 'Tilemap',
                    data: {
                        size: component.m_Size,
                        cellSize: component.m_CellSize,
                        cellGap: component.m_CellGap,
                        cellLayout: component.m_CellLayout,
                        tileAnchor: component.m_TileAnchor,
                        orientation: component.m_Orientation,
                        enabled: component.m_Enabled !== 0,
                    },
                };
            case 'Grid':
                return {
                    type: 'Grid',
                    data: {
                        cellSize: component.m_CellSize,
                        cellGap: component.m_CellGap,
                        cellLayout: component.m_CellLayout,
                        cellSwizzle: component.m_CellSwizzle,
                        enabled: component.m_Enabled !== 0,
                    },
                };
            case 'MonoBehaviour':
                return {
                    type: 'Script',
                    data: {
                        scriptGuid: component.m_Script,
                        properties: { ...component },
                        enabled: component.m_Enabled !== 0,
                    },
                };
            default:
                // Generic component
                return {
                    type: className || 'Unknown',
                    data: {
                        classID: component.classID,
                        properties: { ...component },
                        enabled: component.m_Enabled !== 0,
                    },
                };
        }
    }
    extractSceneSettings() {
        const settings = {};
        // Find physics settings
        for (const doc of this.documents.values()) {
            const data = Object.values(doc.data)[0];
            if (data === null || data === void 0 ? void 0 : data.m_Physics2DSettings) {
                settings.gravity = {
                    x: data.m_Physics2DSettings.m_Gravity.x,
                    y: data.m_Physics2DSettings.m_Gravity.y,
                };
            }
            if (data === null || data === void 0 ? void 0 : data.m_TagManager) {
                settings.tags = data.m_TagManager.tags;
                settings.layers = data.m_TagManager.layers;
                settings.sortingLayers = data.m_TagManager.sortingLayers;
            }
        }
        return settings;
    }
    extractAssets(scene) {
        const assets = [];
        // Extract sprite references
        const spriteRefs = this.extractSpriteReferences();
        for (const ref of spriteRefs) {
            assets.push({
                id: this.generateId(),
                name: ref.name,
                type: 'texture',
                path: ref.path,
                data: { guid: ref.guid },
            });
        }
        // Extract audio references
        const audioRefs = this.extractAudioReferences();
        for (const ref of audioRefs) {
            assets.push({
                id: this.generateId(),
                name: ref.name,
                type: 'audio',
                path: ref.path,
                data: { guid: ref.guid },
            });
        }
        // Extract animation references
        const animRefs = this.extractAnimationReferences();
        for (const ref of animRefs) {
            assets.push({
                id: this.generateId(),
                name: ref.name,
                type: 'animation',
                path: ref.path,
                data: { guid: ref.guid },
            });
        }
        return assets;
    }
    extractSpriteReferences() {
        const refs = [];
        for (const component of this.components.values()) {
            const sprite = component.m_Sprite;
            if (sprite === null || sprite === void 0 ? void 0 : sprite.guid) {
                refs.push({
                    name: `sprite_${sprite.guid.substring(0, 8)}`,
                    path: `Assets/Sprites/${sprite.guid}`,
                    guid: sprite.guid,
                });
            }
        }
        return refs;
    }
    extractAudioReferences() {
        const refs = [];
        for (const component of this.components.values()) {
            const clip = component.m_audioClip;
            if (clip === null || clip === void 0 ? void 0 : clip.guid) {
                refs.push({
                    name: `audio_${clip.guid.substring(0, 8)}`,
                    path: `Assets/Audio/${clip.guid}`,
                    guid: clip.guid,
                });
            }
        }
        return refs;
    }
    extractAnimationReferences() {
        const refs = [];
        for (const component of this.components.values()) {
            const controller = component.m_Controller;
            if (controller === null || controller === void 0 ? void 0 : controller.guid) {
                refs.push({
                    name: `animation_${controller.guid.substring(0, 8)}`,
                    path: `Assets/Animations/${controller.guid}`,
                    guid: controller.guid,
                });
            }
        }
        return refs;
    }
    // ----------------------------------------
    // Helper Methods
    // ----------------------------------------
    quaternionToEulerY(x, y, z, w) {
        // Simplified conversion - returns Y rotation (most common for 2D)
        const sinr_cosp = 2 * (w * z + x * y);
        const cosr_cosp = 1 - 2 * (y * y + z * z);
        return Math.atan2(sinr_cosp, cosr_cosp) * (180 / Math.PI);
    }
    async readFile(path) {
        var _a;
        // In browser environment, this would use fetch or FileReader
        // In Node.js, this would use fs.readFileSync
        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');
    }
    /** Convert Unity RectTransform to Nice2Dev format */
    convertRectTransform(transform) {
        return {
            type: 'RectTransform',
            data: {
                anchorMin: transform.m_AnchorMin || { x: 0.5, y: 0.5 },
                anchorMax: transform.m_AnchorMax || { x: 0.5, y: 0.5 },
                anchoredPosition: transform.m_AnchoredPosition || { x: 0, y: 0 },
                sizeDelta: transform.m_SizeDelta || { x: 100, y: 100 },
                pivot: transform.m_Pivot || { x: 0.5, y: 0.5 },
            },
        };
    }
}

exports.UnitySceneImporter = UnitySceneImporter;
//# sourceMappingURL=UnitySceneImporter.js.map
