void IfcHierarchyHelper::addBox(IfcSchema::IfcShapeRepresentation* rep, double w, double d, double h, 
	IfcSchema::IfcAxis2Placement2D* place, IfcSchema::IfcAxis2Placement3D* place2, 
	IfcSchema::IfcDirection* dir, IfcSchema::IfcRepresentationContext* context) 
{
	if (false) { // TODO What's this?
		IfcSchema::IfcRectangleProfileDef* profile = new IfcSchema::IfcRectangleProfileDef(
			IfcSchema::IfcProfileTypeEnum::IfcProfileType_AREA, boost::none, place ? place : addPlacement2d(), w, d);
		IfcSchema::IfcExtrudedAreaSolid* solid = new IfcSchema::IfcExtrudedAreaSolid(profile, 
			place2 ? place2 : addPlacement3d(), dir ? dir : addTriplet<IfcSchema::IfcDirection>(0, 0, 1), h);

		addEntity(profile);
		addEntity(solid);
		IfcSchema::IfcRepresentationItem::list::ptr items = rep->Items();
		items->push(solid);
		rep->setItems(items);
	} else {
		std::vector<std::pair<double, double> > points;
		points.push_back(std::make_pair(-w/2, -d/2));
		points.push_back(std::make_pair(w/2, -d/2));
		points.push_back(std::make_pair(w/2, d/2));
		points.push_back(std::make_pair(-w/2, d/2));
		// The call to addExtrudedPolyline() closes the polyline
		addExtrudedPolyline(rep, points, h, place, place2, dir, context);
	}
}
int main(int argc, char** argv) {
	IfcHierarchyHelper file;

	IfcSchema::IfcBuildingElementProxy* product = new IfcSchema::IfcBuildingElementProxy(
		guid(), 0, S("Blender's Suzanne"), null, null, 0, 0, null, null);
	file.addBuildingProduct(product);
	product->setOwnerHistory(file.getSingle<IfcSchema::IfcOwnerHistory>());

	product->setObjectPlacement(file.addLocalPlacement());

	IfcSchema::IfcRepresentation::list::ptr reps (new IfcSchema::IfcRepresentation::list);
	IfcSchema::IfcRepresentationItem::list::ptr items (new IfcSchema::IfcRepresentationItem::list);

	std::vector< std::vector< double > > vertices_vector = create_vector_from_array(vertices, sizeof(vertices) / sizeof(vertices[0]));
	std::vector< std::vector< int > > indices_vector = create_vector_from_array(indices, sizeof(indices) / sizeof(indices[0]));

	IfcSchema::IfcCartesianPointList3D* coordinates = new IfcSchema::IfcCartesianPointList3D(vertices_vector);	
	IfcSchema::IfcTriangulatedFaceSet* faceset = new IfcSchema::IfcTriangulatedFaceSet(coordinates, null, null, indices_vector, null);
		
	items->push(faceset);
	IfcSchema::IfcShapeRepresentation* rep = new IfcSchema::IfcShapeRepresentation(
		file.getRepresentationContext("Model"), S("Body"), S("SurfaceModel"), items);
	reps->push(rep);

	IfcSchema::IfcProductDefinitionShape* shape = new IfcSchema::IfcProductDefinitionShape(0, 0, reps);
	file.addEntity(shape);
		
	product->setRepresentation(shape);

	const std::string filename = "tesselated_faceset.ifc";
	file.header().file_name().name(filename);
	std::ofstream f(filename.c_str());
	f << file;
}
void IfcHierarchyHelper::addAxis(IfcSchema::IfcShapeRepresentation* rep, double l, IfcSchema::IfcRepresentationContext* /*context*/) {
	IfcSchema::IfcCartesianPoint* p1 = addDoublet<IfcSchema::IfcCartesianPoint>(-l / 2., 0.);
	IfcSchema::IfcCartesianPoint* p2 = addDoublet<IfcSchema::IfcCartesianPoint>(+l / 2., 0.);
	IfcSchema::IfcCartesianPoint::list::ptr pts(new IfcSchema::IfcCartesianPoint::list);
	pts->push(p1); pts->push(p2);
	IfcSchema::IfcPolyline* poly = new IfcSchema::IfcPolyline(pts);
	addEntity(poly);
	
	IfcSchema::IfcRepresentationItem::list::ptr items = rep->Items();
	items->push(poly);
	rep->setItems(items);
}
IfcSchema::IfcBuildingStorey* IfcHierarchyHelper::addBuildingProduct(IfcSchema::IfcProduct* product, 
	IfcSchema::IfcBuildingStorey* storey, IfcSchema::IfcOwnerHistory* owner_hist) 
{
	if (! owner_hist) {
		owner_hist = getSingle<IfcSchema::IfcOwnerHistory>();
	}
	if (! owner_hist) {
		owner_hist = addOwnerHistory();
	}
	if (! storey) {
		storey = getSingle<IfcSchema::IfcBuildingStorey>();
	}
	if (! storey) {
		storey = addBuildingStorey(0, owner_hist);
	}
	addEntity(product);
	// CV-2x3-158: Don't add decompositions directly to a building storey
	bool is_decomposition = false;
#ifdef USE_IFC4
	IfcSchema::IfcRelAggregates::list::ptr decomposes = product->Decomposes();
	for (IfcSchema::IfcRelAggregates::list::it it = decomposes->begin(); it != decomposes->end(); ++it) {
#else
	IfcSchema::IfcRelDecomposes::list::ptr decomposes = product->Decomposes();
	for (IfcSchema::IfcRelDecomposes::list::it it = decomposes->begin(); it != decomposes->end(); ++it) {
#endif
		if ((*it)->RelatingObject() != product) {
			is_decomposition = true;
			break;
		}
	}
	if (!is_decomposition) {
		addRelatedObject<IfcSchema::IfcRelContainedInSpatialStructure>(storey, product);
		relatePlacements(storey, product);
	}
	return storey;
}

void IfcHierarchyHelper::addExtrudedPolyline(IfcSchema::IfcShapeRepresentation* rep, const std::vector<std::pair<double, double> >& points, double h, 
	IfcSchema::IfcAxis2Placement2D* /*place*/, IfcSchema::IfcAxis2Placement3D* place2, 
	IfcSchema::IfcDirection* dir, IfcSchema::IfcRepresentationContext* /*context*/) 
{
	IfcSchema::IfcCartesianPoint::list::ptr cartesian_points (new IfcSchema::IfcCartesianPoint::list);
	for (std::vector<std::pair<double, double> >::const_iterator i = points.begin(); i != points.end(); ++i) {
		cartesian_points->push(addDoublet<IfcSchema::IfcCartesianPoint>(i->first, i->second));
	}
	if (cartesian_points->size()) cartesian_points->push(*cartesian_points->begin());
	IfcSchema::IfcPolyline* line = new IfcSchema::IfcPolyline(cartesian_points);
	IfcSchema::IfcArbitraryClosedProfileDef* profile = new IfcSchema::IfcArbitraryClosedProfileDef(
		IfcSchema::IfcProfileTypeEnum::IfcProfileType_AREA, boost::none, line);

	IfcSchema::IfcExtrudedAreaSolid* solid = new IfcSchema::IfcExtrudedAreaSolid(
		profile, place2 ? place2 : addPlacement3d(), dir ? dir : addTriplet<IfcSchema::IfcDirection>(0, 0, 1), h);

	IfcSchema::IfcRepresentationItem::list::ptr items = rep->Items();
	items->push(solid);
	rep->setItems(items);

	addEntity(line);
	addEntity(profile);
	addEntity(solid);
}
int main(int argc, char** argv) {
	const char filename[] = "IfcCompositeProfileDef.ifc";
	IfcHierarchyHelper file;
	file.header().file_name().name(filename);

	double coords1[] = {100.0, 0.0};
	double coords2[] = {200.0, 0.0};
	double coords3[] = {300.0, 0.0};

	IfcSchema::IfcProfileDef::list::ptr profiles (new IfcSchema::IfcProfileDef::list());

	IfcSchema::IfcCartesianTransformationOperator2D* transform1 = new IfcSchema::IfcCartesianTransformationOperator2D(file.addDoublet<IfcSchema::IfcDirection>(1, 0), file.addDoublet<IfcSchema::IfcDirection>(0, -1), file.addDoublet<IfcSchema::IfcCartesianPoint>(40, 0), null);
	IfcSchema::IfcCartesianTransformationOperator2D* transform2 = new IfcSchema::IfcCartesianTransformationOperator2D(file.addDoublet<IfcSchema::IfcDirection>(0, -1), file.addDoublet<IfcSchema::IfcDirection>(1, 0), file.addDoublet<IfcSchema::IfcCartesianPoint>(40, 0), 0.3);
	
	IfcSchema::IfcProfileDef* p1 = new Ifc2x3::IfcIShapeProfileDef(
		IfcSchema::IfcProfileTypeEnum::IfcProfileType_AREA,
		null, file.addPlacement2d(),    25.0,    50.0,     5.0,     5.0,     2.0);

	IfcSchema::IfcProfileDef* p2 = new Ifc2x3::IfcLShapeProfileDef(
		IfcSchema::IfcProfileTypeEnum::IfcProfileType_AREA,
		null, file.addPlacement2d(),    50.0,    25.0,     5.0,      1.0,     2.0,     2.0,    null,    null);

	IfcSchema::IfcProfileDef* p3 = new Ifc2x3::IfcTShapeProfileDef(
		IfcSchema::IfcProfileTypeEnum::IfcProfileType_AREA,
		null, file.addPlacement2d(),    50.0,    40.0,    10.0,     10.0,     3.0,     2.0,     1.0,     2.0,     2.0,    null);

	IfcSchema::IfcProfileDef* p4 = new Ifc2x3::IfcCShapeProfileDef(
		IfcSchema::IfcProfileTypeEnum::IfcProfileType_AREA,
		null, file.addPlacement2d(80.),    50.0,    25.0,     5.0,    10.0,     2.0,    null);

	file.addEntity(p2);
	file.addEntity(p3);
	
	file.addEntity(transform1);
	file.addEntity(transform2);
	
	IfcSchema::IfcDerivedProfileDef* p5 = new IfcSchema::IfcDerivedProfileDef(IfcSchema::IfcProfileTypeEnum::IfcProfileType_AREA, null, p2, transform1, null);
	IfcSchema::IfcDerivedProfileDef* p6 = new IfcSchema::IfcDerivedProfileDef(IfcSchema::IfcProfileTypeEnum::IfcProfileType_AREA, null, p3, transform2, null);
	
	profiles->push(p1);
	profiles->push(p5);
	profiles->push(p6);
	profiles->push(p4);

	file.addEntities(profiles->generalize());

	IfcSchema::IfcCompositeProfileDef* composite = new IfcSchema::IfcCompositeProfileDef(IfcSchema::IfcProfileTypeEnum::IfcProfileType_AREA, S("IFC"), profiles, null);

	IfcSchema::IfcBuildingElementProxy* product = new IfcSchema::IfcBuildingElementProxy(
			guid(), 0, S("profile"), null, null, 0, 0, null, null);

	file.addBuildingProduct(product);

	product->setOwnerHistory(file.getSingle<IfcSchema::IfcOwnerHistory>());

	product->setObjectPlacement(file.addLocalPlacement());

	IfcSchema::IfcExtrudedAreaSolid* solid = new IfcSchema::IfcExtrudedAreaSolid(composite,
		file.addPlacement3d(), file.addTriplet<IfcSchema::IfcDirection>(0, 0, 1), 20.0);

	file.addEntity(composite);
	file.addEntity(solid);
		
	IfcSchema::IfcRepresentation::list::ptr reps (new IfcSchema::IfcRepresentation::list());
	IfcSchema::IfcRepresentationItem::list::ptr items (new IfcSchema::IfcRepresentationItem::list());
		
	items->push(solid);
	IfcSchema::IfcShapeRepresentation* rep = new IfcSchema::IfcShapeRepresentation(
		file.getSingle<IfcSchema::IfcRepresentationContext>(), S("Body"), S("SweptSolid"), items);
	reps->push(rep);

	IfcSchema::IfcProductDefinitionShape* shape = new IfcSchema::IfcProductDefinitionShape(boost::none, boost::none, reps);
	file.addEntity(rep);
	file.addEntity(shape);
		
	product->setRepresentation(shape);

	file.getSingle<IfcSchema::IfcProject>()->setName("IfcCompositeProfileDef");

	std::ofstream f(filename);
	f << file;
}