export class DxfService {
    constructor(base, shapeGettersService, edgesService, wiresService) {
        this.base = base;
        this.shapeGettersService = shapeGettersService;
        this.edgesService = edgesService;
        this.wiresService = wiresService;
    }
    /**
     * Step 1: Convert OCCT shape to DXF paths (without layer/color info)
     * This analyzes the shape geometry and creates appropriate DXF segments
     */
    shapeToDxfPaths(inputs) {
        // Get all wires from the shape
        const wires = this.shapeGettersService.getWires({ shape: inputs.shape });
        const paths = [];
        wires.forEach(wire => {
            // Get edges along the wire in order with consistent direction
            const edges = this.edgesService.getEdgesAlongWire({ shape: wire });
            if (edges.length === 0) {
                return;
            }
            // Check if the entire wire is closed once at the beginning
            const isWireClosed = this.wiresService.isWireClosed({ shape: wire });
            const segments = [];
            let i = 0;
            while (i < edges.length) {
                const edge = edges[i];
                // Check if edge is a full circle - handle separately
                if (this.edgesService.isEdgeCircular({ shape: edge })) {
                    const bounds = this.edgesService.getEdgeBounds(edge);
                    const angleRange = bounds.uMax - bounds.uMin;
                    const isFullCircle = Math.abs(angleRange - 2 * Math.PI) < 0.01;
                    if (isFullCircle) {
                        const circle = this.edgesService.getCircularEdgeCenterPoint({ shape: edge });
                        const radius = this.edgesService.getCircularEdgeRadius({ shape: edge });
                        segments.push({
                            center: [circle[0], circle[2]],
                            radius: radius
                        });
                        i++;
                        continue;
                    }
                }
                // Try to collect all edges (linear, arc, and complex) into a unified polyline with bulges
                const polylineResult = this.tryCreateUnifiedPolyline(edges, i, inputs, isWireClosed && i === 0);
                if (polylineResult) {
                    segments.push(polylineResult.polyline);
                    i = polylineResult.nextIndex;
                    continue;
                }
                // Fallback: shouldn't reach here normally
                i++;
            }
            // Create path from segments
            if (segments.length > 0) {
                paths.push({
                    segments: segments
                });
            }
        });
        return paths;
    }
    /**
     * Step 2: Add layer and color information to DXF paths
     * Takes paths from shapeToDxfPaths and adds styling
     */
    dxfPathsWithLayer(inputs) {
        return {
            layer: inputs.layer,
            color: inputs.color,
            paths: inputs.paths
        };
    }
    /**
     * Step 3: Assemble multiple path parts into a complete DXF file
     * Takes multiple outputs from dxfPathsWithLayer and creates final DXF
     */
    dxfCreate(inputs) {
        const model = {
            dxfPathsParts: inputs.pathsParts
        };
        const dxfContent = this.base.io.dxf.dxfCreate(model);
        return dxfContent;
    }
    /**
     * Try to create a unified polyline from consecutive edges (linear, arc, and complex)
     * This creates a single LWPOLYLINE with bulges where applicable
     */
    tryCreateUnifiedPolyline(edges, startIndex, inputs, shouldBeClosed) {
        const points = [];
        const bulges = [];
        let j = startIndex;
        while (j < edges.length) {
            const currentEdge = edges[j];
            const isLinear = this.edgesService.isEdgeLinear({ shape: currentEdge });
            const isCircular = this.edgesService.isEdgeCircular({ shape: currentEdge });
            // Check if it's a full circle - stop here
            if (isCircular) {
                const bounds = this.edgesService.getEdgeBounds(currentEdge);
                const angleRange = bounds.uMax - bounds.uMin;
                if (Math.abs(angleRange - 2 * Math.PI) < 0.01) {
                    break; // Full circle, handle separately
                }
            }
            const startPt = this.edgesService.startPointOnEdge({ shape: currentEdge });
            const endPt = this.edgesService.endPointOnEdge({ shape: currentEdge });
            if (isLinear) {
                // Linear edge: add start point with bulge 0
                points.push([startPt[0], startPt[2]]);
                bulges.push(0);
            }
            else if (isCircular) {
                // Arc edge: add start point with calculated bulge
                points.push([startPt[0], startPt[2]]);
                // DXF bulge is the tangent of 1/4 of the included angle
                // Positive bulge = arc curves to the left when traveling from start to end (CCW)
                // Negative bulge = arc curves to the right when traveling from start to end (CW)
                // Sample a point in the middle of the arc to determine direction
                const midParam = 0.5; // Middle of the arc
                const midPt3d = this.edgesService.pointOnEdgeAtParam({ shape: currentEdge, param: midParam });
                // Calculate the included angle from the actual arc geometry
                // We map from 3D (X,Y,Z) to 2D DXF (X,Y) by dropping the Y coordinate
                // So: 3D X → DXF X, 3D Z → DXF Y
                const center = this.edgesService.getCircularEdgeCenterPoint({ shape: currentEdge });
                // Calculate angles in the 2D projection (XZ plane → XY in DXF)
                const startAngle = Math.atan2(startPt[2] - center[2], startPt[0] - center[0]);
                const midAngle = Math.atan2(midPt3d[2] - center[2], midPt3d[0] - center[0]);
                const endAngle = Math.atan2(endPt[2] - center[2], endPt[0] - center[0]);
                // Calculate the included angle by checking which direction the arc actually goes
                // We use the midpoint to determine if we're going CCW or CW
                let angle1 = endAngle - startAngle;
                let angle2 = midAngle - startAngle;
                // Normalize angles to [-π, π] using modulo arithmetic
                angle1 = Math.atan2(Math.sin(angle1), Math.cos(angle1));
                angle2 = Math.atan2(Math.sin(angle2), Math.cos(angle2));
                // Check if midpoint is on the path from start to end
                // Both should have the same sign and mid should be about half of end
                let includedAngle;
                if (Math.sign(angle1) === Math.sign(angle2) && Math.abs(angle2) < Math.abs(angle1) + 0.1) {
                    // Midpoint is on the short arc from start to end
                    includedAngle = angle1;
                }
                else {
                    // Midpoint is on the long arc, so we go the other way
                    includedAngle = angle1 > 0 ? angle1 - 2 * Math.PI : angle1 + 2 * Math.PI;
                }
                // The bulge is tan(included_angle / 4)
                // Positive angle = CCW = positive bulge (arc curves left)
                // Negative angle = CW = negative bulge (arc curves right)
                const bulge = Math.tan(includedAngle / 4);
                bulges.push(bulge);
            }
            else {
                // Complex edge: tessellate and add points with bulge 0
                const points3d = this.edgesService.edgeToPoints({
                    shape: currentEdge,
                    angularDeflection: inputs.angularDeflection,
                    curvatureDeflection: inputs.curvatureDeflection,
                    minimumOfPoints: inputs.minimumOfPoints,
                    uTolerance: inputs.uTolerance,
                    minimumLength: inputs.minimumLength
                });
                // Add all tessellation points except the last one for all edges
                // The last point of each edge will be the start point of the next edge
                // For closed wires, the last edge's last point equals the first edge's first point
                for (let k = 0; k < points3d.length - 1; k++) {
                    points.push([points3d[k][0], points3d[k][2]]);
                    bulges.push(0);
                }
            }
            j++;
        }
        // Add the final endpoint for open polylines
        if (j > startIndex && !shouldBeClosed) {
            const lastEdge = edges[j - 1];
            const endPt = this.edgesService.endPointOnEdge({ shape: lastEdge });
            points.push([endPt[0], endPt[2]]);
            bulges.push(0);
        }
        // Return null if we didn't process any edges
        if (j <= startIndex || points.length < 2) {
            return null;
        }
        const polyline = {
            points: points,
            closed: shouldBeClosed,
            bulges: bulges
        };
        return {
            polyline: polyline,
            nextIndex: j
        };
    }
    /**
     * Try to create a polyline with bulges from consecutive line/arc segments
     * Returns the polyline and next index, or null if not applicable
     */
    tryCreatePolylineWithBulges(edges, startIndex, shouldBeClosed) {
        const edge = edges[startIndex];
        // Must start with either a line or an arc (not a full circle)
        const isLinear = this.edgesService.isEdgeLinear({ shape: edge });
        const isCircular = this.edgesService.isEdgeCircular({ shape: edge });
        if (!isLinear && !isCircular) {
            return null; // Complex edge, can't create polyline with bulges
        }
        // Check if it's a full circle
        if (isCircular) {
            const bounds = this.edgesService.getEdgeBounds(edge);
            const angleRange = bounds.uMax - bounds.uMin;
            const isFullCircle = Math.abs(angleRange - 2 * Math.PI) < 0.01;
            if (isFullCircle) {
                return null; // Full circle, handle separately
            }
        }
        // Collect consecutive linear and arc edges
        const points = [];
        const bulges = [];
        let j = startIndex;
        while (j < edges.length) {
            const currentEdge = edges[j];
            const isCurrentLinear = this.edgesService.isEdgeLinear({ shape: currentEdge });
            const isCurrentCircular = this.edgesService.isEdgeCircular({ shape: currentEdge });
            // Stop if we hit a complex edge
            if (!isCurrentLinear && !isCurrentCircular) {
                break;
            }
            // Stop if we hit a full circle
            if (isCurrentCircular) {
                const bounds = this.edgesService.getEdgeBounds(currentEdge);
                const angleRange = bounds.uMax - bounds.uMin;
                if (Math.abs(angleRange - 2 * Math.PI) < 0.01) {
                    break;
                }
            }
            const startPt = this.edgesService.startPointOnEdge({ shape: currentEdge });
            points.push([startPt[0], startPt[2]]); // XZ plane
            if (isCurrentLinear) {
                // Linear segment: bulge = 0
                bulges.push(0);
            }
            else {
                // Arc segment: calculate bulge
                const endPt = this.edgesService.endPointOnEdge({ shape: currentEdge });
                const center = this.edgesService.getCircularEdgeCenterPoint({ shape: currentEdge });
                // Calculate the included angle using the actual arc geometry
                const startAngle = Math.atan2(startPt[2] - center[2], startPt[0] - center[0]);
                const endAngle = Math.atan2(endPt[2] - center[2], endPt[0] - center[0]);
                let includedAngle = endAngle - startAngle;
                // Normalize to [-2π, 2π]
                while (includedAngle > Math.PI)
                    includedAngle -= 2 * Math.PI;
                while (includedAngle < -Math.PI)
                    includedAngle += 2 * Math.PI;
                // Bulge = tan(included_angle / 4)
                const bulge = Math.tan(includedAngle / 4);
                bulges.push(bulge);
            }
            j++;
        }
        // Add the end point of the last edge
        if (j > startIndex) {
            const lastEdge = edges[j - 1];
            const endPt = this.edgesService.endPointOnEdge({ shape: lastEdge });
            points.push([endPt[0], endPt[2]]); // XZ plane
            bulges.push(0); // Last vertex doesn't need a bulge
        }
        // Only create polyline with bulges if we have at least 2 edges or if it's beneficial
        if (j - startIndex < 2) {
            return null; // Not enough edges to justify polyline
        }
        const polyline = {
            points: points,
            closed: shouldBeClosed,
            bulges: bulges
        };
        return {
            polyline: polyline,
            nextIndex: j
        };
    }
}
