/** @internal NOTE: Internal APIs. Subject to change. Use of these APIs in production applications is not supported. */ /** */

import {
    Scene,
    MeshBuilder,
    Mesh,
    RayHelper,
    Ray,
    Vector3,
    Color3,
    LinesMesh,
    Color4,
    VertexBuffer
} from '../loader/babylonjs-import';
import { BimIfcObject } from '../loader/bim-ifc-object';
import { BimIfcMesh } from '../loader/bim-ifc-mesh';

const v1 = Vector3.Zero();
const v2 = Vector3.Zero();
const v3 = Vector3.Zero();

export class GeometryTools {
    private static readonly _tmp = {
        twoPointLineOptions: {
            points: [Vector3.Zero(), Vector3.Zero()],
            updatable: true,
            useVertexAlpha: false,
            //colors: [Color4.FromInts(0, 0, 0, 0), Color4.FromInts(0, 0, 0, 0)],
            instance: undefined as LinesMesh | undefined
        }
    };

    constructor(private _scene: Scene) {
        return this;
    }

    public createSphere(id: string, origin: Vector3, size: number, segments = 4): Mesh {
        const sphere = MeshBuilder.CreateSphere(id, { diameter: size, segments: segments }, this._scene);
        sphere.position = origin;
        return sphere;
    }

    public createStartDisc(id: string, origin: Vector3, normal: Vector3, size: number, segments = 6): Mesh {
        const cylinder = MeshBuilder.CreateCylinder(
            id,
            { height: 0.001, tessellation: segments, diameter: size },
            this._scene
        );
        cylinder.position = origin;
        cylinder.setDirection(normal, 0, 1.57079633);
        return cylinder;
    }

    public createLine(id: string, origin: Vector3, target: Vector3, color: Color4): LinesMesh {
        const o = GeometryTools._tmp.twoPointLineOptions;
        o.points[0].copyFrom(origin);
        o.points[1].copyFrom(target);

        // o.colors[0].copyFrom(color);
        // o.colors[1] = o.colors[0];
        o.instance = undefined;
        const mesh = MeshBuilder.CreateLines(id, o, this._scene);
        mesh.color.copyFromFloats(color.r, color.g, color.b);
        return mesh;
    }

    public updateLine(instance: LinesMesh, origin: Vector3, target: Vector3, color: Color4): LinesMesh {
        const positions = instance.getVerticesData(VertexBuffer.PositionKind);
        if (!positions) {
            return instance;
        }
        positions[0] = origin.x;
        positions[1] = origin.y;
        positions[2] = origin.z;

        positions[3] = target.x;
        positions[4] = target.y;
        positions[5] = target.z;

        instance.updateVerticesData(VertexBuffer.PositionKind, positions, true, false);
        return instance;
    }

    public createRay(id: string, ray: Ray, color: Color3): RayHelper {
        return RayHelper.CreateAndShow(ray, this._scene, color);
    }

    public createMeshWireframe(mesh: BimIfcMesh, inWorldSpace = true, color: Color4): LinesMesh[] {
        const vd = mesh.vertexData;
        const iLen = vd.indices.length;
        const localToWorldMatrix = mesh.transform;
        const lines: LinesMesh[] = [];
        for (let i = 0; i < iLen; i += 3) {
            const i1 = vd.indices[i];
            const i2 = vd.indices[i + 1];
            const i3 = vd.indices[i + 2];

            v1.set(vd.positions[i1 * 3], vd.positions[i1 * 3 + 1], vd.positions[i1 * 3 + 2]);
            v2.set(vd.positions[i2 * 3], vd.positions[i2 * 3 + 1], vd.positions[i2 * 3 + 2]);
            v3.set(vd.positions[i3 * 3], vd.positions[i3 * 3 + 1], vd.positions[i3 * 3 + 2]);
            if (inWorldSpace) {
                Vector3.TransformCoordinatesToRef(v1, localToWorldMatrix, v1);
                Vector3.TransformCoordinatesToRef(v2, localToWorldMatrix, v2);
                Vector3.TransformCoordinatesToRef(v3, localToWorldMatrix, v3);
            } else {
                v1.scaleInPlace(0.001);
                v2.scaleInPlace(0.001);
                v3.scaleInPlace(0.001);
            }

            lines.push(this.createLine('st', v1, v2, color));
            lines.push(this.createLine('st', v1, v3, color));
            lines.push(this.createLine('st', v2, v3, color));
        }
        return lines;
    }

    public viewObjectWireFrame(object: BimIfcObject, color: Color4): LinesMesh[][] {
        const productLineMeshes: LinesMesh[][] = [];
        for (const m of object.productMeshes) {
            productLineMeshes.push(this.createMeshWireframe(object.mesh(m), true, color));
        }
        return productLineMeshes;
    }
}
