'use strict';

var poly3 = require('@jscad/modeling/src/geometries/poly3');
var booleans = require('@jscad/modeling/src/operations/booleans');

/**
 * @param compart true => t2  ,  false => t1
 * @returns 索引
 */
function Max(arr, compart)
{
    let best = arr[0];
    let bestIndex = 0;
    for (let i = 1; i < arr.length; i++) {
        let t1 = arr[i];
        if (compart(best, t1)) {
            best = t1;
            bestIndex = i;
        }
    }
    return bestIndex;
}

/** Epsilon used during determination of near zero distances.
 * @default
 */
const EPS = 5e-2;

// //////////////////////////////
//   tolerance: The maximum difference for each parameter allowed to be considered a match
class FuzzyFactory
{
    constructor(numdimensions = 3, tolerance = EPS)
    {
        this.lookuptable = {};
        this.multiplier = 1.0 / tolerance;
    }
    // let obj = f.lookupOrCreate([el1, el2, el3], function(elements) {/* create the new object */});
    // Performs a fuzzy lookup of the object with the specified elements.
    // If found, returns the existing object
    // If not found, calls the supplied callback function which should create a new object with
    // the specified properties. This object is inserted in the lookup database.
    lookupOrCreate(els, object)
    {
        let hash = "";
        let multiplier = this.multiplier;
        for (let el of els) {
            let valueQuantized = Math.round(el * multiplier);
            hash += valueQuantized + "/";
        }
        if (hash in this.lookuptable)
            return this.lookuptable[hash];
        else {
            let hashparts = els.map(el =>
            {
                let q0 = Math.floor(el * multiplier);
                let q1 = q0 + 1;
                return ["" + q0 + "/", "" + q1 + "/"];
            });
            let numelements = els.length;
            let numhashes = 1 << numelements;
            for (let hashmask = 0; hashmask < numhashes; ++hashmask) {
                let hashmaskShifted = hashmask;
                hash = "";
                hashparts.forEach(hashpart =>
                {
                    hash += hashpart[hashmaskShifted & 1];
                    hashmaskShifted >>= 1;
                });
                this.lookuptable[hash] = object;
            }
            return object;
        }
    }
}

//并集path2dCsgs 差集 删除小面积结果
function CSGSubtract(geom, path2dCsgs)
{
    let gemUnion = path2dCsgs[0];
    for (let i = 1; i < path2dCsgs.length; i++)
        gemUnion = booleans.union(gemUnion, path2dCsgs[i]);
    let newGeom = booleans.subtract(geom, gemUnion);
    //删除小面积(只留一个)
    {
        let fuzz = new FuzzyFactory;
        let vmap = new Map;
        for (let poly of newGeom.polygons) {
            for (let v of poly.vertices) {
                let key = fuzz.lookupOrCreate(v, v);
                let arr = vmap.get(key);
                if (!arr) {
                    arr = [];
                    vmap.set(key, arr);
                }
                arr.push(poly);
                v["__key__"] = key;
            }
        }
        let polys = newGeom.polygons.concat();
        let polyGroups = [];
        let calcs = new Set;
        while (polys.length) {
            let poly1 = polys.pop();
            calcs.add(poly1);
            let polyGroup = [poly1];
            polyGroups.push(polyGroup);
            for (let i = 0; i < polyGroup.length; i++) {
                let poly = polyGroup[i];
                for (let v of poly.vertices) {
                    let key = v["__key__"];
                    let arr = vmap.get(key);
                    for (let vpoly of arr) {
                        if (calcs.has(vpoly))
                            continue;
                        calcs.add(vpoly);
                        polyGroup.push(vpoly);
                    }
                }
            }
            // arrayRemoveIf(polys, poly => !calcs.has(poly)); //加上这个无法提高性能
        }
        let areas = polyGroups.map(polys =>
        {
            let area = 0;
            for (let poly of polys)
                area += poly3.measureArea(poly);
            return area;
        });
        let maxIndex = Max(areas, (t1, t2) => t2 > t1);
        newGeom.polygons = polyGroups[maxIndex];
    }
    return newGeom;
}

addEventListener("message", e =>
{
    const [path2dCsgs, geom] = e.data;
    try {
        const newGeom = CSGSubtract(geom, path2dCsgs);
        postMessage({
            status: 0,
            geom: newGeom
        });
    }
    catch (error) {
        postMessage({
            status: 1,
            error
        });
    }
});
var CSGSubtract_worker = {};

module.exports = CSGSubtract_worker;
//# sourceMappingURL=CSGSubtract.worker.cjs.map
