import json
import os

import FreeCAD as App


FREECAD_COMMIT = "0108fd4b4850cc46e625b60e53cea7a7bbe69f8d"


def shape_snapshot(obj):
    shape = obj.Shape
    bounds = shape.BoundBox
    return {
        "name": obj.Name,
        "typeId": obj.TypeId,
        "valid": bool(shape.isValid()),
        "shapeType": shape.ShapeType,
        "solids": len(shape.Solids),
        "faces": len(shape.Faces),
        "edges": len(shape.Edges),
        "vertices": len(shape.Vertexes),
        "volume": round(float(shape.Volume), 9),
        "area": round(float(shape.Area), 9),
        "bounds": [
            round(float(value), 9)
            for value in (bounds.XMin, bounds.YMin, bounds.ZMin, bounds.XMax, bounds.YMax, bounds.ZMax)
        ],
    }


def measure_snapshot(document):
    measure = document.getObject("AreaProbe")
    box = document.getObject("AreaBox")
    return {
        "objectSet": [{"name": obj.Name, "typeId": obj.TypeId} for obj in document.Objects],
        "source": {
            **shape_snapshot(box),
            "length": round(float(box.Length.Value), 9),
            "width": round(float(box.Width.Value), 9),
            "height": round(float(box.Height.Value), 9),
        },
        "measure": {
            "name": measure.Name,
            "typeId": measure.TypeId,
            "areaTypeId": measure.getTypeIdOfProperty("Area"),
            "elementsTypeId": measure.getTypeIdOfProperty("Elements"),
            "area": {"raw": str(measure.Area), "numeric": round(float(measure.Area.Value), 9), "unit": str(measure.Area.Unit)},
            "elements": [
                {"object": obj.Name, "typeId": obj.TypeId, "subElements": list(sub_elements)}
                for obj, sub_elements in measure.Elements
            ],
            "propertyStatus": [str(item) for item in measure.getPropertyStatus("Area")],
            "editorMode": [str(item) for item in measure.getEditorMode("Area")],
            "state": [str(item) for item in measure.State],
            "statusString": str(measure.getStatusString()),
            "shape": {"applicable": hasattr(measure, "Shape")},
        },
    }


def create_native(path):
    document = App.newDocument("PropertyAreaRoundtrip")
    try:
        box = document.addObject("Part::Box", "AreaBox")
        box.Length = 10
        box.Width = 5
        box.Height = 2
        measure = document.addObject("Measure::MeasureArea", "AreaProbe")
        measure.Elements = [(box, ["Face1"])]
        document.recompute()
        document.saveAs(path)
    finally:
        App.closeDocument(document.Name)
    reopened = App.openDocument(path)
    try:
        reopened.recompute()
        return measure_snapshot(reopened)
    finally:
        App.closeDocument(reopened.Name)


def verify_native(path, resaved_path):
    document = App.openDocument(path)
    try:
        document.recompute()
        reopened = measure_snapshot(document)
        document.saveAs(resaved_path)
    finally:
        App.closeDocument(document.Name)
    resaved_document = App.openDocument(resaved_path)
    try:
        resaved_document.recompute()
        resaved = measure_snapshot(resaved_document)
    finally:
        App.closeDocument(resaved_document.Name)
    return {"reopened": reopened, "resaved": resaved}


mode = os.environ.get("FREECAD_PROPERTY_AREA_ROUNDTRIP_MODE", "")
path = os.environ.get("FREECAD_PROPERTY_AREA_ROUNDTRIP_PATH", "")
resaved_path = os.environ.get("FREECAD_PROPERTY_AREA_ROUNDTRIP_RESAVED_PATH", "")
if mode == "create":
    result = create_native(path)
elif mode == "verify":
    result = verify_native(path, resaved_path)
else:
    raise RuntimeError("FREECAD_PROPERTY_AREA_ROUNDTRIP_MODE must be create or verify")

print("FREECAD_PROPERTY_AREA_ROUNDTRIP_RESULT=" + json.dumps({
    "schemaVersion": 1,
    "status": "pass",
    "baselineId": "freecad-1.1.1-property-area-roundtrip",
    "freecadVersion": ".".join(str(value) for value in App.Version()[:3]),
    "gitCommit": FREECAD_COMMIT,
    "mode": mode,
    "result": result,
}, sort_keys=True))
