import Vector3 from "../../../../../core/geom/Vector3.js";
import Entity from "../../../../ecs/Entity.js";
import { EventType } from "../../../../ecs/EventType.js";
import Renderable from "../../../../ecs/renderable/Renderable.js";
import { RenderableFlags } from "../../../../ecs/renderable/RenderableFlags.js";
import { Transform } from "../../../../ecs/transform/Transform.js";
import ThreeFactory from "../../../three/ThreeFactory.js";
import {
    RIBBON_ATTRIBUTE_ADDRESS_AGE,
    RIBBON_ATTRIBUTE_ADDRESS_UV_OFFSET
} from "../../../trail/x/ribbon_attributes_spec.js";
import { RibbonX } from "../../../trail/x/RibbonX.js";

export class RibbonPathBuilder {
    constructor() {
        /**
         *
         * @type {RibbonPathStyle|null}
         */
        this.style = null;

        /**
         *
         * @type {Path|null}
         */
        this.path = null;

        /**
         *
         * @type {RibbonXPlugin|null}
         */
        this.plugin = null;
    }

    /**
     *
     * @param {RibbonPathStyle} style
     */
    setStyle(style) {
        this.style = style;
    }

    /**
     *
     * @param {Path} path
     */
    setPath(path) {
        this.path = path;
    }

    /**
     *
     * @param {RibbonXPlugin} plugin
     */
    setPlugin(plugin) {
        this.plugin = plugin;
    }

    /**
     *
     * @param {Entity[]} destination
     */
    build(destination) {

        const path = this.path;

        const pointCount = path.getPointCount();

        /**
         *
         * @type {RibbonPathStyle|null}
         */
        const style = this.style;

        const ribbon_count_count = pointCount * style.resolution;

        const ribbon = new RibbonX();

        ribbon.buildGeometry();
        ribbon.setCount(ribbon_count_count);

        const v3 = new Vector3();

        const path_total_length = path.computeLength();

        for (let i = 0; i < ribbon_count_count; i++) {
            const f = i / (ribbon_count_count - 1);

            path.sample(v3, f * path_total_length);

            ribbon.setPointColor(i, 255, 255, 255);
            ribbon.setPointPosition(i, v3.x, v3.y, v3.z);
            ribbon.setPointThickness(i, style.thickness);
            ribbon.setPointAttribute_Scalar(i, RIBBON_ATTRIBUTE_ADDRESS_UV_OFFSET, f);
            ribbon.setPointAlpha(i, 0.5);
            ribbon.setPointAttribute_Scalar(i, RIBBON_ATTRIBUTE_ADDRESS_AGE, 0);
        }

        if (pointCount > 0) {
            // write first position again to ensure all segments have correct direction
            path.sample(v3, 0);
            ribbon.setPointPosition(0, v3.x, v3.y, v3.z);
        }


        const plugin = this.plugin;
        const engine = plugin.engine;

        const graphics = engine.graphics;

        const geometry = ribbon.getGeometry();
        const material = plugin.obtain_material_x(style.material);

        const mesh = ThreeFactory.createMesh(geometry, material);


        const renderable = new Renderable(mesh);

        renderable.clearFlag(RenderableFlags.BoundingBoxNeedsUpdate);

        ribbon.computeBoundingBox(renderable.boundingBox);

        const entityBuilder = new Entity()
            .add(renderable)
            .add(new Transform());

        function setResolution() {
            material.uniforms.resolution.value.copy(graphics.viewport.size);
        }

        function handleEntityRemoved() {

            graphics.viewport.size.onChanged.remove(setResolution);

        }

        entityBuilder.on.built.add(() => {
            // set resolution
            setResolution();

            graphics.viewport.size.onChanged.add(setResolution);

            entityBuilder.addEventListener(EventType.EntityRemoved, handleEntityRemoved);
        });

        destination.push(entityBuilder);
    }

}
