/// <summary>
        /// Exports list of geometries to IFC body representation.
        /// </summary>
        /// <param name="exporterIFC">The ExporterIFC object.</param>
        /// <param name="categoryId">The category id.</param>
        /// <param name="geometryListIn">The geometry list.</param>
        /// <param name="options">The settings for how to export the body.</param>
        /// <param name="exportBodyParams">The extrusion creation data.</param>
        /// <returns>The BodyData containing the handle, offset and material ids.</returns>
        public static BodyData ExportBody(ExporterIFC exporterIFC,
            Element element, 
            ElementId categoryId,
            ElementId overrideMaterialId,
            IList<GeometryObject> geometryList,
            BodyExporterOptions options,
            IFCExtrusionCreationData exportBodyParams) 
        {
            BodyData bodyData = new BodyData();
            if (geometryList.Count == 0)
                return bodyData;

            Document document = element.Document;
            bool tryToExportAsExtrusion = options.TryToExportAsExtrusion;
            bool canExportSolidModelRep = tryToExportAsExtrusion && ExporterCacheManager.ExportOptionsCache.CanExportSolidModelRep;
            
            // This will default to false for now in all cases, as swept solids are not included in CV2.0.
            bool tryToExportAsSweptSolid = options.TryToExportAsSweptSolid;
            
            IFCFile file = exporterIFC.GetFile();
            IFCAnyHandle contextOfItems = exporterIFC.Get3DContextHandle("Body");
            double scale = exporterIFC.LinearScale;

            double eps = element.Document.Application.VertexTolerance * scale;

            bool allFaces = true;
            foreach (GeometryObject geomObject in geometryList)
            {
                if (allFaces && !(geomObject is Face))
                        allFaces = false;
                break;
            }

            IList<IFCAnyHandle> bodyItems = new List<IFCAnyHandle>();
            IList<ElementId> materialIdsForExtrusions = new List<ElementId>();

            IList<int> exportAsBRep = new List<int>();
            IList<int> exportAsSweptSolid = new List<int>();
            IList<int> exportAsExtrusion = new List<int>();

            bool hasExtrusions = false;
            bool hasSweptSolids = false;

            XYZ unscaledTrfOrig = new XYZ();
            using (IFCTransaction tr = new IFCTransaction(file))
            {
                // generate "bottom corner" of bbox; create new local placement if passed in.
                // need to transform, but not scale, this point to make it the new origin.
                using (IFCTransformSetter transformSetter = IFCTransformSetter.Create())
                {
                    // We may need to get the original values back, in case the export rolls back.
                    IFCLocalPlacementBackup localPlacementBackup = null;

                    if (options.AllowOffsetTransform && exportBodyParams!= null)
                    {
                        localPlacementBackup = new IFCLocalPlacementBackup(exportBodyParams.GetLocalPlacement());
                        bodyData.OffsetTransform = transformSetter.InitializeFromBoundingBox(exporterIFC, geometryList, exportBodyParams);
                    }

                    if (tryToExportAsExtrusion)
                    {
                        // Check to see if we have Geometries or GFaces.
                        // We will have the specific all GFaces case and then the generic case.
                        IList<Face> faces = null;

                        if (allFaces)
                        {
                            faces = new List<Face>();
                            foreach (GeometryObject geometryObject in geometryList)
                            {
                                faces.Add(geometryObject as Face);
                            }
                        }

                        int numExtrusionsToCreate = allFaces ? 1 : geometryList.Count;

                        IList<IList<IFCExtrusionData>> extrusionLists = new List<IList<IFCExtrusionData>>();
                        for (int ii = 0; ii < numExtrusionsToCreate && tryToExportAsExtrusion; ii++)
                        {
                            IList<IFCExtrusionData> extrusionList = new List<IFCExtrusionData>();

                            IFCExtrusionAxes axesToExtrudeIn = exportBodyParams != null ? exportBodyParams.PossibleExtrusionAxes : IFCExtrusionAxes.TryDefault;
                            XYZ directionToExtrudeIn = XYZ.Zero;
                            if (exportBodyParams != null && exportBodyParams.HasCustomAxis)
                                directionToExtrudeIn = exportBodyParams.CustomAxis;

                            IFCExtrusionCalculatorOptions extrusionOptions =
                               new IFCExtrusionCalculatorOptions(exporterIFC, axesToExtrudeIn, directionToExtrudeIn, scale);

                            if (allFaces)
                                extrusionList = IFCExtrusionCalculatorUtils.CalculateExtrusionData(extrusionOptions, faces);
                            else
                                extrusionList = IFCExtrusionCalculatorUtils.CalculateExtrusionData(extrusionOptions, geometryList[ii]);

                            if (extrusionList.Count == 0)
                            {
                                if (tryToExportAsSweptSolid)
                                    exportAsSweptSolid.Add(ii);
                                else if (!canExportSolidModelRep)
                                {
                                    tryToExportAsExtrusion = false;
                                    break;
                                }
                                else
                                    exportAsBRep.Add(ii);
                            }
                            else
                            {
                                extrusionLists.Add(extrusionList);
                                exportAsExtrusion.Add(ii);
                            }
                        }

                        int numCreatedExtrusions = extrusionLists.Count;
                        for (int ii = 0; ii < numCreatedExtrusions && tryToExportAsExtrusion; ii++)
                        {
                            int geomIndex = exportAsExtrusion[ii];

                            ElementId matId = SetBestMaterialIdInExporter(geometryList[geomIndex], element, overrideMaterialId, exporterIFC);
                            if (matId != ElementId.InvalidElementId)
                                bodyData.AddMaterial(matId);

                            if (exportBodyParams != null && exportBodyParams.AreInnerRegionsOpenings)
                            {
                                IList<CurveLoop> curveLoops = extrusionLists[ii][0].GetLoops();
                                XYZ extrudedDirection = extrusionLists[ii][0].ExtrusionDirection;

                                int numLoops = curveLoops.Count;
                                for (int jj = numLoops - 1; jj > 0; jj--)
                                {
                                    ExtrusionExporter.AddOpeningData(exportBodyParams, extrusionLists[ii][0], curveLoops[jj]);
                                    extrusionLists[ii][0].RemoveLoopAt(jj);
                                }
                            }

                            bool exportedAsExtrusion = false;
                            IFCExtrusionBasis whichBasis = extrusionLists[ii][0].ExtrusionBasis;
                            if (whichBasis >= 0)
                            {
                                IFCAnyHandle extrusionHandle = ExtrusionExporter.CreateExtrudedSolidFromExtrusionData(exporterIFC, element, extrusionLists[ii][0]);
                                if (!IFCAnyHandleUtil.IsNullOrHasNoValue(extrusionHandle))
                                {
                                    bodyItems.Add(extrusionHandle);
                                    materialIdsForExtrusions.Add(exporterIFC.GetMaterialIdForCurrentExportState());

                                    IList<CurveLoop> curveLoops = extrusionLists[ii][0].GetLoops();
                                    XYZ extrusionDirection = extrusionLists[ii][0].ExtrusionDirection;

                                    if (exportBodyParams != null)
                                    {
                                        exportBodyParams.Slope = GeometryUtil.GetSimpleExtrusionSlope(extrusionDirection, whichBasis);
                                        exportBodyParams.ScaledLength = extrusionLists[ii][0].ScaledExtrusionLength;
                                        exportBodyParams.ExtrusionDirection = extrusionDirection;
                                        for (int kk = 1; kk < extrusionLists[ii].Count; kk++)
                                        {
                                            ExtrusionExporter.AddOpeningData(exportBodyParams, extrusionLists[ii][kk]);
                                        }

                                        Plane plane = null;
                                        double height = 0.0, width = 0.0;
                                        if (ExtrusionExporter.ComputeHeightWidthOfCurveLoop(curveLoops[0], plane, out height, out width))
                                        {
                                            exportBodyParams.ScaledHeight = height * scale;
                                            exportBodyParams.ScaledWidth = width * scale;
                                        }

                                        double area = ExporterIFCUtils.ComputeAreaOfCurveLoops(curveLoops);
                                        if (area > 0.0)
                                        {
                                            exportBodyParams.ScaledArea = area * scale * scale;
                                        }

                                        double innerPerimeter = ExtrusionExporter.ComputeInnerPerimeterOfCurveLoops(curveLoops);
                                        double outerPerimeter = ExtrusionExporter.ComputeOuterPerimeterOfCurveLoops(curveLoops);
                                        if (innerPerimeter > 0.0)
                                            exportBodyParams.ScaledInnerPerimeter = innerPerimeter * scale;
                                        if (outerPerimeter > 0.0)
                                            exportBodyParams.ScaledOuterPerimeter = outerPerimeter * scale;
                                    }
                                    exportedAsExtrusion = true;
                                    hasExtrusions = true;
                                }
                            }

                            if (!exportedAsExtrusion)
                            {
                                if (tryToExportAsSweptSolid)
                                    exportAsSweptSolid.Add(ii);
                                else if (!canExportSolidModelRep)
                                {
                                    tryToExportAsExtrusion = false;
                                    break;
                                }
                                else
                                    exportAsBRep.Add(ii);
                            }
                        }
                    }

                    if (tryToExportAsSweptSolid)
                    {
                        int numCreatedSweptSolids = exportAsSweptSolid.Count;
                        for (int ii = 0; (ii < numCreatedSweptSolids) && tryToExportAsSweptSolid; ii++)
                        {
                            bool exported = false;
                            int geomIndex = exportAsSweptSolid[ii];
                            Solid solid = geometryList[geomIndex] as Solid;
                            // TODO: allFaces to SweptSolid
                            if (solid != null)
                            {
                                // TODO: other types of Axes
                                XYZ normal = new XYZ(0, 0, 1);
                                SweptSolidExporter sweptSolidExporter = SweptSolidExporter.Create(exporterIFC, element, solid, normal);
                                if (sweptSolidExporter != null)
                                {
                                    IFCAnyHandle sweptHandle = sweptSolidExporter.RepresentationItem;
                                    if (!IFCAnyHandleUtil.IsNullOrHasNoValue(sweptHandle))
                                    {
                                        bodyItems.Add(sweptHandle);
                                        materialIdsForExtrusions.Add(exporterIFC.GetMaterialIdForCurrentExportState());
                                        exported = true;
                                        if (sweptSolidExporter.IsExtrusion)
                                            hasExtrusions = true;
                                        else
                                            hasSweptSolids = true;
                                    }
                                }
                            }

                            if (!exported)
                                exportAsBRep.Add(ii);
                        }
                    }

                    bool exportSucceeded = (exportAsBRep.Count == 0) && (tryToExportAsExtrusion || tryToExportAsSweptSolid) && (hasExtrusions || hasSweptSolids);
                    if (exportSucceeded || canExportSolidModelRep)
                    {
                        int sz = bodyItems.Count();
                        for (int ii = 0; ii < sz; ii++)
                            BodyExporter.CreateSurfaceStyleForRepItem(exporterIFC, document, bodyItems[ii], materialIdsForExtrusions[ii]);

                        if (exportSucceeded)
                        {
                        if (hasExtrusions && !hasSweptSolids)
                        {
                            bodyData.RepresentationHnd =
                                RepresentationUtil.CreateSweptSolidRep(exporterIFC, element, categoryId, contextOfItems, bodyItems, bodyData.RepresentationHnd);
                            bodyData.ShapeRepresentationType = ShapeRepresentationType.SweptSolid;
                        }
                        else if (hasSweptSolids && !hasExtrusions)
                        {
                            bodyData.RepresentationHnd =
                                RepresentationUtil.CreateAdvancedSweptSolidRep(exporterIFC, element, categoryId, contextOfItems, bodyItems, bodyData.RepresentationHnd);
                            bodyData.ShapeRepresentationType = ShapeRepresentationType.AdvancedSweptSolid;
                        }
                        else
                        {
                            bodyData.RepresentationHnd =
                                RepresentationUtil.CreateSolidModelRep(exporterIFC, element, categoryId, contextOfItems, bodyItems);
                            bodyData.ShapeRepresentationType = ShapeRepresentationType.SolidModel;
                        }
                        
                        // TODO: include BRep, CSG, Clipping
                        tr.Commit();
                        return bodyData;
                    }
                    }

                    // If we are going to export a solid model, keep the created items.
                    if (!canExportSolidModelRep)
                    {
                        tr.RollBack();

                        // Revert to the original local placement, and re-set the relative placement, as the rollback may delete either.
                        if (localPlacementBackup != null)
                        {
                           IFCAnyHandle origLocalPlacement = localPlacementBackup.Restore();
                           if (!IFCAnyHandleUtil.IsNullOrHasNoValue(origLocalPlacement))
                               exportBodyParams.SetLocalPlacement(origLocalPlacement);
                        }
                    }
                    else
                        tr.Commit();
                }

                // We couldn't export it as an extrusion; export as a solid, brep, or a surface model.
                if (!canExportSolidModelRep)
                {
                    exportAsExtrusion.Clear();
                    bodyItems.Clear();
                    if (exportBodyParams != null)
                        exportBodyParams.ClearOpenings();
                }

                if (exportAsExtrusion.Count == 0)
                    exportAsBRep.Clear();
            }

            using (IFCTransaction tr = new IFCTransaction(file))
            {
                using (IFCTransformSetter transformSetter = IFCTransformSetter.Create())
                {
                    if (exportBodyParams != null && (exportAsBRep.Count == 0))
                        bodyData.OffsetTransform = transformSetter.InitializeFromBoundingBox(exporterIFC, geometryList, exportBodyParams);

                    BodyData retBodyData = ExportBodyAsBRep(exporterIFC, geometryList, exportAsBRep, bodyItems, element, categoryId, overrideMaterialId, contextOfItems, eps, options, bodyData);
                    if (retBodyData != null)
                        tr.Commit();
                    else
                        tr.RollBack();
                    return retBodyData;
                }
            }
        }
 public IFCLocalPlacementBackup(IFCAnyHandle localPlacement)
 {
     if (!IFCAnyHandleUtil.IsNullOrHasNoValue(localPlacement))
     {
         m_LocalPlacement = localPlacement;
         IFCAnyHandle placementRelTo = IFCAnyHandleUtil.GetInstanceAttribute(m_LocalPlacement, "PlacementRelTo");
         if (IFCAnyHandleUtil.IsSubTypeOf(placementRelTo, IFCEntityType.IfcLocalPlacement))
             m_PlacementRelTo = new IFCLocalPlacementBackup(placementRelTo);
         IFCAnyHandle relativePlacement = IFCAnyHandleUtil.GetInstanceAttribute(m_LocalPlacement, "RelativePlacement");
         if (IFCAnyHandleUtil.IsSubTypeOf(relativePlacement, IFCEntityType.IfcAxis2Placement3D))
             m_RelativePlacement = new IFCAxis2Placement3DBackup(relativePlacement);
     }
 }