import { Scene } from '../../../scene/scene.js';
import { defineProps } from '../types.js';
import { Vector3, Vector4 } from '@zephyr3d/base';
import { JSONData, JSONArray } from '../json.js';
import { ScriptAttachment, normalizeScriptAttachmentConfig } from '../../../scene/script_attachment.js';

/** @internal */ function getSceneClass(manager) {
    return {
        ctor: Scene,
        name: 'Scene',
        createFunc () {
            return {
                obj: new Scene()
            };
        },
        getProps () {
            return defineProps([
                {
                    name: 'Name',
                    description: 'Scene name',
                    type: 'string',
                    default: '',
                    isHidden () {
                        return true;
                    },
                    get (value) {
                        value.str[0] = this.name;
                    },
                    set (value) {
                        this.name = value.str[0];
                    }
                },
                {
                    name: 'EnvLightType',
                    description: 'Environment light type of scene',
                    phase: 0,
                    type: 'string',
                    options: {
                        enum: {
                            labels: [
                                'None',
                                'Constant',
                                'Hemispheric',
                                'IBL'
                            ],
                            values: [
                                'none',
                                'constant',
                                'hemisphere',
                                'ibl'
                            ]
                        }
                    },
                    default: 'ibl',
                    get (value) {
                        value.str[0] = this.env.light.type;
                    },
                    set (value) {
                        this.env.light.type = value.str[0];
                    }
                },
                {
                    name: 'AmbientColor',
                    description: 'RGB value for ambient color if environment light type sets to `Constant`',
                    type: 'rgb',
                    phase: 1,
                    default: [
                        0.2,
                        0.2,
                        0.2
                    ],
                    options: {
                        animatable: true
                    },
                    get (value) {
                        const color = this.env.light.ambientColor;
                        value.num[0] = color.x;
                        value.num[1] = color.y;
                        value.num[2] = color.z;
                    },
                    set (value) {
                        this.env.light.ambientColor.setXYZW(value.num[0], value.num[1], value.num[2], 1);
                    },
                    isValid () {
                        return this.env.light.type === 'constant';
                    }
                },
                {
                    name: 'AmbientUp',
                    description: 'RGB value for upper ambient color if environment light type sets to `Hemispheric`',
                    type: 'rgb',
                    phase: 1,
                    default: [
                        0.3,
                        0.5,
                        0.8
                    ],
                    options: {
                        animatable: true
                    },
                    get (value) {
                        const color = this.env.light.ambientUp;
                        value.num[0] = color.x;
                        value.num[1] = color.y;
                        value.num[2] = color.z;
                    },
                    set (value) {
                        this.env.light.ambientUp.setXYZW(value.num[0], value.num[1], value.num[2], 1);
                    },
                    isValid () {
                        return this.env.light.type === 'hemisphere';
                    }
                },
                {
                    name: 'AmbientDown',
                    description: 'RGB value for lower ambient color if environment light type sets to `Hemispheric`',
                    type: 'rgb',
                    phase: 1,
                    default: [
                        0.2,
                        0.2,
                        0.2
                    ],
                    options: {
                        animatable: true
                    },
                    get (value) {
                        const color = this.env.light.ambientDown;
                        value.num[0] = color.x;
                        value.num[1] = color.y;
                        value.num[2] = color.z;
                    },
                    set (value) {
                        this.env.light.ambientDown.setXYZW(value.num[0], value.num[1], value.num[2], 1);
                    },
                    isValid () {
                        return this.env.light.type === 'hemisphere';
                    }
                },
                {
                    name: 'EnvLightStrength',
                    description: 'Environment light strength',
                    type: 'float',
                    phase: 0,
                    options: {
                        animatable: true,
                        minValue: 0,
                        maxValue: 10
                    },
                    default: 1,
                    get (value) {
                        value.num[0] = this.env.light.strength;
                    },
                    set (value) {
                        this.env.light.strength = value.num[0];
                    }
                },
                {
                    name: 'SHWindowWeights',
                    description: 'Window weights for SH lighting',
                    type: 'vec3',
                    options: {
                        minValue: 0,
                        maxValue: 1
                    },
                    default: [
                        1,
                        1,
                        1
                    ],
                    get (value) {
                        const weights = this.env.sky.shWindowWeights;
                        value.num[0] = weights.x;
                        value.num[1] = weights.y;
                        value.num[2] = weights.z;
                    },
                    set (value) {
                        this.env.sky.shWindowWeights = new Vector3(value.num[0], value.num[1], value.num[2]);
                    }
                },
                {
                    name: 'RadianceConvSamples',
                    description: 'Sample count for radiance convolution',
                    type: 'int',
                    options: {
                        minValue: 1,
                        maxValue: 2048
                    },
                    default: 64,
                    get (value) {
                        value.num[0] = this.env.sky.radianceConvSamples;
                    },
                    set (value) {
                        this.env.sky.radianceConvSamples = value.num[0];
                    }
                },
                {
                    name: 'IrradianceConvSamples',
                    description: 'Sample count for irradiance convolution',
                    type: 'int',
                    options: {
                        minValue: 1,
                        maxValue: 2048
                    },
                    default: 256,
                    get (value) {
                        value.num[0] = this.env.sky.irradianceConvSamples;
                    },
                    set (value) {
                        this.env.sky.irradianceConvSamples = value.num[0];
                    }
                },
                {
                    name: 'SkyType',
                    description: 'Type of sky rendering used by the scene',
                    type: 'string',
                    phase: 0,
                    options: {
                        enum: {
                            labels: [
                                'None',
                                'Image',
                                'SkyBox',
                                'Scatter'
                            ],
                            values: [
                                'none',
                                'image',
                                'skybox',
                                'scatter'
                            ]
                        }
                    },
                    default: 'scatter',
                    get (value) {
                        value.str[0] = this.env.sky.skyType;
                    },
                    set (value) {
                        this.env.sky.skyType = value.str[0];
                    }
                },
                {
                    name: 'SkyColor',
                    description: 'RGB value for sky color if skyType sets to `Image`',
                    type: 'rgb',
                    phase: 1,
                    default: [
                        1,
                        1,
                        1
                    ],
                    options: {
                        animatable: true
                    },
                    isValid () {
                        return this.env.sky.skyType === 'image';
                    },
                    get (value) {
                        const color = this.env.sky.skyColor;
                        value.num[0] = color.x;
                        value.num[1] = color.y;
                        value.num[2] = color.z;
                    },
                    set (value) {
                        this.env.sky.skyColor = new Vector4(value.num[0], value.num[1], value.num[2], 1);
                        this.env.sky.invalidate();
                    }
                },
                {
                    name: 'SkyImage',
                    description: 'Sky image file path if skyType sets to `Image`',
                    type: 'object',
                    default: null,
                    options: {
                        mimeTypes: [
                            'image/jpeg',
                            'image/png',
                            'image/tga',
                            'image/vnd.radiance',
                            'image/x-dds',
                            'image/webp'
                        ]
                    },
                    isValid () {
                        return this.env.sky.skyType === 'image';
                    },
                    isNullable () {
                        return true;
                    },
                    get (value) {
                        value.str[0] = manager.getAssetId(this.env.sky.skyImage) ?? '';
                    },
                    async set (value) {
                        if (!value) {
                            this.env.sky.skyImage = null;
                        } else {
                            if (value.str[0]) {
                                const assetId = value.str[0];
                                let tex;
                                try {
                                    tex = await manager.fetchTexture(assetId);
                                } catch (err) {
                                    console.error(`Load asset failed: ${value.str[0]}: ${err}`);
                                    tex = null;
                                }
                                const isValidTextureType = !!tex?.isTexture2D();
                                if (isValidTextureType) {
                                    this.env.sky.skyImage = tex;
                                } else {
                                    console.error('Invalid texture type');
                                }
                            }
                        }
                    }
                },
                {
                    name: 'FogType',
                    description: 'Type of fog rendering used by the scene',
                    type: 'string',
                    phase: 0,
                    options: {
                        enum: {
                            labels: [
                                'None',
                                'ExponentialHeightFog'
                            ],
                            values: [
                                'none',
                                'height_fog'
                            ]
                        }
                    },
                    default: 'none',
                    get (value) {
                        value.str[0] = this.env.sky.fogType;
                    },
                    set (value) {
                        this.env.sky.fogType = value.str[0];
                    }
                },
                {
                    type: 'float',
                    name: 'HeightFogDensity',
                    description: 'Base density of exponential height fog',
                    default: 0.04,
                    options: {
                        group: 'HeightFog',
                        label: 'Density',
                        minValue: 0,
                        maxValue: 1
                    },
                    isValid () {
                        return this.env.sky.fogType === 'height_fog';
                    },
                    get (value) {
                        value.num[0] = this.env.sky.heightFogDensity;
                    },
                    set (value) {
                        this.env.sky.heightFogDensity = value.num[0];
                    }
                },
                {
                    name: 'HeightFogFalloff',
                    description: 'How quickly fog density changes with height',
                    type: 'float',
                    default: 0.2,
                    options: {
                        group: 'HeightFog',
                        label: 'Falloff',
                        minValue: 0,
                        maxValue: 1
                    },
                    isValid () {
                        return this.env.sky.fogType === 'height_fog';
                    },
                    get (value) {
                        value.num[0] = this.env.sky.heightFogFalloff;
                    },
                    set (value) {
                        this.env.sky.heightFogFalloff = value.num[0];
                    }
                },
                {
                    name: 'HeightFogStartHeight',
                    description: 'World height where height fog starts',
                    type: 'float',
                    default: 0,
                    options: {
                        group: 'HeightFog',
                        label: 'StartHeight',
                        minValue: 0,
                        maxValue: 1000
                    },
                    isValid () {
                        return this.env.sky.fogType === 'height_fog';
                    },
                    get (value) {
                        value.num[0] = this.env.sky.heightFogStartHeight;
                    },
                    set (value) {
                        this.env.sky.heightFogStartHeight = value.num[0];
                    }
                },
                {
                    name: 'HeightFogColor',
                    description: 'Base color of the height fog',
                    type: 'rgb',
                    phase: 1,
                    default: [
                        0,
                        0,
                        0
                    ],
                    options: {
                        group: 'HeightFog',
                        label: 'FogColor',
                        animatable: true
                    },
                    isValid () {
                        return this.env.sky.fogType === 'height_fog';
                    },
                    get (value) {
                        const color = this.env.sky.heightFogColor;
                        value.num[0] = color.x;
                        value.num[1] = color.y;
                        value.num[2] = color.z;
                    },
                    set (value) {
                        this.env.sky.heightFogColor = new Vector3(value.num[0], value.num[1], value.num[2]);
                    }
                },
                {
                    name: 'HeightFogStartDistance',
                    description: 'Distance from the camera where height fog begins',
                    type: 'float',
                    default: 0,
                    options: {
                        group: 'HeightFog',
                        label: 'StartDistance'
                    },
                    isValid () {
                        return this.env.sky.fogType === 'height_fog';
                    },
                    get (value) {
                        value.num[0] = this.env.sky.heightFogStartDistance;
                    },
                    set (value) {
                        this.env.sky.heightFogStartDistance = value.num[0];
                    }
                },
                {
                    name: 'HeightFogEndDistance',
                    description: 'Distance from the camera where height fog reaches full effect',
                    type: 'float',
                    default: 10000,
                    options: {
                        group: 'HeightFog',
                        label: 'EndDistance'
                    },
                    isValid () {
                        return this.env.sky.fogType === 'height_fog';
                    },
                    get (value) {
                        value.num[0] = this.env.sky.heightFogEndDistance;
                    },
                    set (value) {
                        this.env.sky.heightFogEndDistance = value.num[0];
                    }
                },
                {
                    name: 'HeightFogMaxOpacity',
                    description: 'Maximum opacity of the height fog',
                    type: 'float',
                    options: {
                        group: 'HeightFog',
                        label: 'MaxOpacity',
                        minValue: 0,
                        maxValue: 1
                    },
                    default: 1,
                    isValid () {
                        return this.env.sky.fogType === 'height_fog';
                    },
                    get (value) {
                        value.num[0] = this.env.sky.heightFogMaxOpacity;
                    },
                    set (value) {
                        this.env.sky.heightFogMaxOpacity = value.num[0];
                    }
                },
                {
                    name: 'HeightFogAtmosphereStrength',
                    description: 'How much atmospheric scattering contributes to the fog',
                    type: 'float',
                    options: {
                        group: 'HeightFog',
                        label: 'AtmosphereStrength',
                        minValue: 0,
                        maxValue: 10
                    },
                    default: 1,
                    isValid () {
                        return this.env.sky.fogType === 'height_fog';
                    },
                    get (value) {
                        value.num[0] = this.env.sky.heightFogAtmosphereContribution;
                    },
                    set (value) {
                        this.env.sky.heightFogAtmosphereContribution = value.num[0];
                    }
                },
                {
                    name: 'HeightFogDirExponent',
                    description: 'Directional inscattering exponent for height fog',
                    type: 'float',
                    default: 4,
                    options: {
                        group: 'HeightFog',
                        label: 'DirectionalExponent'
                    },
                    isValid () {
                        return this.env.sky.fogType === 'height_fog';
                    },
                    get (value) {
                        value.num[0] = this.env.sky.heightFogDirExponent;
                    },
                    set (value) {
                        this.env.sky.heightFogDirExponent = value.num[0];
                    }
                },
                {
                    name: 'HeightFogDirColor',
                    description: 'Directional inscattering color for height fog',
                    type: 'rgb',
                    default: [
                        0,
                        0,
                        0
                    ],
                    options: {
                        group: 'HeightFog',
                        label: 'DirectionalInscattering'
                    },
                    isValid () {
                        return this.env.sky.fogType === 'height_fog';
                    },
                    get (value) {
                        value.num[0] = this.env.sky.heightFogDirColor.x;
                        value.num[1] = this.env.sky.heightFogDirColor.y;
                        value.num[2] = this.env.sky.heightFogDirColor.z;
                    },
                    set (value) {
                        this.env.sky.heightFogDirColor = new Vector3(value.num[0], value.num[1], value.num[2]);
                    }
                },
                {
                    name: 'AerialPerspectiveDistance',
                    description: 'Distance scale for aerial perspective in the scattering sky model',
                    type: 'float',
                    phase: 1,
                    options: {
                        animatable: true,
                        minValue: 1,
                        maxValue: 50000
                    },
                    default: 1,
                    get (value) {
                        value.num[0] = this.env.sky.aerialPerspectiveDistance;
                    },
                    set (value) {
                        this.env.sky.aerialPerspectiveDistance = value.num[0];
                    },
                    isValid () {
                        return this.env.sky.skyType === 'scatter';
                    }
                },
                {
                    name: 'CameraHeightScale',
                    description: 'Scale factor applied to camera height in atmospheric scattering',
                    type: 'float',
                    phase: 1,
                    options: {
                        animatable: true,
                        minValue: 1,
                        maxValue: 1000
                    },
                    default: 1,
                    get (value) {
                        value.num[0] = this.env.sky.cameraHeightScale;
                    },
                    set (value) {
                        this.env.sky.cameraHeightScale = value.num[0];
                    },
                    isValid () {
                        return this.env.sky.skyType === 'scatter';
                    }
                },
                {
                    name: 'AtmosphereExposure',
                    description: 'Exposure multiplier for atmospheric scattering and sky rendering',
                    type: 'float',
                    options: {
                        animatable: true,
                        minValue: 0,
                        maxValue: 8
                    },
                    default: 1,
                    get (value) {
                        value.num[0] = this.env.sky.atmosphereExposure;
                    },
                    set (value) {
                        this.env.sky.atmosphereExposure = value.num[0];
                    },
                    isValid () {
                        return this.env.sky.skyType === 'scatter';
                    }
                },
                {
                    name: 'Cloudy',
                    description: 'Cloud coverage amount for the procedural sky',
                    type: 'float',
                    default: 0.6,
                    phase: 1,
                    options: {
                        animatable: true,
                        minValue: 0,
                        maxValue: 1
                    },
                    get (value) {
                        value.num[0] = this.env.sky.cloudy;
                    },
                    set (value) {
                        this.env.sky.cloudy = value.num[0];
                    },
                    isValid () {
                        return this.env.sky.skyType === 'scatter';
                    }
                },
                {
                    name: 'CloudIntensity',
                    description: 'Brightness of procedural clouds',
                    type: 'float',
                    default: 40,
                    phase: 1,
                    options: {
                        animatable: true,
                        minValue: 0,
                        maxValue: 200
                    },
                    get (value) {
                        value.num[0] = this.env.sky.cloudIntensity;
                    },
                    set (value) {
                        this.env.sky.cloudIntensity = value.num[0];
                    },
                    isValid () {
                        return this.env.sky.skyType === 'scatter';
                    }
                },
                {
                    name: 'Wind',
                    description: 'Wind direction and speed used by the procedural sky',
                    type: 'vec2',
                    default: [
                        0,
                        0
                    ],
                    phase: 1,
                    options: {
                        animatable: true,
                        minValue: -100,
                        maxValue: 100
                    },
                    get (value) {
                        value.num[0] = this.env.sky.wind.x;
                        value.num[1] = this.env.sky.wind.y;
                    },
                    set (value) {
                        this.env.sky.wind.setXY(value.num[0], value.num[1]);
                    },
                    isValid () {
                        return this.env.sky.skyType === 'scatter';
                    }
                },
                {
                    name: 'SkyBoxRotation',
                    description: 'Euler rotation of the skybox',
                    type: 'vec3',
                    default: [
                        0,
                        0,
                        0
                    ],
                    phase: 1,
                    options: {
                        label: 'Rotation',
                        edit: 'quaternion',
                        animatable: true
                    },
                    get (value) {
                        value.num[0] = this.env.sky.skyboxRotation.x;
                        value.num[1] = this.env.sky.skyboxRotation.y;
                        value.num[2] = this.env.sky.skyboxRotation.z;
                    },
                    set (value) {
                        this.env.sky.skyboxRotation = new Vector3(value.num[0], value.num[1], value.num[2]);
                    },
                    isValid () {
                        return this.env.sky.skyType === 'skybox';
                    }
                },
                {
                    name: 'SkyBoxTextureSize',
                    description: 'Skybox cubemap resolution',
                    type: 'int',
                    default: 1024,
                    get (value) {
                        value.num[0] = this.env.sky.skyboxTextureSize;
                    },
                    set (value) {
                        this.env.sky.skyboxTextureSize = value.num[0];
                    },
                    isValid () {
                        return this.env.sky.skyType === 'skybox';
                    }
                },
                {
                    name: 'PanoramaTexture',
                    description: 'Panorama texture file path if skyType sets to `skybox`',
                    type: 'object',
                    phase: 1,
                    options: {
                        mimeTypes: [
                            'image/vnd.radiance'
                        ]
                    },
                    get (value) {
                        value.str[0] = this.env.sky.panoramaTextureAsset;
                    },
                    async set (value) {
                        this.env.sky.panoramaTextureAsset = value.str[0];
                    },
                    isValid () {
                        return this.env.sky.skyType === 'skybox';
                    }
                },
                {
                    name: 'NodeHierarchy',
                    description: 'Node tree of this scene',
                    type: 'object',
                    isHidden () {
                        return true;
                    },
                    get (value) {
                        value.object[0] = this.rootNode;
                    },
                    set (value) {
                        const sceneNode = value.object[0];
                        if (sceneNode) {
                            sceneNode.remove();
                            for (const child of sceneNode.children){
                                child.parent = this.rootNode;
                            }
                        }
                    }
                },
                {
                    name: 'MainCamera',
                    description: 'Main camera node of this scene',
                    type: 'string',
                    isHidden () {
                        return true;
                    },
                    get (value) {
                        value.str[0] = this.mainCamera?.persistentId ?? '';
                    },
                    set (value) {
                        this.mainCamera = value.str[0] ? this.findNodeById(value.str[0]) : null;
                    }
                },
                {
                    name: 'Script',
                    description: 'The first script path which is attached to this scene',
                    type: 'object',
                    options: {
                        mimeTypes: [
                            'text/x-typescript'
                        ]
                    },
                    isHidden () {
                        return true;
                    },
                    isNullable () {
                        return true;
                    },
                    get (value) {
                        value.str[0] = this.script;
                    },
                    set (value) {
                        this.script = value?.str?.[0] ?? '';
                    }
                },
                {
                    name: 'Scripts',
                    description: 'All scripts paths which are attached to this scene',
                    type: 'object_array',
                    options: {
                        objectTypes: [
                            ScriptAttachment
                        ]
                    },
                    isHidden () {
                        return true;
                    },
                    getDefaultValue () {
                        return this.scripts;
                    },
                    isNullable () {
                        return false;
                    },
                    get (value) {
                        value.object = this.scripts;
                    },
                    set (value) {
                        this.scripts = value.object ?? [];
                    },
                    create () {
                        return new ScriptAttachment();
                    },
                    add (value, index) {
                        const attachments = this.scripts;
                        attachments.splice(index ?? attachments.length, 0, value.object?.[0] ?? new ScriptAttachment());
                        this.scripts = attachments;
                    },
                    delete (index) {
                        const attachments = this.scripts;
                        if (index >= 0 && index < attachments.length) {
                            attachments.splice(index, 1);
                            this.scripts = attachments;
                        }
                    }
                },
                {
                    name: 'ScriptConfig',
                    description: 'Configuration object passed to the first scene script',
                    type: 'object',
                    default: null,
                    options: {
                        objectTypes: [
                            JSONData,
                            JSONArray
                        ]
                    },
                    isHidden () {
                        return true;
                    },
                    isNullable () {
                        return true;
                    },
                    isPersistent () {
                        return !!this.script;
                    },
                    get (value) {
                        const config = normalizeScriptAttachmentConfig(this.scriptConfig);
                        value.object[0] = config == null ? null : Array.isArray(config) ? new JSONArray(null, config) : new JSONData(null, config);
                    },
                    set (value) {
                        const data = value?.object[0];
                        this.scriptConfig = data instanceof JSONData || data instanceof JSONArray ? normalizeScriptAttachmentConfig(data.data) : normalizeScriptAttachmentConfig(data);
                    }
                },
                {
                    name: 'ScriptConfigs',
                    description: 'Per-script configuration array for all scene scripts',
                    type: 'object',
                    default: null,
                    options: {
                        objectTypes: [
                            JSONArray
                        ]
                    },
                    getDefaultValue () {
                        return this.scriptConfigs;
                    },
                    isHidden () {
                        return true;
                    },
                    isNullable () {
                        return true;
                    },
                    isPersistent () {
                        return this.scripts.length > 0;
                    },
                    get (value) {
                        value.object[0] = this.scriptConfigs.length > 0 ? new JSONArray(null, this.scriptConfigs) : null;
                    },
                    set (value) {
                        const data = value?.object[0];
                        this.scriptConfigs = data instanceof JSONArray ? data.data : Array.isArray(data) ? data : [];
                    }
                },
                {
                    name: 'Metadata',
                    description: 'Meta data for this scene',
                    type: 'object',
                    default: null,
                    options: {
                        objectTypes: [
                            JSONData,
                            JSONArray
                        ]
                    },
                    isNullable () {
                        return true;
                    },
                    get (value) {
                        value.object[0] = this.metaData ? Array.isArray(this.metaData) ? new JSONArray(null, this.metaData) : new JSONData(null, this.metaData) : null;
                    },
                    set (value) {
                        const data = value?.object[0];
                        this.metaData = data instanceof JSONData || data instanceof JSONArray ? data.data : data ?? null;
                    }
                }
            ]);
        }
    };
}

export { getSceneClass };
//# sourceMappingURL=scene.js.map
