import { STSpace } from "./STSpace.js";
import { vec3 } from "gl-matrix";
import { GROUP_WITH_ANY } from "./constants/GROUP_WITH_ANY.js";
import { assert } from "../../../../core/assert.js";
import { GetNormal } from "./GetNormal.js";
import { NormalizeSafe } from "./NormalizeSafe.js";
import { GetPosition } from "./GetPosition.js";
import { clamp01 } from "../../../../core/math/clamp01.js";
import { v3_scale_dot_sub_normalize } from "./v3_scale_dot_sub_normalize.js";


let offset = 0;
const _buffer = new ArrayBuffer(3 * 4 * 6);

const vOs = new Float32Array(_buffer, offset, 3);
offset += 12;
const vOt = new Float32Array(_buffer, offset, 3);
offset += 12;
const n = new Float32Array(_buffer, offset, 3);
offset += 12;

const p0 = new Float32Array(_buffer, offset, 3);
offset += 12;
const p1 = new Float32Array(_buffer, offset, 3);
offset += 12;
const p2 = new Float32Array(_buffer, offset, 3);
offset += 12;

/**
 *
 * @param {STSpace} res
 * @param {number[]|Uint32Array|Int32Array} face_indices
 * @param {number} iFaces
 * @param {number[]} piTriListIn
 * @param {STriInfo[]} pTriInfos
 * @param {SMikkTSpaceContext} pContext
 * @param {number} iVertexRepresentitive
 */
export function EvalTspace(res, face_indices, iFaces, piTriListIn, pTriInfos, pContext, iVertexRepresentitive) {

    let fAngleSum = 0;

    res.vOs[0] = 0.0;
    res.vOs[1] = 0.0;
    res.vOs[2] = 0.0;

    res.vOt[0] = 0.0;
    res.vOt[1] = 0.0;
    res.vOt[2] = 0.0;

    res.fMagS = 0;
    res.fMagT = 0;


    for (let face = 0; face < iFaces; face++) {
        const f = face_indices[face];
        const tri_info = pTriInfos[f];

        // only valid triangles get to add their contribution
        if ((tri_info.iFlag & GROUP_WITH_ANY) === 0) {
            let i = -1;

            const f3 = 3 * f;

            if (piTriListIn[f3] === iVertexRepresentitive) {
                i = 0;
            } else if (piTriListIn[f3 + 1] === iVertexRepresentitive) {
                i = 1;
            } else if (piTriListIn[f3 + 2] === iVertexRepresentitive) {
                i = 2;
            }

            assert(i >= 0 && i < 3);

            // project
            const index = piTriListIn[f3 + i];

            GetNormal(n, pContext, index);

            v3_scale_dot_sub_normalize(vOs, n, tri_info.vOs);
            v3_scale_dot_sub_normalize(vOt, n, tri_info.vOt);

            const i0 = piTriListIn[f3 + (i > 0 ? (i - 1) : 2)];
            const i1 = piTriListIn[f3 + i];
            const i2 = piTriListIn[f3 + (i < 2 ? (i + 1) : 0)];

            GetPosition(p0, 0, pContext, i0);
            GetPosition(p1, 0, pContext, i1);
            GetPosition(p2, 0, pContext, i2);

            vec3.sub(p0, p0, p1);
            vec3.sub(p1, p2, p1);

            // project
            v3_scale_dot_sub_normalize(p0, n, p0);
            v3_scale_dot_sub_normalize(p1, n, p1);

            // weight contribution by the angle
            // between the two edge vectors

            const fCos = clamp01(vec3.dot(p0, p1));
            const fAngle = Math.acos(fCos);
            const fMagS = tri_info.fMagS;
            const fMagT = tri_info.fMagT;

            vec3.scale(p2, vOs, fAngle);
            vec3.add(res.vOs, res.vOs, p2);

            vec3.scale(p2, vOt, fAngle);
            vec3.add(res.vOt, res.vOt, p2);

            res.fMagS += (fAngle * fMagS);
            res.fMagT += (fAngle * fMagT);

            fAngleSum += fAngle;
        }
    }

    // normalize
    NormalizeSafe(res.vOs, res.vOs);
    NormalizeSafe(res.vOt, res.vOt);

    if (fAngleSum > 0) {
        res.fMagS /= fAngleSum;
        res.fMagT /= fAngleSum;
    }

}
