import hashlib
import json
import xml.etree.ElementTree as ET

import FreeCAD as App
import Robot


FREECAD_COMMIT = "0108fd4b4850cc46e625b60e53cea7a7bbe69f8d"


def quantity_snapshot(value):
    return {
        "raw": str(value),
        "numeric": round(float(value.Value), 9),
        "unit": str(value.Unit),
    }


def trajectory_snapshot(obj):
    content = obj.Trajectory.Content
    root = ET.fromstring(content)
    waypoints = []
    for waypoint in root.findall("Waypoint"):
        waypoints.append({
            "name": waypoint.attrib["name"],
            "type": waypoint.attrib["type"],
            "position": [round(float(waypoint.attrib[key]), 9) for key in ("Px", "Py", "Pz")],
            "velocity": round(float(waypoint.attrib["vel"]), 9),
            "acceleration": round(float(waypoint.attrib["acc"]), 9),
            "continuous": waypoint.attrib["cont"] == "1",
            "tool": int(waypoint.attrib["tool"]),
            "base": int(waypoint.attrib["base"]),
        })
    return {
        "contentSha256": hashlib.sha256(content.encode("utf-8")).hexdigest(),
        "waypointCount": len(waypoints),
        "length": round(float(obj.Trajectory.Length), 9),
        "duration": round(float(obj.Trajectory.Duration), 9),
        "waypoints": waypoints,
    }


def semantic_snapshot(obj):
    return {
        "value": quantity_snapshot(obj.Acceleration),
        "propertyStatus": [str(item) for item in obj.getPropertyStatus("Acceleration")],
        "editorMode": [str(item) for item in obj.getEditorMode("Acceleration")],
        "source": {"name": obj.Source.Name, "typeId": obj.Source.TypeId},
        "useAcceleration": bool(obj.UseAcceleration),
        "trajectory": trajectory_snapshot(obj),
        "shape": {"applicable": hasattr(obj, "Shape")},
        "objectState": [str(item) for item in obj.State],
        "statusString": str(obj.getStatusString()),
        "mustExecute": bool(obj.MustExecute),
    }


def object_snapshot(document):
    return [{"name": obj.Name, "typeId": obj.TypeId} for obj in document.Objects]


def run():
    document = App.newDocument("PropertyAccelerationMutation")
    try:
        source = document.addObject("Robot::TrajectoryObject", "SourceTrajectory")
        trajectory = source.Trajectory
        trajectory = trajectory.insertWaypoints(App.Placement(App.Vector(1.0, 2.0, 3.0), App.Rotation()))
        trajectory = trajectory.insertWaypoints(App.Placement(App.Vector(4.0, 6.0, 3.0), App.Rotation()))
        source.Trajectory = trajectory

        obj = document.addObject("Robot::TrajectoryDressUpObject", "AccelerationProbe")
        obj.Source = source
        obj.UseAcceleration = True
        initial_recompute = int(document.recompute())
        before = semantic_snapshot(obj)
        source_before = trajectory_snapshot(source)
        objects_before = object_snapshot(document)

        obj.Acceleration = "500 mm/s^2"
        touched_after_edit = {
            "value": quantity_snapshot(obj.Acceleration),
            "objectState": [str(item) for item in obj.State],
            "mustExecute": bool(obj.MustExecute),
        }
        edit_recompute = int(document.recompute())
        edited = semantic_snapshot(obj)
        source_edited = trajectory_snapshot(source)

        obj.Acceleration = "1000 mm/s^2"
        restore_recompute = int(document.recompute())
        restored = semantic_snapshot(obj)
        source_restored = trajectory_snapshot(source)
        objects_restored = object_snapshot(document)

        return {
            "schemaVersion": 1,
            "status": "pass",
            "baselineId": "freecad-1.1.1-property-acceleration-mutation",
            "freecadVersion": ".".join(str(value) for value in App.Version()[:3]),
            "gitCommit": FREECAD_COMMIT,
            "object": {"name": obj.Name, "typeId": obj.TypeId},
            "property": {"name": "Acceleration", "typeId": obj.getTypeIdOfProperty("Acceleration")},
            "recompute": {
                "initial": initial_recompute,
                "edit": edit_recompute,
                "restore": restore_recompute,
            },
            "touchedAfterEdit": touched_after_edit,
            "before": before,
            "edited": edited,
            "restored": restored,
            "source": {
                "before": source_before,
                "edited": source_edited,
                "restored": source_restored,
                "preserved": source_before == source_edited == source_restored,
            },
            "document": {
                "objectsBefore": objects_before,
                "objectsRestored": objects_restored,
                "objectsPreserved": objects_before == objects_restored,
            },
            "classification": {
                "propertyChanged": before["value"] != edited["value"],
                "trajectoryChanged": before["trajectory"] != edited["trajectory"],
                "propertyRestored": before["value"] == restored["value"],
                "trajectoryRestored": before["trajectory"] == restored["trajectory"],
                "semanticStateRestored": before == restored,
                "geometry": "not-applicable-no-shape-property",
            },
        }
    finally:
        App.closeDocument(document.Name)


print("FREECAD_PROPERTY_ACCELERATION_MUTATION_RESULT=" + json.dumps(run(), sort_keys=True))
