import { query_edge_is_boundary } from "../../../../core/geom/3d/topology/query/query_edge_is_boundary.js";
import { query_edge_other_vertex } from "../../../../core/geom/3d/topology/query/query_edge_other_vertex.js";
import { TopoMesh } from "../../../../core/geom/3d/topology/struct/TopoMesh.js";

/**
 * Move UVs to reduce stretching
 *
 * @param {Float64Array|Float32Array|number[]} positions
 * @param {Float64Array|Float32Array|number[]} uvs
 * @param {Uint16Array|Uint32Array|number[]|undefined} faces
 * @param {number} [steps] Number of optimization passes to perform
 */
export function geometry_optimize_uv_tension(
    positions,
    uvs,
    faces,
    steps = 3
) {

    // Build topology from the data, we need to know which faces are connected to each vertex
    const topo = new TopoMesh();

    topo.build(positions, faces);
    topo.computeEdgeSquaredLengths();

    const vertex_count = positions.length / 3;

    for (let i = 0; i < steps; i++) {

        loop_vertices: for (let j = 0; j < vertex_count; j++) {

            const v0 = topo.vertices[j];

            // 1) get attached edges

            const attached_edges = v0.edges;

            const attached_edge_count = attached_edges.length;

            if (attached_edge_count === 0) {
                continue;
            }

            // Compute neighbour UV weight based on edge length

            let sum_u = 0;
            let sum_v = 0;

            let weight_sum = 0;

            for (let k = 0; k < attached_edge_count; k++) {

                const edge = attached_edges[k];

                if (query_edge_is_boundary(edge)) {
                    // edge vertex, no adjustment possible
                    continue loop_vertices;
                }

                const v1 = query_edge_other_vertex(edge, v0);

                const other_uv_address = v1.index * 2;

                const u = uvs[other_uv_address];
                const v = uvs[other_uv_address + 1];

                const weight = 1 / edge.lengthSqr;

                sum_u += u * weight;
                sum_v += v * weight;

                weight_sum += weight;
            }

            // compute new UV position to equalize the vertex distance
            const u = sum_u / weight_sum;
            const v = sum_v / weight_sum;

            const uv_address = v0.index * 2;

            uvs[uv_address] = u;
            uvs[uv_address + 1] = v;
        }
    }
}
