int convert_to_ifc(const TopoDS_Wire& wire, IfcSchema::IfcLoop*& loop, bool advanced) {
	bool polygonal = true;
	for (TopExp_Explorer exp(wire, TopAbs_EDGE); exp.More(); exp.Next()) {
		double a, b;
		Handle_Geom_Curve crv = BRep_Tool::Curve(TopoDS::Edge(exp.Current()), a, b);
		if (crv.IsNull()) {
			continue;
		}
		if (crv->DynamicType() != STANDARD_TYPE(Geom_Line)) {
			polygonal = false;
			break;
		}
	}
	if (!polygonal && !advanced) {
		return 0;
	} else if (polygonal && !advanced) {
		IfcSchema::IfcCartesianPoint::list::ptr points(new IfcSchema::IfcCartesianPoint::list);
		BRepTools_WireExplorer exp(wire);
		IfcSchema::IfcCartesianPoint* p;
		for (; exp.More(); exp.Next()) {
			if (convert_to_ifc(exp.CurrentVertex(), p, advanced)) {
				points->push(p);
			} else {
				return 0;
			}
		}
		loop = new IfcSchema::IfcPolyLoop(points);
		return 1;
	} else {
		IfcSchema::IfcOrientedEdge::list::ptr edges(new IfcSchema::IfcOrientedEdge::list);
		BRepTools_WireExplorer exp(wire);
		for (; exp.More(); exp.Next()) {
			IfcSchema::IfcEdge* edge;
			// With advanced set to true convert_to_ifc(TopoDS_Edge&) will always create an IfcOrientedEdge
			if (!convert_to_ifc(exp.Current(), edge, true)) {
				double a, b;
				if (BRep_Tool::Curve(TopoDS::Edge(exp.Current()), a, b).IsNull()) {
					continue;
				} else {
					return 0;
				}
			}
			edges->push(edge->as<IfcSchema::IfcOrientedEdge>());
		}
		loop = new IfcSchema::IfcEdgeLoop(edges);
		return 1;
	}
}
const Py::Object makeGeometryCurvePy(const Handle_Geom_Curve& c)
{
    if (c->IsKind(STANDARD_TYPE(Geom_Circle))) {
        Handle_Geom_Circle circ = Handle_Geom_Circle::DownCast(c);
        return Py::asObject(new CirclePy(new GeomCircle(circ)));
    }
    else if (c->IsKind(STANDARD_TYPE(Geom_Ellipse))) {
        Handle_Geom_Ellipse ell = Handle_Geom_Ellipse::DownCast(c);
        return Py::asObject(new EllipsePy(new GeomEllipse(ell)));
    }
    else if (c->IsKind(STANDARD_TYPE(Geom_Hyperbola))) {
        Handle_Geom_Hyperbola hyp = Handle_Geom_Hyperbola::DownCast(c);
        return Py::asObject(new HyperbolaPy(new GeomHyperbola(hyp)));
    }
    else if (c->IsKind(STANDARD_TYPE(Geom_Line))) {
        Handle_Geom_Line lin = Handle_Geom_Line::DownCast(c);
        return Py::asObject(new GeometryCurvePy(new GeomLine(lin)));
    }
    else if (c->IsKind(STANDARD_TYPE(Geom_OffsetCurve))) {
        Handle_Geom_OffsetCurve oc = Handle_Geom_OffsetCurve::DownCast(c);
        return Py::asObject(new OffsetCurvePy(new GeomOffsetCurve(oc)));
    }
    else if (c->IsKind(STANDARD_TYPE(Geom_Parabola))) {
        Handle_Geom_Parabola par = Handle_Geom_Parabola::DownCast(c);
        return Py::asObject(new ParabolaPy(new GeomParabola(par)));
    }
    else if (c->IsKind(STANDARD_TYPE(Geom_TrimmedCurve))) {
        Handle_Geom_TrimmedCurve trc = Handle_Geom_TrimmedCurve::DownCast(c);
        return Py::asObject(new GeometryCurvePy(new GeomTrimmedCurve(trc)));
    }
    /*else if (c->IsKind(STANDARD_TYPE(Geom_BoundedCurve))) {
        Handle_Geom_BoundedCurve bc = Handle_Geom_BoundedCurve::DownCast(c);
        return Py::asObject(new GeometryCurvePy(new GeomBoundedCurve(bc)));
    }*/
    else if (c->IsKind(STANDARD_TYPE(Geom_BezierCurve))) {
        Handle_Geom_BezierCurve bezier = Handle_Geom_BezierCurve::DownCast(c);
        return Py::asObject(new BezierCurvePy(new GeomBezierCurve(bezier)));
    }
    else if (c->IsKind(STANDARD_TYPE(Geom_BSplineCurve))) {
        Handle_Geom_BSplineCurve bspline = Handle_Geom_BSplineCurve::DownCast(c);
        return Py::asObject(new BSplineCurvePy(new GeomBSplineCurve(bspline)));
    }

    std::string err = "Unhandled curve type ";
    err += c->DynamicType()->Name();
    throw Py::TypeError(err);
}
int convert_to_ifc(const TopoDS_Edge& e, IfcSchema::IfcEdge*& edge, bool advanced) {
	double a, b;

	TopExp_Explorer exp(e, TopAbs_VERTEX);
	if (!exp.More()) return 0;
	TopoDS_Vertex v1 = TopoDS::Vertex(exp.Current());
	exp.Next();
	if (!exp.More()) return 0;
	TopoDS_Vertex v2 = TopoDS::Vertex(exp.Current());

	IfcSchema::IfcVertex *vertex1, *vertex2;
	if (!(convert_to_ifc(v1, vertex1, advanced) && convert_to_ifc(v2, vertex2, advanced))) {
		return 0;
	}

	Handle_Geom_Curve crv = BRep_Tool::Curve(e, a, b);

	if (crv.IsNull()) {
		return 0;
	}

	if (crv->DynamicType() == STANDARD_TYPE(Geom_Line) && !advanced) {
		IfcSchema::IfcEdge* edge2 = new IfcSchema::IfcEdge(vertex1, vertex2);
		edge = new IfcSchema::IfcOrientedEdge(edge2, true);
		return 1;
	} else {
		IfcSchema::IfcCurve* curve;
		if (!convert_to_ifc(crv, curve, advanced)) {
			return 0;
		}
		/// @todo probably not correct
		const bool sense = e.Orientation() == TopAbs_FORWARD;
		IfcSchema::IfcEdge* edge2 = new IfcSchema::IfcEdgeCurve(vertex1, vertex2, curve, true);
		edge = new IfcSchema::IfcOrientedEdge(edge2, sense);
		return 1;
	}
}
int convert_to_ifc(const Handle_Geom_Curve& c, IfcSchema::IfcCurve*& curve, bool advanced) {
	if (c->DynamicType() == STANDARD_TYPE(Geom_Line)) {
		IfcSchema::IfcDirection* d;
		IfcSchema::IfcCartesianPoint* p;

		Handle_Geom_Line line = Handle_Geom_Line::DownCast(c);

		if (!convert_to_ifc(line->Position().Location(), p, advanced)) {
			return 0;
		}
		if (!convert_to_ifc(line->Position().Direction(), d, advanced)) {
			return 0;
		}

		IfcSchema::IfcVector* v = new IfcSchema::IfcVector(d, 1.);
		curve = new IfcSchema::IfcLine(p, v);

		return 1;
	} else if (c->DynamicType() == STANDARD_TYPE(Geom_Circle)) {
		IfcSchema::IfcAxis2Placement3D* ax;

		Handle_Geom_Circle circle = Handle_Geom_Circle::DownCast(c);

		convert_to_ifc(circle->Position(), ax, advanced);
		curve = new IfcSchema::IfcCircle(ax, circle->Radius());

		return 1;
	} else if (c->DynamicType() == STANDARD_TYPE(Geom_Ellipse)) {
		IfcSchema::IfcAxis2Placement3D* ax;

		Handle_Geom_Ellipse ellipse = Handle_Geom_Ellipse::DownCast(c);

		convert_to_ifc(ellipse->Position(), ax, advanced);
		curve = new IfcSchema::IfcEllipse(ax, ellipse->MajorRadius(), ellipse->MinorRadius());

		return 1;
	}
#ifdef USE_IFC4
	else if (c->DynamicType() == STANDARD_TYPE(Geom_BSplineCurve)) {
		Handle_Geom_BSplineCurve bspline = Handle_Geom_BSplineCurve::DownCast(c);

		IfcSchema::IfcCartesianPoint::list::ptr points(new IfcSchema::IfcCartesianPoint::list);
		TColgp_Array1OfPnt poles(1, bspline->NbPoles());
		bspline->Poles(poles);
		for (int i = 1; i <= bspline->NbPoles(); ++i) {
			IfcSchema::IfcCartesianPoint* p;
			if (!convert_to_ifc(poles.Value(i), p, advanced)) {
				return 0;
			}
			points->push(p);
		}
		IfcSchema::IfcKnotType::IfcKnotType knot_spec = opencascade_knotspec_to_ifc(bspline->KnotDistribution());

		std::vector<int> mults;
		std::vector<double> knots;
		std::vector<double> weights;

		TColStd_Array1OfInteger bspline_mults(1, bspline->NbKnots());
		TColStd_Array1OfReal bspline_knots(1, bspline->NbKnots());
		TColStd_Array1OfReal bspline_weights(1, bspline->NbPoles());

		bspline->Multiplicities(bspline_mults);
		bspline->Knots(bspline_knots);
		bspline->Weights(bspline_weights);

		opencascade_array_to_vector(bspline_mults, mults);
		opencascade_array_to_vector(bspline_knots, knots);
		opencascade_array_to_vector(bspline_weights, weights);

		bool rational = false;
		for (std::vector<double>::const_iterator it = weights.begin(); it != weights.end(); ++it) {
			if ((*it) != 1.) {
				rational = true;
				break;
			}
		}

		if (rational) {
			curve = new IfcSchema::IfcRationalBSplineCurveWithKnots(
				bspline->Degree(),
				points,
				IfcSchema::IfcBSplineCurveForm::IfcBSplineCurveForm_UNSPECIFIED,
				bspline->IsClosed(),
				false,
				mults,
				knots,
				knot_spec,
				weights
				);
		} else {
			curve = new IfcSchema::IfcBSplineCurveWithKnots(
				bspline->Degree(),
				points,
				IfcSchema::IfcBSplineCurveForm::IfcBSplineCurveForm_UNSPECIFIED,
				bspline->IsClosed(),
				false,
				mults,
				knots,
				knot_spec
				);
		}

		return 1;
	}
#endif
	return 0;
}