import json

import FreeCAD as App
import Part
import Sketcher


TOLERANCE = 1e-7


def line(start_x, start_y, end_x, end_y):
    return Part.LineSegment(App.Vector(start_x, start_y, 0), App.Vector(end_x, end_y, 0))


def point_value(point):
    return [float(point.x), float(point.y)]


def line_value(geometry):
    return {"start": point_value(geometry.StartPoint), "end": point_value(geometry.EndPoint)}


def sketch_state(sketch):
    return {
        "geometry": [line_value(geometry) for geometry in sketch.Geometry if hasattr(geometry, "StartPoint")],
        "construction": [bool(sketch.getConstruction(index)) for index in range(sketch.GeometryCount)],
        "constraintTypes": [str(constraint.Type) for constraint in sketch.Constraints],
        "degreesOfFreedom": int(sketch.DoF),
        "fullyConstrained": bool(sketch.FullyConstrained),
    }


def run_case(case_id, operation):
    document = App.newDocument("EditorOracle_" + case_id.replace("-", "_"))
    sketch = document.addObject("Sketcher::SketchObject", "Sketch")
    sketch.MapMode = "Deactivated"
    try:
        details = operation(document, sketch)
        document.recompute()
        return {"id": case_id, "passed": True, **details, "final": sketch_state(sketch)}
    except Exception as error:
        return {"id": case_id, "passed": False, "errorType": type(error).__name__, "error": str(error)}
    finally:
        App.closeDocument(document.Name)


def drag_case(document, sketch):
    sketch.addGeometry(line(0, 0, 5, 1), False)
    sketch.addConstraint(Sketcher.Constraint("Horizontal", 0))
    sketch.solve()
    before = sketch_state(sketch)
    sketch.moveGeometry(0, 2, App.Vector(8, 2, 0), False)
    solve_status = sketch.solve()
    return {"before": before, "solveStatus": int(solve_status)}


def split_case(document, sketch):
    sketch.addGeometry(line(0, 0, 10, 0), True)
    sketch.split(0, App.Vector(4, 0, 0))
    return {}


def extend_case(document, sketch):
    sketch.addGeometry(line(0, 0, 10, 0), False)
    sketch.extend(0, 5.0, 2)
    return {}


def trim_case(document, sketch):
    sketch.addGeometry([line(0, 0, 10, 0), line(7, -2, 7, 2)], False)
    sketch.trim(0, App.Vector(9, 0, 0))
    return {}


def construction_case(document, sketch):
    sketch.addGeometry(line(0, 0, 1, 0), False)
    sketch.toggleConstruction(0)
    return {}


def transaction_case(document, sketch):
    document.UndoMode = 1
    sketch.addGeometry(line(0, 0, 5, 0), False)
    document.recompute()
    document.openTransaction("Move endpoint")
    sketch.moveGeometry(0, 2, App.Vector(8, 0, 0), False)
    document.commitTransaction()
    document.recompute()
    committed = point_value(sketch.Geometry[0].EndPoint)
    document.undo()
    document.recompute()
    undone = point_value(sketch.Geometry[0].EndPoint)
    document.redo()
    document.recompute()
    redone = point_value(sketch.Geometry[0].EndPoint)
    return {"committedEnd": committed, "undoneEnd": undone, "redoneEnd": redone}


def auto_constraint_case(document, sketch):
    sketch.addGeometry([line(0, 0, 5, 0.0001), line(5.0001, 0, 5, 4)], False)
    sketch.addConstraint(Sketcher.Constraint("Coincident", 0, 2, 1, 1))
    sketch.addConstraint(Sketcher.Constraint("Horizontal", 0))
    solve_status = sketch.solve()
    return {"solveStatus": int(solve_status)}


cases = [
    run_case("drag-horizontal", drag_case),
    run_case("split-line", split_case),
    run_case("extend-line", extend_case),
    run_case("trim-at-intersection", trim_case),
    run_case("toggle-construction", construction_case),
    run_case("transaction-undo-redo", transaction_case),
    run_case("auto-constraint", auto_constraint_case),
]
report = {
    "schemaVersion": 1,
    "baselineId": "freecad-1.1.1-sketcher-editor-oracle",
    "freecadVersion": ".".join(App.Version()[0:3]),
    "gitCommit": "0108fd4b4850cc46e625b60e53cea7a7bbe69f8d",
    "tolerance": TOLERANCE,
    "status": "pass" if all(case["passed"] for case in cases) else "failed",
    "summary": {"cases": len(cases), "passed": sum(1 for case in cases if case["passed"])},
    "cases": cases,
}
print("FREECAD_SKETCHER_EDITOR_ORACLE_RESULT=" + json.dumps(report, sort_keys=True))
