import { vec3 } from "gl-matrix";
import { BuildNeighborsFast } from "./BuildNeighborsFast.js";
import { CalcTexArea } from "./CalcTexArea.js";
import { GROUP_WITH_ANY } from "./constants/GROUP_WITH_ANY.js";
import { MARK_DEGENERATE } from "./constants/MARK_DEGENERATE.js";
import { ORIENT_PRESERVING } from "./constants/ORIENT_PRESERVING.js";
import { GetPosition } from "./GetPosition.js";
import { GetTexCoord } from "./GetTexCoord.js";
import { NotZero } from "./NotZero.js";

const t1 = vec3.create();
const t2 = vec3.create();
const t3 = vec3.create();

const d1 = vec3.create();
const d2 = vec3.create();

const vOs = vec3.create();
const vOt = vec3.create();

const v1 = vec3.create();
const v2 = vec3.create();
const v3 = vec3.create();

const v_temp_0 = vec3.create();
const v_temp_1 = vec3.create();

/**
 *
 * @param {STriInfo[]} pTriInfos
 * @param {number[]|Int32Array} piTriListIn
 * @param {SMikkTSpaceContext} pContext
 * @param {number} iNrTrianglesIn
 * @returns {void}
 */
export function InitTriInfo(pTriInfos, piTriListIn, pContext, iNrTrianglesIn) {
    let f = 0, i = 0, t = 0;
    // pTriInfos[f].iFlag is cleared in GenerateInitialVerticesIndexList() which is called before this function.

    // generate neighbor info list
    for (f = 0; f < iNrTrianglesIn; f++) {
        const tri = pTriInfos[f];

        for (i = 0; i < 3; i++) {

            tri.FaceNeighbors[i] = -1;
            tri.AssignedGroup[i] = null;
        }

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

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

        tri.fMagS = 0;
        tri.fMagT = 0;

        // assumed bad
        tri.iFlag |= GROUP_WITH_ANY;
    }


    // evaluate first order derivatives
    for (f = 0; f < iNrTrianglesIn; f++) {
        // initial values
        const f3 = f * 3;

        GetPosition(v1, 0, pContext, piTriListIn[f3 + 0]);
        GetPosition(v2, 0, pContext, piTriListIn[f3 + 1]);
        GetPosition(v3, 0, pContext, piTriListIn[f3 + 2]);

        GetTexCoord(t1, pContext, piTriListIn[f3 + 0]);
        GetTexCoord(t2, pContext, piTriListIn[f3 + 1]);
        GetTexCoord(t3, pContext, piTriListIn[f3 + 2]);

        const t21x = t2[0] - t1[0];
        const t21y = t2[1] - t1[1];

        const t31x = t3[0] - t1[0];
        const t31y = t3[1] - t1[1];

        vec3.sub(d1, v2, v1);
        vec3.sub(d2, v3, v1);

        const fSignedAreaSTx2 = t21x * t31y - t21y * t31x;
        //assert(fSignedAreaSTx2!=0);

        vec3.scale(v_temp_0, d1, t31y);
        vec3.scale(v_temp_1, d2, t21y);
        vec3.sub(vOs, v_temp_0, v_temp_1);	// eq 18


        vec3.scale(v_temp_0, d1, -t31x);
        vec3.scale(v_temp_1, d2, t21x);
        vec3.add(vOt, v_temp_0, v_temp_1); // eq 19

        const tri_info = pTriInfos[f];
        tri_info.iFlag |= (fSignedAreaSTx2 > 0 ? ORIENT_PRESERVING : 0);

        if (NotZero(fSignedAreaSTx2)) {
            const fAbsArea = Math.abs(fSignedAreaSTx2);

            const fLenOs = vec3.length(vOs);
            const fLenOt = vec3.length(vOt);

            const fS = (tri_info.iFlag & ORIENT_PRESERVING) === 0 ? (-1.0) : 1.0;

            if (NotZero(fLenOs)) {
                vec3.scale(tri_info.vOs, vOs, fS / fLenOs);
            }

            if (NotZero(fLenOt)) {
                vec3.scale(tri_info.vOt, vOt, fS / fLenOt);
            }

            // evaluate magnitudes prior to normalization of vOs and vOt
            tri_info.fMagS = fLenOs / fAbsArea;
            tri_info.fMagT = fLenOt / fAbsArea;

            // if this is a good triangle
            if (NotZero(tri_info.fMagS) && NotZero(tri_info.fMagT)) {
                tri_info.iFlag &= (~GROUP_WITH_ANY);
            }
        }
    }

    // force otherwise healthy quads to a fixed orientation
    while (t < (iNrTrianglesIn - 1)) {
        const tri_0 = pTriInfos[t];
        const tri_1 = pTriInfos[t + 1];

        const iFO_a = tri_0.iOrgFaceNumber;
        const iFO_b = tri_1.iOrgFaceNumber;

        if (iFO_a === iFO_b) {
            // this is a quad
            const bIsDeg_a = (tri_0.iFlag & MARK_DEGENERATE) !== 0;
            const bIsDeg_b = (tri_1.iFlag & MARK_DEGENERATE) !== 0;

            // bad triangles should already have been removed by
            // DegenPrologue(), but just in case check bIsDeg_a and bIsDeg_a are false
            if ((bIsDeg_a || bIsDeg_b) === false) {
                const bOrientA = (tri_0.iFlag & ORIENT_PRESERVING) !== 0;
                const bOrientB = (tri_1.iFlag & ORIENT_PRESERVING) !== 0;

                // if this happens the quad has extremely bad mapping!!
                if (bOrientA !== bOrientB) {
                    //printf("found quad with bad mapping\n");
                    let bChooseOrientFirstTri = false;

                    if ((tri_1.iFlag & GROUP_WITH_ANY) !== 0) {
                        bChooseOrientFirstTri = true;
                    } else if (CalcTexArea(pContext, piTriListIn, t * 3) >= CalcTexArea(pContext, piTriListIn, (t + 1) * 3 + 0)) {
                        bChooseOrientFirstTri = true;
                    }

                    // force match
                    const t0 = bChooseOrientFirstTri ? tri_0 : tri_1;
                    const t1 = bChooseOrientFirstTri ? tri_1 : tri_0;
                    t1.iFlag &= (~ORIENT_PRESERVING);	// clear first
                    t1.iFlag |= (t0.iFlag & ORIENT_PRESERVING);	// copy bit
                }
            }
            t += 2;
        } else {
            ++t;
        }
    }

    // match up edge pairs

    const pEdges = new Float32Array(iNrTrianglesIn * 3 * 3);
    BuildNeighborsFast(pTriInfos, pEdges, piTriListIn, iNrTrianglesIn);

}
