"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.PolyFillType = exports.ClipType = void 0;
exports.combineStack = combineStack;
exports.combine = combine;
exports.removeOverlap = removeOverlap;
const tslib_1 = require("tslib");
const clipperLib = tslib_1.__importStar(require("js-angusj-clipper"));
const bindings_1 = require("../../bindings");
const slice_arc_1 = require("../shared/slice-arc");
const intersections_1 = require("./intersections");
const rebuild_1 = require("./rebuild");
const to_poly_1 = require("./to-poly");
function combineStack(ops, resolution = 256) {
    return combineStackImpl([...ops], resolution);
}
function combine(op, s1, s2, rule1, rule2, resolution = 256) {
    return combineStackImpl([
        { type: "operand", shape: s1, fillType: rule1 },
        { type: "operand", shape: s2, fillType: rule2 },
        { type: "operator", operator: op },
    ], resolution);
}
function combineStackImpl(ops, resolution) {
    const clipperLibInstance = bindings_1.Bindings.clipperLib;
    if (!clipperLibInstance)
        throw new Error("ClipperLib instance not loaded. Call `await typoGeom.Init()` first.");
    for (let i = 0; i < ops.length; i++) {
        const op = ops[i];
        if (op.type !== "operand")
            continue;
        const rectifiedArcs = ToBez3Slices(op.shape);
        ops[i] = {
            type: "operand_rectified",
            fillType: op.fillType,
            shape_rectified: rectifiedArcs,
            intersectionTs: (0, intersections_1.findSelfIntersections)(rectifiedArcs),
        };
    }
    for (let i = 0; i < ops.length; i++) {
        const opI = ops[i];
        if (opI.type !== "operand_rectified")
            continue;
        for (let j = i; j < ops.length; j++) {
            const opJ = ops[j];
            if (opJ.type !== "operand_rectified")
                continue;
            (0, intersections_1.findCrossIntersections)(opI.shape_rectified, opJ.shape_rectified, opI.intersectionTs, opJ.intersectionTs, i === j);
        }
    }
    const segHash = new to_poly_1.SegHashStore();
    for (let i = 0; i < ops.length; i++) {
        const op = ops[i];
        if (op.type !== "operand_rectified")
            continue;
        ops[i] = {
            type: "operand_poly",
            shape_poly: (0, to_poly_1.toPoly)(op.shape_rectified, i + 1, op.intersectionTs, segHash, resolution),
            fillType: op.fillType,
        };
    }
    const stack = [];
    for (const op of ops) {
        if (op.type === "operand_poly") {
            stack.push({ operand: op.shape_poly, fillType: op.fillType });
        }
        else if (op.type === "operator") {
            const x2 = stack.pop();
            const x1 = stack.pop();
            if (!x1 || !x2)
                throw new Error("Unreachable: Not enough operands for operator");
            try {
                const solutionPaths = clipperLibInstance.clipToPaths({
                    clipType: op.operator,
                    subjectFillType: x1.fillType,
                    subjectInputs: [
                        {
                            data: x1.operand,
                            closed: true,
                        },
                    ],
                    clipFillType: x2.fillType,
                    clipInputs: [
                        {
                            data: x2.operand,
                        },
                    ],
                });
                stack.push({ operand: solutionPaths, fillType: clipperLib.PolyFillType.NonZero });
            }
            catch (_) {
                const cleanedX1 = clipperLibInstance.cleanPolygons(x1.operand, 1);
                const cleanedX2 = clipperLibInstance.cleanPolygons(x2.operand, 1);
                const isX1Empty = (0, to_poly_1.PolySetIsEmpty)(cleanedX1);
                const isX2Empty = (0, to_poly_1.PolySetIsEmpty)(cleanedX2);
                if (isX1Empty && isX2Empty) {
                    stack.push({ operand: [], fillType: clipperLib.PolyFillType.NonZero });
                    continue;
                }
                else if (isX1Empty) {
                    if (op.operator === clipperLib.ClipType.Union ||
                        op.operator === clipperLib.ClipType.Xor) {
                        stack.push(x2);
                        continue;
                    }
                    else {
                        stack.push({ operand: [], fillType: clipperLib.PolyFillType.NonZero });
                        continue;
                    }
                }
                else if (isX2Empty) {
                    if (op.operator === clipperLib.ClipType.Union ||
                        op.operator === clipperLib.ClipType.Xor ||
                        op.operator === clipperLib.ClipType.Difference) {
                        stack.push(x1);
                        continue;
                    }
                    else {
                        stack.push({ operand: [], fillType: clipperLib.PolyFillType.NonZero });
                        continue;
                    }
                }
                else {
                    const solutionPaths = clipperLibInstance.clipToPaths({
                        clipType: op.operator,
                        subjectFillType: x1.fillType,
                        subjectInputs: [
                            {
                                data: cleanedX1,
                                closed: true,
                            },
                        ],
                        clipFillType: x2.fillType,
                        clipInputs: [
                            {
                                data: cleanedX2,
                            },
                        ],
                    });
                    stack.push({
                        operand: solutionPaths,
                        fillType: clipperLib.PolyFillType.NonZero,
                    });
                }
                console.log(cleanedX1, cleanedX2);
            }
        }
    }
    const result = stack.pop();
    if (!result)
        throw new Error("Unreachable: No result on stack");
    for (const path of result.operand)
        path.reverse();
    return (0, rebuild_1.rebuildShape)(result.operand, segHash, resolution);
}
function removeOverlapImpl(s1, rule, resolution = 256) {
    const clipperLibInstance = bindings_1.Bindings.clipperLib;
    if (!clipperLibInstance)
        throw new Error("ClipperLib instance not loaded. Call `await typoGeom.Init()` first.");
    if (!s1.length)
        return s1;
    const i1 = (0, intersections_1.findSelfIntersections)(s1);
    (0, intersections_1.findCrossIntersections)(s1, s1, i1, i1, true);
    const segHash = new to_poly_1.SegHashStore();
    const p1 = (0, to_poly_1.toPoly)(s1, 1, i1, segHash, resolution);
    const solution_paths = clipperLibInstance.simplifyPolygons(p1, rule);
    for (const path of solution_paths)
        path.reverse();
    return (0, rebuild_1.rebuildShape)(solution_paths, segHash, resolution);
}
function ToBez3Slices(shape) {
    const result = [];
    for (const contour of shape) {
        const resultContour = [];
        for (let j = 0; j < contour.length; j++) {
            resultContour.push(new slice_arc_1.Bez3Slice(contour[j].a, contour[j].b, contour[j].c, contour[j].d));
        }
        result.push(resultContour);
    }
    return result;
}
function removeOverlap(s1, rule, RESOLUTION = 256) {
    return removeOverlapImpl(ToBez3Slices(s1), rule, RESOLUTION);
}
var js_angusj_clipper_1 = require("js-angusj-clipper");
Object.defineProperty(exports, "ClipType", { enumerable: true, get: function () { return js_angusj_clipper_1.ClipType; } });
Object.defineProperty(exports, "PolyFillType", { enumerable: true, get: function () { return js_angusj_clipper_1.PolyFillType; } });
