import { createGPUClothWrapBindingData } from '../../../animation/cloth/gpu_cloth_system.js';
import { defineProps } from '../types.js';

const clothConfigHostMap = new WeakMap();
const clothTargetHostMap = new WeakMap();
const clothTargetOwnerMap = new WeakMap();
function clamp01(value) {
    return Math.min(Math.max(value, 0), 1);
}
function normalizePlaneDirection(value) {
    return value < 0 ? -1 : 1;
}
function resolveClothHostScope(host) {
    return typeof host?.getPrefabNode === 'function' && host.getPrefabNode() || host.scene?.rootNode || host;
}
function resolveClothMeshById(host, meshId) {
    const id = String(meshId ?? '').trim();
    if (!host || !id) {
        return null;
    }
    const scope = resolveClothHostScope(host);
    const candidate = scope?.findNodeById?.(id);
    return candidate?.isMesh?.() && candidate.primitive ? candidate : null;
}
function resolveClothMeshLabel(host, meshId) {
    const id = String(meshId ?? '').trim();
    if (!id) {
        return '';
    }
    const mesh = resolveClothMeshById(host, id);
    if (mesh) {
        const name = String(mesh.name ?? '').trim();
        return name || mesh.persistentId || id;
    }
    return `[Missing] ${id}`;
}
function getClothConfigHost(config) {
    return clothConfigHostMap.get(config) ?? null;
}
function getClothTargetHost(target) {
    const owner = clothTargetOwnerMap.get(target);
    return clothTargetHostMap.get(target) ?? (owner ? clothConfigHostMap.get(owner) : null) ?? null;
}
function setClothTargetOwner(target, owner, host) {
    clothTargetOwnerMap.set(target, owner);
    if (host) {
        clothTargetHostMap.set(target, host);
    }
}
function clearClothTargetBinding(target) {
    target.bindingData = '';
    target.bindingSignature = '';
}
function clearClothBindings(config) {
    for (const target of config.targetMeshes){
        clearClothTargetBinding(target);
    }
}
function setClothBindingStatus(config, status) {
    config.bindingStatus = status;
}
function ensureDefaultTargetMeshEntry(config) {
    if (config.targetMeshes.length === 0) {
        const target = new ClothTargetMeshConfig();
        config.targetMeshes.push(target);
        setClothTargetOwner(target, config, getClothConfigHost(config));
    }
}
function invalidateClothBindings(config, status = 'Binding cache needs rebuild') {
    clearClothBindings(config);
    setClothBindingStatus(config, status);
}
async function bindClothTargetMeshes(config) {
    const host = getClothConfigHost(config);
    if (config.bindingInProgress) {
        return;
    }
    const simulationMesh = resolveClothMeshById(host, config.simulationMeshId) || (host?.isMesh?.() && host.primitive ? host : null);
    if (!simulationMesh) {
        setClothBindingStatus(config, 'Set Simulation Mesh before binding.');
        return;
    }
    const targetEntries = config.targetMeshes.filter((entry)=>String(entry.meshId ?? '').trim().length > 0);
    if (targetEntries.length === 0) {
        setClothBindingStatus(config, 'Add at least one Target Mesh before binding.');
        return;
    }
    config.bindingInProgress = true;
    setClothBindingStatus(config, 'Binding target meshes...');
    let successCount = 0;
    const failedTargets = [];
    try {
        for (const entry of targetEntries){
            const targetMesh = resolveClothMeshById(getClothTargetHost(entry), entry.meshId);
            if (!targetMesh) {
                clearClothTargetBinding(entry);
                failedTargets.push(resolveClothMeshLabel(host, entry.meshId));
                continue;
            }
            if (targetMesh === simulationMesh) {
                clearClothTargetBinding(entry);
                failedTargets.push(resolveClothMeshLabel(host, entry.meshId));
                continue;
            }
            try {
                const data = await createGPUClothWrapBindingData(simulationMesh, targetMesh);
                entry.bindingData = JSON.stringify(data);
                entry.bindingSignature = `${data.version}:${data.vertexCount}:${data.sourceVertexCount}:${data.influenceCount}`;
                successCount++;
            } catch (err) {
                clearClothTargetBinding(entry);
                failedTargets.push(`${resolveClothMeshLabel(host, entry.meshId)}: ${err instanceof Error ? err.message : String(err)}`);
            }
        }
    } finally{
        config.bindingInProgress = false;
    }
    if (successCount > 0 && failedTargets.length === 0) {
        setClothBindingStatus(config, `Bound ${successCount} target mesh(es).`);
    } else if (successCount > 0) {
        setClothBindingStatus(config, `Bound ${successCount} target mesh(es), ${failedTargets.length} failed: ${failedTargets.join('; ')}`);
    } else {
        setClothBindingStatus(config, failedTargets[0] || 'Binding failed.');
    }
}
function bindClothScriptConfigHost(config, host) {
    clothConfigHostMap.set(config, host);
    ensureDefaultTargetMeshEntry(config);
    for (const target of config.targetMeshes){
        setClothTargetOwner(target, config, host);
    }
}
class ClothColliderConfig {
    type;
    enabled;
    bone;
    offsetX;
    offsetY;
    offsetZ;
    endOffsetX;
    endOffsetY;
    endOffsetZ;
    normalX;
    normalY;
    normalZ;
    radius;
    constructor(type = 'sphere'){
        this.type = type;
        this.enabled = true;
        this.bone = '';
        this.offsetX = 0;
        this.offsetY = 0;
        this.offsetZ = 0;
        this.endOffsetX = 0;
        this.endOffsetY = 0.2;
        this.endOffsetZ = 0;
        this.normalX = 0;
        this.normalY = 1;
        this.normalZ = 0;
        this.radius = 0.15;
    }
}
class ClothTargetMeshConfig {
    meshId;
    bindingData;
    bindingSignature;
    constructor(){
        this.meshId = '';
        this.bindingData = '';
        this.bindingSignature = '';
    }
}
class ClothScriptConfig {
    enabled;
    autoUpdate;
    simulationMeshId;
    targetMeshes;
    bindingStatus;
    bindingInProgress;
    damping;
    dynamicFriction;
    staticFriction;
    stiffness;
    poseFollow;
    substeps;
    gravityX;
    gravityY;
    gravityZ;
    solverIterations;
    vertexPinWeightsByTarget;
    pinnedVertexIndicesByTarget;
    maxNeighbors;
    maxTrianglesPerVertex;
    workgroupSize;
    rebuildNormals;
    colliders;
    constructor(){
        this.enabled = true;
        this.autoUpdate = true;
        this.simulationMeshId = '';
        this.targetMeshes = [
            new ClothTargetMeshConfig()
        ];
        this.bindingStatus = 'Binding cache needs rebuild';
        this.bindingInProgress = false;
        this.damping = 0.02;
        this.dynamicFriction = 0.15;
        this.staticFriction = 0.3;
        this.stiffness = 0.3;
        this.poseFollow = 0;
        this.substeps = 2;
        this.gravityX = 0;
        this.gravityY = -9.8;
        this.gravityZ = 0;
        this.solverIterations = 5;
        this.vertexPinWeightsByTarget = '';
        this.pinnedVertexIndicesByTarget = '';
        this.maxNeighbors = 8;
        this.maxTrianglesPerVertex = 16;
        this.workgroupSize = 64;
        this.rebuildNormals = true;
        this.colliders = [];
    }
}
function getClothColliderConfigClass() {
    return {
        ctor: ClothColliderConfig,
        name: 'ClothColliderConfig',
        noTitle: true,
        createFunc () {
            return {
                obj: new ClothColliderConfig()
            };
        },
        getProps () {
            return defineProps([
                {
                    name: 'Type',
                    type: 'string',
                    options: {
                        group: 'Shape',
                        enum: {
                            labels: [
                                'Sphere',
                                'Capsule',
                                'Plane'
                            ],
                            values: [
                                'sphere',
                                'capsule',
                                'plane'
                            ]
                        }
                    },
                    get (value) {
                        value.str[0] = this.type;
                    },
                    set (value) {
                        this.type = value.str[0] ?? 'sphere';
                    }
                },
                {
                    name: 'Enabled',
                    type: 'bool',
                    options: {
                        group: 'General'
                    },
                    get (value) {
                        value.bool[0] = this.enabled;
                    },
                    set (value) {
                        this.enabled = !!value.bool[0];
                    }
                },
                {
                    name: 'Bone',
                    type: 'string',
                    options: {
                        group: 'General'
                    },
                    get (value) {
                        value.str[0] = this.bone;
                    },
                    set (value) {
                        this.bone = value.str[0] ?? '';
                    }
                },
                {
                    name: 'Offset',
                    type: 'vec3',
                    options: {
                        group: 'Shape'
                    },
                    isValid () {
                        return this.type !== 'plane';
                    },
                    get (value) {
                        value.num[0] = this.offsetX;
                        value.num[1] = this.offsetY;
                        value.num[2] = this.offsetZ;
                    },
                    set (value) {
                        this.offsetX = value.num[0];
                        this.offsetY = value.num[1];
                        this.offsetZ = value.num[2];
                    }
                },
                {
                    name: 'EndOffset',
                    type: 'vec3',
                    options: {
                        group: 'Shape'
                    },
                    isValid () {
                        return this.type === 'capsule';
                    },
                    get (value) {
                        value.num[0] = this.endOffsetX;
                        value.num[1] = this.endOffsetY;
                        value.num[2] = this.endOffsetZ;
                    },
                    set (value) {
                        this.endOffsetX = value.num[0];
                        this.endOffsetY = value.num[1];
                        this.endOffsetZ = value.num[2];
                    }
                },
                {
                    name: 'Radius',
                    type: 'float',
                    options: {
                        group: 'Shape',
                        minValue: 0
                    },
                    isValid () {
                        return this.type !== 'plane';
                    },
                    get (value) {
                        value.num[0] = this.radius;
                    },
                    set (value) {
                        this.radius = value.num[0];
                    }
                },
                {
                    name: 'Normal',
                    type: 'float',
                    options: {
                        group: 'Shape',
                        minValue: -1,
                        maxValue: 1
                    },
                    isValid () {
                        return this.type === 'plane';
                    },
                    get (value) {
                        value.num[0] = this.normalY;
                    },
                    set (value) {
                        this.normalX = 0;
                        this.normalY = normalizePlaneDirection(value.num[0]);
                        this.normalZ = 0;
                    }
                }
            ]);
        }
    };
}
function getClothTargetMeshConfigClass() {
    return {
        ctor: ClothTargetMeshConfig,
        name: 'ClothTargetMeshConfig',
        noTitle: true,
        createFunc () {
            return {
                obj: new ClothTargetMeshConfig()
            };
        },
        getProps () {
            return defineProps([
                {
                    name: 'TargetMesh',
                    type: 'string',
                    options: {
                        sceneNode: {
                            kind: 'mesh'
                        }
                    },
                    isPersistent () {
                        return false;
                    },
                    get (value) {
                        value.str[0] = resolveClothMeshLabel(getClothTargetHost(this), this.meshId);
                    },
                    set (value) {
                        const nextId = String(value.str[0] ?? '').trim();
                        if (this.meshId !== nextId) {
                            this.meshId = nextId;
                            clearClothTargetBinding(this);
                            const owner = clothTargetOwnerMap.get(this);
                            if (owner) {
                                setClothBindingStatus(owner, 'Binding cache needs rebuild');
                            }
                        }
                    }
                },
                {
                    name: 'MeshId',
                    type: 'string',
                    isHidden () {
                        return true;
                    },
                    get (value) {
                        value.str[0] = this.meshId;
                    },
                    set (value) {
                        this.meshId = value.str[0] ?? '';
                    }
                },
                {
                    name: 'BindingData',
                    type: 'string',
                    isHidden () {
                        return true;
                    },
                    get (value) {
                        value.str[0] = this.bindingData;
                    },
                    set (value) {
                        this.bindingData = value.str[0] ?? '';
                    }
                },
                {
                    name: 'BindingSignature',
                    type: 'string',
                    isHidden () {
                        return true;
                    },
                    get (value) {
                        value.str[0] = this.bindingSignature;
                    },
                    set (value) {
                        this.bindingSignature = value.str[0] ?? '';
                    }
                }
            ]);
        }
    };
}
function getClothScriptConfigClass() {
    return {
        ctor: ClothScriptConfig,
        name: 'ClothScriptConfig',
        noTitle: true,
        createFunc () {
            return {
                obj: new ClothScriptConfig()
            };
        },
        getProps () {
            return defineProps([
                {
                    name: 'Enabled',
                    type: 'bool',
                    options: {
                        group: 'General'
                    },
                    get (value) {
                        value.bool[0] = this.enabled;
                    },
                    set (value) {
                        this.enabled = !!value.bool[0];
                    }
                },
                {
                    name: 'AutoUpdate',
                    type: 'bool',
                    options: {
                        group: 'General'
                    },
                    get (value) {
                        value.bool[0] = this.autoUpdate;
                    },
                    set (value) {
                        this.autoUpdate = !!value.bool[0];
                    }
                },
                {
                    name: 'SimulationMesh',
                    type: 'string',
                    options: {
                        group: 'General',
                        sceneNode: {
                            kind: 'mesh'
                        }
                    },
                    isPersistent () {
                        return false;
                    },
                    get (value) {
                        value.str[0] = resolveClothMeshLabel(getClothConfigHost(this), this.simulationMeshId);
                    },
                    set (value) {
                        const nextId = String(value.str[0] ?? '').trim();
                        if (this.simulationMeshId !== nextId) {
                            this.simulationMeshId = nextId;
                            invalidateClothBindings(this);
                        }
                    }
                },
                {
                    name: 'SimulationMeshId',
                    type: 'string',
                    isHidden () {
                        return true;
                    },
                    get (value) {
                        value.str[0] = this.simulationMeshId;
                    },
                    set (value) {
                        this.simulationMeshId = value.str[0] ?? '';
                    }
                },
                {
                    name: 'TargetMesh',
                    type: 'object_array',
                    options: {
                        group: 'Binding',
                        objectTypes: [
                            ClothTargetMeshConfig
                        ],
                        inlineObjectArray: true
                    },
                    get (value) {
                        ensureDefaultTargetMeshEntry(this);
                        value.object = this.targetMeshes;
                    },
                    set (value) {
                        this.targetMeshes = (value.object ?? []).filter((entry)=>entry instanceof ClothTargetMeshConfig);
                        ensureDefaultTargetMeshEntry(this);
                        const host = getClothConfigHost(this);
                        for (const entry of this.targetMeshes){
                            setClothTargetOwner(entry, this, host);
                        }
                    },
                    add (value, index) {
                        const target = value.object?.[0] ?? new ClothTargetMeshConfig();
                        setClothTargetOwner(target, this, getClothConfigHost(this));
                        this.targetMeshes.splice(index ?? this.targetMeshes.length, 0, target);
                        setClothBindingStatus(this, 'Binding cache needs rebuild');
                    },
                    delete (index) {
                        this.targetMeshes.splice(index, 1);
                        ensureDefaultTargetMeshEntry(this);
                        setClothBindingStatus(this, 'Binding cache needs rebuild');
                    },
                    create () {
                        return new ClothTargetMeshConfig();
                    }
                },
                {
                    name: 'BindingStatus',
                    type: 'string',
                    readonly: true,
                    options: {
                        group: 'Binding'
                    },
                    isPersistent () {
                        return false;
                    },
                    get (value) {
                        value.str[0] = this.bindingStatus;
                    }
                },
                {
                    name: 'Binding',
                    type: 'command',
                    options: {
                        group: 'Binding'
                    },
                    get (value) {
                        value.str[0] = this.bindingInProgress ? 'Binding...' : 'Bind';
                        value.str[1] = 'Clear';
                    },
                    command (index) {
                        if (index === 0) {
                            if (!this.bindingInProgress) {
                                void bindClothTargetMeshes(this);
                            }
                        } else {
                            clearClothBindings(this);
                            setClothBindingStatus(this, 'Binding cache cleared.');
                        }
                        return true;
                    }
                },
                {
                    name: 'VertexPinWeightsByTarget',
                    type: 'string',
                    isHidden () {
                        return true;
                    },
                    get (value) {
                        value.str[0] = this.vertexPinWeightsByTarget;
                    },
                    set (value) {
                        this.vertexPinWeightsByTarget = value.str[0] ?? '';
                    }
                },
                {
                    name: 'PinnedVertexIndicesByTarget',
                    type: 'string',
                    isHidden () {
                        return true;
                    },
                    get (value) {
                        value.str[0] = this.pinnedVertexIndicesByTarget;
                    },
                    set (value) {
                        this.pinnedVertexIndicesByTarget = value.str[0] ?? '';
                    }
                },
                {
                    name: 'Gravity',
                    type: 'vec3',
                    options: {
                        group: 'Simulation'
                    },
                    get (value) {
                        value.num[0] = this.gravityX;
                        value.num[1] = this.gravityY;
                        value.num[2] = this.gravityZ;
                    },
                    set (value) {
                        this.gravityX = value.num[0];
                        this.gravityY = value.num[1];
                        this.gravityZ = value.num[2];
                    }
                },
                {
                    name: 'Damping',
                    type: 'float',
                    options: {
                        group: 'Simulation',
                        minValue: 0,
                        maxValue: 1
                    },
                    get (value) {
                        value.num[0] = this.damping;
                    },
                    set (value) {
                        this.damping = clamp01(value.num[0]);
                    }
                },
                {
                    name: 'Stiffness',
                    type: 'float',
                    options: {
                        group: 'Simulation',
                        minValue: 0,
                        maxValue: 1
                    },
                    get (value) {
                        value.num[0] = this.stiffness;
                    },
                    set (value) {
                        this.stiffness = clamp01(value.num[0]);
                    }
                },
                {
                    name: 'DynamicFriction',
                    type: 'float',
                    options: {
                        group: 'Simulation',
                        minValue: 0,
                        maxValue: 1
                    },
                    get (value) {
                        value.num[0] = this.dynamicFriction;
                    },
                    set (value) {
                        this.dynamicFriction = clamp01(value.num[0]);
                    }
                },
                {
                    name: 'PoseFollow',
                    type: 'float',
                    options: {
                        group: 'PoseFollow',
                        minValue: 0,
                        maxValue: 1
                    },
                    get (value) {
                        value.num[0] = this.poseFollow;
                    },
                    set (value) {
                        this.poseFollow = clamp01(value.num[0]);
                    }
                },
                {
                    name: 'StaticFriction',
                    type: 'float',
                    options: {
                        group: 'Simulation',
                        minValue: 0,
                        maxValue: 1
                    },
                    get (value) {
                        value.num[0] = this.staticFriction;
                    },
                    set (value) {
                        this.staticFriction = clamp01(value.num[0]);
                    }
                },
                {
                    name: 'SolverIterations',
                    type: 'int',
                    options: {
                        group: 'Simulation',
                        minValue: 1,
                        maxValue: 32
                    },
                    get (value) {
                        value.num[0] = this.solverIterations;
                    },
                    set (value) {
                        this.solverIterations = value.num[0];
                    }
                },
                {
                    name: 'Substeps',
                    type: 'int',
                    options: {
                        group: 'Simulation',
                        minValue: 1,
                        maxValue: 8
                    },
                    get (value) {
                        value.num[0] = this.substeps;
                    },
                    set (value) {
                        this.substeps = Math.max(1, Math.min(8, value.num[0] | 0));
                    }
                },
                {
                    name: 'MaxNeighbors',
                    type: 'int',
                    options: {
                        group: 'Advanced',
                        minValue: 1,
                        maxValue: 64
                    },
                    get (value) {
                        value.num[0] = this.maxNeighbors;
                    },
                    set (value) {
                        this.maxNeighbors = value.num[0];
                    }
                },
                {
                    name: 'MaxTrianglesPerVertex',
                    type: 'int',
                    options: {
                        group: 'Advanced',
                        minValue: 1,
                        maxValue: 64
                    },
                    get (value) {
                        value.num[0] = this.maxTrianglesPerVertex;
                    },
                    set (value) {
                        this.maxTrianglesPerVertex = value.num[0];
                    }
                },
                {
                    name: 'WorkgroupSize',
                    type: 'int',
                    options: {
                        group: 'Advanced',
                        minValue: 1,
                        maxValue: 256
                    },
                    get (value) {
                        value.num[0] = this.workgroupSize;
                    },
                    set (value) {
                        this.workgroupSize = value.num[0];
                    }
                },
                {
                    name: 'RebuildNormals',
                    type: 'bool',
                    options: {
                        group: 'Advanced'
                    },
                    get (value) {
                        value.bool[0] = this.rebuildNormals;
                    },
                    set (value) {
                        this.rebuildNormals = !!value.bool[0];
                    }
                },
                {
                    name: 'Colliders',
                    type: 'object_array',
                    options: {
                        group: 'Collider',
                        objectTypes: [
                            ClothColliderConfig
                        ]
                    },
                    get (value) {
                        value.object = this.colliders;
                    },
                    set (value) {
                        this.colliders = value.object ?? [];
                    },
                    add (value, index) {
                        this.colliders.splice(index ?? this.colliders.length, 0, value.object[0]);
                    },
                    delete (index) {
                        this.colliders.splice(index, 1);
                    },
                    create () {
                        return new ClothColliderConfig();
                    }
                }
            ]);
        }
    };
}

export { ClothColliderConfig, ClothScriptConfig, ClothTargetMeshConfig, bindClothScriptConfigHost, getClothColliderConfigClass, getClothScriptConfigClass, getClothTargetMeshConfigClass };
//# sourceMappingURL=cloth_script.js.map
