import { MarkerNodeAction } from "./MarkerNodeAction.js";
import { MarkerNodeConsumerBuffer } from "../emitter/MarkerNodeConsumerBuffer.js";
import Vector3 from "../../../core/geom/Vector3.js";
import { assert } from "../../../core/assert.js";

/**
 * Spawns imaginary markers that are not added to the grid, and executes further actions on these
 * Useful when desired additional markers are not present in the grid
 */
export class MarkerNodeActionImaginary extends MarkerNodeAction {
    /**
     * @type {MarkerNodeEmitter}
     */
    emitter;

    /**
     * @type {MarkerNodeAction}
     */
    action;

    /**
     * Pre-multiplies emitted marker transform by the transform of the consumed node
     * @type {boolean}
     * @private
     */
    propagate_scale = true;

    _buffer = new MarkerNodeConsumerBuffer();

    /**
     *
     * @param {MarkerNodeEmitter} emitter
     * @param {MarkerNodeAction} action
     * @param {boolean} [propagate_scale]
     * @returns {MarkerNodeActionImaginary}
     */
    static from({
                    emitter,
                    action,
                    propagate_scale = true
                }) {

        assert.defined(emitter, 'emitter');
        assert.equal(emitter.isMarkerNodeEmitter, true, 'emitter.isMarkerNodeEmitter !== true');

        assert.defined(action, 'emitter');
        assert.equal(action.isMarkerNodeAction, true, 'action.isMarkerNodeAction !== true');

        assert.isBoolean(propagate_scale, 'propagate_scale');

        const r = new MarkerNodeActionImaginary();

        r.emitter = emitter;
        r.action = action;
        r.propagate_scale = propagate_scale;

        return r;
    }

    execute(grid, ecd, node) {
        this._buffer.reset();

        const rotation = node.transform.rotation.computeTwistAngle(Vector3.down);

        this.emitter.execute(grid, node.position.x, node.position.y, rotation, this._buffer);

        const n = this._buffer.size();

        for (let i = 0; i < n; i++) {
            const imaginary_node = this._buffer.get(i);

            if (this.propagate_scale) {
                imaginary_node.transform.scale.multiply(node.transform.scale);

                imaginary_node.size *= node.size;
            }

            this.action.execute(grid, ecd, imaginary_node);
        }
    }

    initialize(grid, ecd, seed) {
        this.emitter.initialize(grid, seed);
        this.action.initialize(grid, ecd, seed);
    }
}
