import json
import os

import FreeCAD as App
import Part


def make_feature_shape(type_id, properties):
    document = App.newDocument("GoldenPrimitive")
    try:
        feature = document.addObject(type_id, "Primitive")
        for name, value in properties.items():
            setattr(feature, name, value)
        document.recompute()
        return feature.Shape.copy()
    finally:
        App.closeDocument(document.Name)


def make_shape(operation):
    kind = operation["type"]
    if kind == "box":
        shape = Part.makeBox(operation["length"], operation["width"], operation["height"])
    elif kind == "cylinder":
        shape = Part.makeCylinder(operation["radius"], operation["height"], App.Vector(0, 0, 0), App.Vector(0, 0, 1), operation["angle"])
    elif kind == "sphere":
        shape = Part.makeSphere(operation["radius"])
    elif kind == "cone":
        shape = Part.makeCone(operation["radius1"], operation["radius2"], operation["height"], App.Vector(0, 0, 0), App.Vector(0, 0, 1), operation["angle"])
    elif kind == "ellipsoid":
        shape = make_feature_shape("Part::Ellipsoid", {
            "Radius1": operation["radius1"], "Radius2": operation["radius2"], "Radius3": operation["radius3"],
            "Angle1": operation["angle1"], "Angle2": operation["angle2"], "Angle3": operation["angle3"],
        })
    elif kind == "torus":
        shape = make_feature_shape("Part::Torus", {
            "Radius1": operation["radius1"], "Radius2": operation["radius2"],
            "Angle1": operation["angle1"], "Angle2": operation["angle2"], "Angle3": operation["angle3"],
        })
    elif kind == "prism":
        shape = make_feature_shape("Part::Prism", {
            "Polygon": operation["polygon"], "Circumradius": operation["circumradius"], "Height": operation["height"],
            "FirstAngle": operation["firstAngle"], "SecondAngle": operation["secondAngle"],
        })
    elif kind == "wedge":
        shape = make_feature_shape("Part::Wedge", {
            "Xmin": operation["xmin"], "Ymin": operation["ymin"], "Zmin": operation["zmin"],
            "Z2min": operation["z2min"], "X2min": operation["x2min"], "Xmax": operation["xmax"],
            "Ymax": operation["ymax"], "Zmax": operation["zmax"], "Z2max": operation["z2max"], "X2max": operation["x2max"],
        })
    elif kind == "helix":
        shape = make_feature_shape("Part::Helix", {
            "Pitch": operation["pitch"], "Height": operation["height"], "Radius": operation["radius"],
            "Angle": operation["angle"], "SegmentLength": operation["segmentLength"],
        })
    elif kind in ("cut", "fuse", "common"):
        base = make_shape(operation["base"])
        tool = make_shape(operation["tool"])
        shape = base.cut(tool) if kind == "cut" else base.fuse(tool) if kind == "fuse" else base.common(tool)
    else:
        raise ValueError("Unsupported golden operation: " + kind)

    placement_value = operation.get("placement")
    if placement_value:
        placement = App.Placement()
        placement.Base = App.Vector(*(placement_value.get("translation") or [0, 0, 0]))
        rotation = placement_value.get("rotation")
        if rotation:
            placement.Rotation = App.Rotation(App.Vector(*rotation["axis"]), rotation["angle"])
        shape = shape.copy()
        shape.Placement = placement
    return shape


def vector(x, y, z):
    return [float(x), float(y), float(z)]


scenario_path = os.environ.get("FREECAD_GOLDEN_SCENARIO")
if not scenario_path:
    raise RuntimeError("FREECAD_GOLDEN_SCENARIO is required")

with open(scenario_path, "r", encoding="utf-8") as scenario_file:
    scenario = json.load(scenario_file)

shape = make_shape(scenario["operation"])
box = shape.BoundBox
version = App.Version()
result = {
    "schemaVersion": 1,
    "fixtureId": scenario["id"],
    "freecadVersion": ".".join(str(value) for value in version[:3]),
    "shapeType": shape.ShapeType,
    "isNull": shape.isNull(),
    "isValid": shape.isValid(),
    "solids": len(shape.Solids),
    "faces": len(shape.Faces),
    "edges": len(shape.Edges),
    "vertices": len(shape.Vertexes),
    "volume": float(shape.Volume),
    "area": float(shape.Area),
    "boundingBox": {
        "min": vector(box.XMin, box.YMin, box.ZMin),
        "max": vector(box.XMax, box.YMax, box.ZMax),
    },
}
print("FREECAD_GOLDEN_RESULT=" + json.dumps(result, sort_keys=True, separators=(",", ":")))
