import { Vector3, Quaternion } from '@zephyr3d/base';
import { createSpringParticle } from './spring_particle.js';
import { createSpringConstraint } from './spring_constraint.js';

/**
 * Manages a chain of spring particles and constraints
 *
 * @public
 */ class SpringChain {
    /** Array of particles in the chain */ particles;
    /** Array of constraints between particles */ constraints;
    constructor(){
        this.particles = [];
        this.constraints = [];
    }
    /**
   * Creates a spring chain from a bone hierarchy
   * @param startNode - Root node of the bone chain
   * @param endNode - End node of the bone chain (optional, if null uses all descendants)
   * @param options - Additional options
   * @returns A new SpringChain instance
   */ static fromBoneChain(startNode, endNode = null, options) {
        const chain = new SpringChain();
        const mass = options?.mass ?? 1.0;
        const damping = options?.damping ?? 0.95;
        const stiffness = options?.stiffness ?? 0.8;
        // Collect nodes from startNode to endNode
        const nodes = [];
        let current = startNode;
        while(current){
            nodes.push(current);
            if (current === endNode) {
                break;
            }
            const children = current.children;
            if (children.length > 0) {
                current = children[0];
            } else {
                break;
            }
        }
        // Create particles for each bone
        for(let i = 0; i < nodes.length; i++){
            const node = nodes[i];
            const worldPos = new Vector3(node.worldMatrix.m03, node.worldMatrix.m13, node.worldMatrix.m23);
            // Get the original world rotation
            const originalRotation = new Quaternion();
            node.worldMatrix.decompose(null, originalRotation, null);
            const particle = createSpringParticle(worldPos, {
                mass,
                damping,
                fixed: i === 0,
                node,
                originalRotation
            });
            const particleIndex = chain.addParticle(particle);
            // Create constraint with previous particle
            if (i > 0) {
                const prevIndex = particleIndex - 1;
                const prevParticle = chain.particles[prevIndex];
                const restLength = Vector3.distance(particle.position, prevParticle.position);
                chain.addConstraint(createSpringConstraint(prevIndex, particleIndex, restLength, stiffness));
            }
        }
        return chain;
    }
    /**
   * Adds a particle to the chain
   * @param particle - The particle to add
   * @returns The index of the added particle
   */ addParticle(particle) {
        this.particles.push(particle);
        return this.particles.length - 1;
    }
    /**
   * Adds a constraint to the chain
   * @param constraint - The constraint to add
   */ addConstraint(constraint) {
        this.constraints.push(constraint);
    }
    /**
   * Adds cross constraints between particles for cloth-like behavior
   * @param stiffness - Stiffness of the cross constraints
   * @param skipDistance - Distance between particles to connect (default: 2)
   */ addCrossConstraints(stiffness = 0.6, skipDistance = 2) {
        for(let i = 0; i < this.particles.length - skipDistance; i++){
            const pA = this.particles[i];
            const pB = this.particles[i + skipDistance];
            const restLength = Vector3.distance(pA.position, pB.position);
            this.addConstraint(createSpringConstraint(i, i + skipDistance, restLength, stiffness));
        }
    }
    /**
   * Resets all particles to their original positions
   */ reset() {
        for (const particle of this.particles){
            particle.position.set(particle.originalPosition);
            particle.prevPosition.set(particle.originalPosition);
        }
    }
}

export { SpringChain };
//# sourceMappingURL=spring_chain.js.map
