import { Matrix4,Quaternion, Vector3 } from "three";
import { type Behavior, type EmissionState, Matrix4 as QMatrix4,type Particle, type ParticleSystem } from "three.quarks";

import { CircularBuffer } from "../../engine/engine_utils.js";
import { $particleLife, SubEmitterType } from "./ParticleSystem.js";
import type { IParticleSystem } from "./ParticleSystemModules.js";

const VECTOR_ONE = new Vector3(1, 1, 1);
const VECTOR_Z = new Vector3(0, 0, 1);
const $emitterMatrix = Symbol("emitterMatrix");

export class ParticleSubEmitter implements Behavior {

    type = "NeedleParticleSubEmitter";

    emitterType?: SubEmitterType;
    emitterProbability?: number;

    //private matrix_ = new Matrix4();
    private q_ = new Quaternion();
    private v_ = new Vector3();
    private v2_ = new Vector3();


    private _emitterMatrix: QMatrix4 = new QMatrix4();
    private _circularBuffer: CircularBuffer<QMatrix4>;

    constructor(
        private system: IParticleSystem,
        private particleSystem: ParticleSystem,
        private subSystem: IParticleSystem,
        public subParticleSystem?: ParticleSystem
    ) {
        if (this.subParticleSystem && this.subParticleSystem) {
            this.subParticleSystem.onlyUsedByOther = true;
        }
        const maxMatrices = 1000;
        this._circularBuffer = new CircularBuffer(() => new QMatrix4(), maxMatrices)
    }

    clone(): Behavior {
        throw new Error("Method not implemented.");
    }

    initialize(particle: Particle): void {
        particle.emissionState = {
            burstIndex: 0,
            burstWaveIndex: 0,
            time: 0,
            waitEmiting: 0,
            // matrix: new Matrix4(),
        } as EmissionState;
        // particle[$emitterMatrix] = new Matrix4();
        this._emitterMatrix.copy(this.subSystem.matrixWorld as unknown as QMatrix4).invert().premultiply(this.system.matrixWorld as unknown as QMatrix4);
        this._emitterMatrix.setPosition(0, 0, 0);

        if (this.emitterType === SubEmitterType.Birth) {
            this.run(particle);
        }
    }

    update(particle: Particle, _delta: number): void {
        this.run(particle);
    }

    frameUpdate(_delta: number): void {
    }

    toJSON(): any {
    }

    reset() {
    }

    private run(particle: Particle) {
        if (this.subSystem.currentParticles >= this.subSystem.main.maxParticles)
            return;
        if (!this.subParticleSystem || !particle.emissionState)
            return;

        if (this.emitterProbability && Math.random() > this.emitterProbability)
            return;

        const delta = this.system.deltaTime;

        if (this.emitterType === SubEmitterType.Death) {
            let lifeTime = particle.life;
            if (particle[$particleLife] !== undefined) lifeTime = particle[$particleLife];
            const willDie = particle.age + delta * 1.2 >= lifeTime;
            if (!willDie) return;
            // Just emit all for now, we should probably add a way to get the amount from the subsystem emission module
            const maxAmount = this.subSystem.main.maxParticles - this.subSystem.currentParticles;
            particle.emissionState.waitEmiting = maxAmount;
        }

        // TODO: figure out how to re-use matrices
        const m = new QMatrix4();// this._circularBuffer.get();// new Matrix4();// particle[$emitterMatrix];

        m.set(
            1, 0, 0, particle.position.x,
            0, 1, 0, particle.position.y,
            0, 0, 1, particle.position.z,
            0, 0, 0, 1
        );

        if (!this.particleSystem.worldSpace) {
            m.multiplyMatrices(this._emitterMatrix, m)
        }

        this.subParticleSystem!.emit(delta, particle.emissionState!, m);
    }
}