import { AbstractSimulationStep } from "./AbstractSimulationStep.js";
import {
    PARTICLE_ATTRIBUTE_AGE,
    PARTICLE_ATTRIBUTE_DEATH_AGE,
    PARTICLE_ATTRIBUTE_LAYER_POSITION,
    PARTICLE_ATTRIBUTE_POSITION,
    PARTICLE_ATTRIBUTE_ROTATION,
    PARTICLE_ATTRIBUTE_ROTATION_SPEED,
    PARTICLE_ATTRIBUTE_VELOCITY
} from "../emitter/PARTICLE_ATTRIBUTES.js";
import { update_parameters } from "./update_parameters.js";

/**
 * Common buffer for better memory layout
 * @type {ArrayBuffer}
 */
const scratch_common_buffer = new ArrayBuffer(4 * 3 * 2);
/**
 *
 * @type {Float32Array|number[]|vec3}
 */
const velocity = new Float32Array(scratch_common_buffer, 0, 3);

/**
 *
 * @type {Float32Array|number[]|vec3}
 */
const position = new Float32Array(scratch_common_buffer, 3 * 4, 3);

export class SimulationStepFixedPhysics extends AbstractSimulationStep {
    execute() {
        let i = 0;
        const particles = this.particles;
        const timeDelta = this.timeDelta;

        const liveParticleCount = particles.size();


        const layerMask = this.layer_mask;

        const emitter = this.emitter;

        /**
         *
         * @type {ParticleLayer[]}
         */
        const layers = emitter.layers.asArray();

        for (; i < liveParticleCount; i++) {

            const layer_position = particles.readAttributeScalar(i, PARTICLE_ATTRIBUTE_LAYER_POSITION);

            if ((layerMask & (1 << layer_position)) === 0) {
                //skip
                continue;
            }

            const age = particles.readAttributeScalar(i, PARTICLE_ATTRIBUTE_AGE);

            const deathAge = particles.readAttributeScalar(i, PARTICLE_ATTRIBUTE_DEATH_AGE);

            const newAge = age + timeDelta;

            const normalized_age = newAge / deathAge;

            //retire dead particles
            if (normalized_age >= 1) {
                //add to trash
                particles.remove(i);
                //we're done with this particle
                continue;
            }

            //make older
            particles.writeAttributeScalar(i, PARTICLE_ATTRIBUTE_AGE, newAge);

            //update rotation
            const rotationSpeed = particles.readAttributeScalar(i, PARTICLE_ATTRIBUTE_ROTATION_SPEED);
            const oldRotation = particles.readAttributeScalar(i, PARTICLE_ATTRIBUTE_ROTATION);

            particles.writeAttributeScalar(i, PARTICLE_ATTRIBUTE_ROTATION, oldRotation + rotationSpeed * timeDelta);

            //advance position based on velocity
            particles.readAttributeVector3(i, PARTICLE_ATTRIBUTE_VELOCITY, velocity);

            particles.readAttributeVector3(i, PARTICLE_ATTRIBUTE_POSITION, position);

            // Verlet integration
            const p_x = position[0] + velocity[0] * timeDelta;
            const p_y = position[1] + velocity[1] * timeDelta;
            const p_z = position[2] + velocity[2] * timeDelta;

            particles.writeAttributeVector3(i, PARTICLE_ATTRIBUTE_POSITION, p_x, p_y, p_z);

            const layer = layers[layer_position];

            update_parameters(i, particles, layer, normalized_age);
        }
    }

    get parameter_schema() {
        return [];
    }
}
