import { QUAD_ONE_DEGEN_TRI } from "./constants/QUAD_ONE_DEGEN_TRI.js";
import { vec3 } from "gl-matrix";
import { GetPosition } from "./GetPosition.js";
import { MakeIndex } from "./MakeIndex.js";
import { assert } from "../../../../core/assert.js";

/**
 * @type {vec3}
 */
const vDstP = vec3.create();
const vSrcP = vec3.create();

class VertReverseLookupContext {
    /**
     *
     * @type {boolean}
     */
    bIsInitialized = false;
    /**
     * int*
     * @type {Int32Array|null}
     */
    pLookup = null;
    /**
     * int
     * @type {number}
     */
    iMaxVertIndex = 0;
}

/**
 *
 * @param {number[]} piTriListIn
 * @param {number} iNrTrianglesIn
 * @param {VertReverseLookupContext} pLookupCtx
 */
function GenerateReverseLookup(
    piTriListIn,
    iNrTrianglesIn,
    pLookupCtx
) {
    let t;

    // Figure out what size of lookup array we need.
    pLookupCtx.iMaxVertIndex = -1;

    for (t = 0; t < 3 * iNrTrianglesIn; t++) {
        const iVertIndex = piTriListIn[t];

        if (iVertIndex > pLookupCtx.iMaxVertIndex) {
            pLookupCtx.iMaxVertIndex = iVertIndex;
        }

    }
    // Allocate memory.
    if (pLookupCtx.iMaxVertIndex < 1) {
        // Nothing to allocate, all triangles are degenerate.
        return;
    }
    pLookupCtx.pLookup = new Int32Array(pLookupCtx.iMaxVertIndex + 1);

    // Fill in lookup.
    for (t = 0; t <= pLookupCtx.iMaxVertIndex; t++) {
        pLookupCtx.pLookup[t] = -1;
    }

    for (t = 0; t < 3 * iNrTrianglesIn; t++) {
        const iVertIndex = piTriListIn[t];

        if (pLookupCtx.pLookup[iVertIndex] !== -1) {
            continue;
        }

        pLookupCtx.pLookup[iVertIndex] = t;
    }
}

/**
 *
 * @param {VertReverseLookupContext} pLookupCtx
 * @param {number[]} piTriListIn
 * @param {number} iNrTrianglesIn
 * @param {number} iVertexIndex
 * @returns {number}
 */
function LookupVertexIndexFromGoodTriangle(
    pLookupCtx,
    piTriListIn,
    iNrTrianglesIn,
    iVertexIndex
) {
    // Allocate lookup on demand.
    if (!pLookupCtx.bIsInitialized) {
        GenerateReverseLookup(piTriListIn, iNrTrianglesIn, pLookupCtx);
        pLookupCtx.bIsInitialized = true;
    }
    // Make sure vertex index is in the mapping.
    if (iVertexIndex > pLookupCtx.iMaxVertIndex) {
        return -1;
    }
    if (pLookupCtx.pLookup === null) {
        return -1;
    }

    // Perform actual lookup.
    return pLookupCtx.pLookup[iVertexIndex];
}

/**
 *
 * @param {STSpace[]} psTspace
 * @param {STriInfo[]} pTriInfos
 * @param {number[]|Int32Array} piTriListIn
 * @param {SMikkTSpaceContext} pContext
 * @param {number} iNrTrianglesIn
 * @param {number} iTotTris
 * @returns {void}
 */
export function DegenEpilogue(psTspace, pTriInfos, piTriListIn, pContext, iNrTrianglesIn, iTotTris) {

    // deal with degenerate triangles
    // punishment for degenerate triangles is O(N^2)
    const lookupCtx = new VertReverseLookupContext();

    for (let t = iNrTrianglesIn; t < iTotTris; t++) {
        const tri0 = pTriInfos[t];

        // degenerate triangles on a quad with one good triangle are skipped
        // here but processed in the next loop
        const bSkip = (tri0.iFlag & QUAD_ONE_DEGEN_TRI) !== 0;

        if (bSkip) {
            continue;
        }

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

            const index1 = piTriListIn[t * 3 + i];
            const j = LookupVertexIndexFromGoodTriangle(lookupCtx, piTriListIn, iNrTrianglesIn, index1);

            if (j < 0) {
                // Matching vertex from good triangle is not found.
                continue;
            }

            const iTri = Math.floor(j / 3);
            const iVert = j % 3;

            const tri1 = pTriInfos[iTri];

            const iSrcVert = tri1.vert_num[iVert];
            const iSrcOffs = tri1.iTSpacesOffs;

            const iDstVert = tri0.vert_num[i];
            const iDstOffs = tri0.iTSpacesOffs;

            // copy tspace
            const dst_tspace = psTspace[iDstOffs + iDstVert];
            const src_tspace = psTspace[iSrcOffs + iSrcVert];

            dst_tspace.copy(src_tspace);
        }
    }


    // deal with degenerate quads with one good triangle
    for (let t = 0; t < iNrTrianglesIn; t++) {
        // this triangle belongs to a quad where the
        // other triangle is degenerate
        const tri = pTriInfos[t];

        if ((tri.iFlag & QUAD_ONE_DEGEN_TRI) === 0) {
            continue;
        }

        const pV = tri.vert_num;

        const iFlag = (1 << pV[0]) | (1 << pV[1]) | (1 << pV[2]);

        let iMissingIndex = 0;

        if ((iFlag & 2) === 0) {
            iMissingIndex = 1;
        } else if ((iFlag & 4) === 0) {
            iMissingIndex = 2;
        } else if ((iFlag & 8) === 0) {
            iMissingIndex = 3;
        }

        const iOrgF = tri.iOrgFaceNumber;

        GetPosition(vDstP, 0, pContext, MakeIndex(iOrgF, iMissingIndex));

        let bNotFound = true;
        let i = 0;

        while (i < 3) {

            const iVert = pV[i];
            GetPosition(vSrcP, 0, pContext, MakeIndex(iOrgF, iVert));

            if (vec3.exactEquals(vSrcP, vDstP)) {

                const iOffs = tri.iTSpacesOffs;

                psTspace[iOffs + iMissingIndex].copy(psTspace[iOffs + iVert]);

                bNotFound = false;

                break;

            } else {
                ++i;
            }

        }

        assert(!bNotFound);
    }
}
