/// <summary>
        /// Returns a handle for creation of an AdvancedBrep with AdvancedFace and assigns it to the file
        /// </summary>
        /// <param name="exporterIFC">exporter IFC</param>
        /// <param name="element">the element</param>
        /// <param name="options">exporter option</param>
        /// <param name="geomObject">the geometry object</param>
        /// <returns>the handle</returns>
        public static IFCAnyHandle ExportBodyAsAdvancedBrep(ExporterIFC exporterIFC, Element element, BodyExporterOptions options,
            GeometryObject geomObject)
        {
            IFCFile file = exporterIFC.GetFile();
            Document document = element.Document;

            // IFCFuzzyXYZ will be used in this routine to compare 2 XYZs, we consider two points are the same if their distance
            // is within this tolerance
            IFCFuzzyXYZ.IFCFuzzyXYZEpsilon = document.Application.VertexTolerance; 
            
            IFCAnyHandle advancedBrep = null;

         using (IFCTransaction tr = new IFCTransaction(file))
         {
            try
            {
                if (!(geomObject is Solid))
                {
                    return null;
                }
                HashSet<IFCAnyHandle> cfsFaces = new HashSet<IFCAnyHandle>();
                Solid geomSolid = geomObject as Solid;
                FaceArray faces = geomSolid.Faces;

               // Check for supported curve and face types before creating an advanced BRep.
               IList<KeyValuePair<Edge, Curve>> edgesAndCurves = new List<KeyValuePair<Edge, Curve>>();
                foreach (Edge edge in geomSolid.Edges) 
                {
                    Curve currCurve = edge.AsCurve();
                  if (currCurve == null)
                     return null;

                  bool isValidCurve = !(currCurve is CylindricalHelix);
                    if (!isValidCurve)
                        return null;

                    // based on the definition of IfcAdvancedBrep in IFC 4 specification, an IfcAdvancedBrep must contain a closed shell, so we
                    // have a test to reject all open shells here.
                    // we check that geomSolid is an actual solid and not an open shell by verifying that each edge is shared by exactly 2 faces.
                    for (int ii = 0; ii < 2; ii++)
                    {
                        if (edge.GetFace(ii) == null)
                            return null;
                        }

                  edgesAndCurves.Add(new KeyValuePair<Edge, Curve>(edge, currCurve));
                }

                foreach (Face face in geomSolid.Faces) 
                {
                  bool isValidFace = (face is PlanarFace) || (face is CylindricalFace) || (face is RuledFace) || (face is HermiteFace) || (face is RevolvedFace) || (face is ConicalFace);
                    if (!isValidFace) 
                    {
                        return null;
                    }
                }

                Dictionary<Face, IList<Edge>> faceToEdges = new Dictionary<Face, IList<Edge>>();
                Dictionary<Edge, IFCAnyHandle> edgeToIfcEdgeCurve = new Dictionary<Edge, IFCAnyHandle>();

               // A map of already created IfcCartesianPoints, to avoid duplication.  This is used for vertex points and other geometry in the BRep.
               // We do not share IfcCartesianPoints across BReps.
               IDictionary<IFCFuzzyXYZ, IFCAnyHandle> cartesianPoints = new SortedDictionary<IFCFuzzyXYZ, IFCAnyHandle>();

               // A map of already created IfcVertexPoints, to avoid duplication.
                IDictionary<IFCFuzzyXYZ, IFCAnyHandle> vertices = new SortedDictionary<IFCFuzzyXYZ, IFCAnyHandle>();

                // First phase: get all the vertices:
               foreach (KeyValuePair<Edge, Curve> edgeAndCurve in edgesAndCurves)
                {
                  Edge edge = edgeAndCurve.Key;
                  Curve currCurve = edgeAndCurve.Value;

                    XYZ startPoint = currCurve.GetEndPoint(0);
                    XYZ endPoint = currCurve.GetEndPoint(1);
                    IFCFuzzyXYZ fuzzyStartPoint = new IFCFuzzyXYZ(startPoint);
                    IFCFuzzyXYZ fuzzyEndPoint = new IFCFuzzyXYZ(endPoint);
                    IFCAnyHandle edgeStart = null;
                    IFCAnyHandle edgeEnd = null;

                    if (vertices.ContainsKey(fuzzyStartPoint))
                    {
                        edgeStart = vertices[fuzzyStartPoint];
                    }
                    else
                    {
                     IFCAnyHandle edgeStartCP = GeometryUtil.XYZtoIfcCartesianPoint(exporterIFC, currCurve.GetEndPoint(0), cartesianPoints);
                        edgeStart = IFCInstanceExporter.CreateVertexPoint(file, edgeStartCP);
                        vertices.Add(fuzzyStartPoint, edgeStart);
                    }

                    if (vertices.ContainsKey(fuzzyEndPoint))
                    {
                        edgeEnd = vertices[fuzzyEndPoint];
                    }
                    else
                    {
                     IFCAnyHandle edgeEndCP = GeometryUtil.XYZtoIfcCartesianPoint(exporterIFC, currCurve.GetEndPoint(1), cartesianPoints);
                        edgeEnd = IFCInstanceExporter.CreateVertexPoint(file, edgeEndCP);
                        vertices.Add(fuzzyEndPoint, edgeEnd);
                    }

                  IFCAnyHandle edgeCurve = CreateEdgeCurveFromCurve(file, exporterIFC, currCurve, edgeStart, edgeEnd, true, cartesianPoints);

                    edgeToIfcEdgeCurve.Add(edge, edgeCurve);

                    Face face = null;
                    for (int ii = 0; ii < 2; ii++)
                    {
                        face = edge.GetFace(ii);
                        if (!faceToEdges.ContainsKey(face))
                        {
                            faceToEdges.Add(face, new List<Edge>());
                        }
                        IList<Edge> edges = faceToEdges[face];
                        edges.Add(edge);
                    }
                }

                // Second phase: create IfcFaceOuterBound, IfcFaceInnerBound, IfcAdvancedFace and IfcAdvancedBrep
               foreach (Face face in geomSolid.Faces)
               {
                  // List of created IfcEdgeLoops of this face
                  IList<IFCAnyHandle> edgeLoopList = new List<IFCAnyHandle>();
                  // List of created IfcOrientedEdge in one loop
                  IList<IFCAnyHandle> orientedEdgeList = new List<IFCAnyHandle>();
                  IFCAnyHandle surface = null;
                  IList<HashSet<IFCAnyHandle>> boundsCollection = new List<HashSet<IFCAnyHandle>>();

                  // calculate sameSense by getting a point on the surface and compute the normal of the face and the surface at that point
                  // if these two vectors agree, then sameSense is true.
                  // The point we use to test will be the start point of the first edge in the first loop of this face
                  bool sameSenseAF = true;
                  EdgeArrayArray faceEdgeLoops = face.EdgeLoops;
                  if (faceEdgeLoops == null || faceEdgeLoops.Size == 0)
                  {
                     return null;
                  }
                  EdgeArray firstEdgeLoop = faceEdgeLoops.get_Item(0);
                  if (firstEdgeLoop == null)
                  {
                     return null;
                  }
                  Edge firstEdge = firstEdgeLoop.get_Item(0);
                  UV pointToTest = firstEdge.EvaluateOnFace(0, face);
                  // Compute the normal of the FACE at pointToTest
                  XYZ faceNormal = face.ComputeNormal(pointToTest);
                  // Compute the normal of the SURFACE at pointToTest
                  Transform testPointDerivatives = face.ComputeDerivatives(pointToTest);
                  XYZ surfaceNormal = testPointDerivatives.BasisZ;
                  if (!faceNormal.IsAlmostEqualTo(surfaceNormal))
                  {
                     sameSenseAF = false;
                  }

                  Dictionary<EdgeArray, IList<EdgeArray>> sortedEdgeLoop = GeometryUtil.SortEdgeLoop(faceEdgeLoops, face);
                  // check that we get back the same number of edgeloop
                  int numberOfSortedEdgeLoop = 0;
                  foreach (KeyValuePair<EdgeArray, IList<EdgeArray>> pair in sortedEdgeLoop)
                  {
                     numberOfSortedEdgeLoop += 1 + pair.Value.Count;
                  }

                  if (numberOfSortedEdgeLoop != face.EdgeLoops.Size)
                  {
                     return null;
                  }

                  foreach (KeyValuePair<EdgeArray, IList<EdgeArray>> pair in sortedEdgeLoop)
                  {
                     if (pair.Key == null || pair.Value == null)
                        return null;

                     HashSet<IFCAnyHandle> bounds = new HashSet<IFCAnyHandle>();

                     // Append the outerloop at the beginning of the list of inner loop
                     pair.Value.Insert(0, pair.Key);

                     // Process each inner loop
                     foreach (EdgeArray edgeArray in pair.Value)
                     {
                        // Map each edge in this loop back to its corresponding edge curve and then calculate its orientation to create IfcOrientedEdge
                        foreach (Edge edge in edgeArray)
                        {
                           // The reason why edgeToIfcEdgeCurve cannot find edge is that either we haven't created the IfcOrientedEdge
                           // corresponding to that edge OR we already have but the dictionary cannot find the edge as its key because 
                           // Face.EdgeLoop and geomSolid.Edges return different pointers for the same edge. This can be avoided if 
                           // Equals() method is implemented for Edge
                           if (!edgeToIfcEdgeCurve.ContainsKey(edge))
                              return null;

                           IFCAnyHandle edgeCurve = edgeToIfcEdgeCurve[edge];

                           Curve currCurve = edge.AsCurve();
                           Curve curveInCurrentFace = edge.AsCurveFollowingFace(face);

                           if (currCurve == null || curveInCurrentFace == null)
                              return null;

                           // if the curve length is 0, ignore it.
                           if (MathUtil.IsAlmostZero(currCurve.ApproximateLength))
                              continue;

                           // if the curve is unbound, it means that the solid may be corrupted, we shouldn't process it anymore
                           if (!currCurve.IsBound)
                              return null;

                           bool orientation = currCurve.GetEndPoint(0).IsAlmostEqualTo(curveInCurrentFace.GetEndPoint(0));

                           IFCAnyHandle orientedEdge = IFCInstanceExporter.CreateOrientedEdge(file, edgeCurve, orientation);
                           orientedEdgeList.Add(orientedEdge);
                        }

                        IFCAnyHandle edgeLoop = IFCInstanceExporter.CreateEdgeLoop(file, orientedEdgeList);
                        edgeLoopList.Add(edgeLoop);

                        IFCAnyHandle faceBound = null;

                        // EdgeLoopList has only 1 element indicates that this is the outer loop
                        if (edgeLoopList.Count == 1)
                           faceBound = IFCInstanceExporter.CreateFaceOuterBound(file, edgeLoop, true);
                        else
                           faceBound = IFCInstanceExporter.CreateFaceBound(file, edgeLoop, false);

                        bounds.Add(faceBound);

                        // After finishing processing one loop, clear orientedEdgeList
                        orientedEdgeList.Clear();
                     }
                     boundsCollection.Add(bounds);
                  }

                  edgeLoopList.Clear();

                  // TODO: create a new face processing method to factor out this code
                  // process the face now
                  if (face is PlanarFace)
                  {
                     PlanarFace plFace = face as PlanarFace;
                     IFCAnyHandle location = GeometryUtil.XYZtoIfcCartesianPoint(exporterIFC, plFace.Origin, cartesianPoints);

                     // Since there is no easy way to get the surface normal, we will calculate the face's normal and negate it if we know
                     // the surface normal doesn't agree with the face normal based on the sameSense attribute that we calculate above
                     XYZ zdir = plFace.FaceNormal;
                     if (!sameSenseAF)
                        zdir = zdir.Negate();
                     XYZ xdir = plFace.XVector;

                     IFCAnyHandle position = CreatePositionForFace(exporterIFC, location, zdir, xdir);

                     surface = IFCInstanceExporter.CreatePlane(file, position);
                  }
                  else if (face is CylindricalFace)
                  {
                     CylindricalFace cylFace = face as CylindricalFace;
                     IFCAnyHandle location = GeometryUtil.XYZtoIfcCartesianPoint(exporterIFC, cylFace.Origin, cartesianPoints);

                     // get radius-x and radius-y and the position of the origin
                     XYZ rad = UnitUtil.ScaleLength(cylFace.get_Radius(0));
                     double radius = rad.GetLength();

                     XYZ zdir = cylFace.Axis;
                     XYZ xdir = rad.Normalize();

                     IFCAnyHandle position = CreatePositionForFace(exporterIFC, location, zdir, xdir);

                     surface = IFCInstanceExporter.CreateCylindricalSurface(file, position, radius);
                  }
                  else if (face is ConicalFace)
                  {
                     ConicalFace conicalFace = face as ConicalFace;
                     IFCAnyHandle location = GeometryUtil.XYZtoIfcCartesianPoint(exporterIFC, conicalFace.Origin, cartesianPoints);

                     XYZ zdir = conicalFace.Axis;
                     if (zdir == null)
                        return null;

                     // Create a finite profile curve for the cone based on the bounding box.
                     BoundingBoxUV coneUV = conicalFace.GetBoundingBox();
                     if (coneUV == null)
                        return null;

                     XYZ startPoint = conicalFace.Evaluate(new UV(0, coneUV.Min.V));
                     XYZ endPoint = conicalFace.Evaluate(new UV(0, coneUV.Max.V));

                     Curve profileCurve = Line.CreateBound(startPoint, endPoint);

                     IFCAnyHandle axis = GeometryUtil.VectorToIfcDirection(exporterIFC, zdir);

                     IFCAnyHandle axisPosition = IFCInstanceExporter.CreateAxis1Placement(file, location, axis);

                     IFCAnyHandle sweptCurve = CreateProfileCurveFromCurve(file, exporterIFC, profileCurve, Resources.ConicalFaceProfileCurve, cartesianPoints);

                     // The profile position is optional in IFC4+.
                     surface = IFCInstanceExporter.CreateSurfaceOfRevolution(file, sweptCurve, null, axisPosition);
                  }
                  else if (face is RevolvedFace)
                  {
                     RevolvedFace revFace = face as RevolvedFace;
                     IFCAnyHandle location = GeometryUtil.XYZtoIfcCartesianPoint(exporterIFC, revFace.Origin, cartesianPoints);

                     XYZ zdir = revFace.Axis;
                     if (zdir == null)
                        return null;

                     // Note that the returned curve is in the coordinate system of the face.
                     Curve curve = revFace.Curve;
                     if (curve == null)
                        return null;

                     // Create arbitrary plane with z direction as normal.
                     Plane arbitraryPlane = new Plane(zdir, XYZ.Zero);

                     Transform revitTransform = Transform.Identity;
                     revitTransform.BasisX = arbitraryPlane.XVec;
                     revitTransform.BasisY = arbitraryPlane.YVec;
                     revitTransform.BasisZ = zdir;
                     revitTransform.Origin = revFace.Origin;
                     Curve profileCurve = curve.CreateTransformed(revitTransform);

                     IFCAnyHandle axis = GeometryUtil.VectorToIfcDirection(exporterIFC, zdir);

                     IFCAnyHandle axisPosition = IFCInstanceExporter.CreateAxis1Placement(file, location, axis);

                     IFCAnyHandle sweptCurve = CreateProfileCurveFromCurve(file, exporterIFC, profileCurve, Resources.RevolvedFaceProfileCurve, cartesianPoints);

                     // The profile position is optional in IFC4+.
                     surface = IFCInstanceExporter.CreateSurfaceOfRevolution(file, sweptCurve, null, axisPosition);
                  }
                  else if (face is RuledFace)
                  {
                     return null;        // currently does not support this type of face
                  }
                  else if (face is HermiteFace)
                  {
                     return null;        // currently does not support this type of face
                  }
                  else
                  {
                     return null;
                  }

                  // If we had trouble creating a surface, stop trying.
                  if (IFCAnyHandleUtil.IsNullOrHasNoValue(surface))
                     return null;

                  foreach (HashSet<IFCAnyHandle> faceBound in boundsCollection)
                  {
                     IFCAnyHandle advancedFace = IFCInstanceExporter.CreateAdvancedFace(file, faceBound, surface, sameSenseAF);
                     cfsFaces.Add(advancedFace);
                  }
               }

                // create advancedBrep
                IFCAnyHandle closedShell = IFCInstanceExporter.CreateClosedShell(file, cfsFaces);
                advancedBrep = IFCInstanceExporter.CreateAdvancedBrep(file, closedShell);

               if (IFCAnyHandleUtil.IsNullOrHasNoValue(advancedBrep))
                  tr.RollBack();
               else
                  tr.Commit();

                return advancedBrep;
            }
            catch
            {
                return null;
            }
        }
      }
        /// <summary>
        /// Add one loop of vertices that will define a boundary loop of the current face.
        /// </summary>
        public void AddLoopVertices(IList<XYZ> loopVertices)
        {
            int vertexCount = (loopVertices == null) ? 0 : loopVertices.Count;
            if (vertexCount < 3)
                throw new InvalidOperationException("Too few distinct loop vertices, ignoring.");

            IList<XYZ> adjustedLoopVertices = new List<XYZ>();
            IDictionary<IFCFuzzyXYZ, int> createdVertices = new SortedDictionary<IFCFuzzyXYZ, int>();

            int numCreated = 0;
            for (int ii = 0; ii < vertexCount; ii++)
            {
                IFCFuzzyXYZ fuzzyXYZ = new IFCFuzzyXYZ(loopVertices[ii]);

                int createdVertexIndex = -1;
                if (createdVertices.TryGetValue(fuzzyXYZ, out createdVertexIndex))
                {
                    // We will allow the first and last point to be equivalent, or the current and last point.  Otherwise we will throw.
                    if (((createdVertexIndex == 0) && (ii == vertexCount - 1)) || (createdVertexIndex == numCreated-1))
                        continue;

                    throw new InvalidOperationException("Loop is self-intersecting, ignoring.");
                }

                XYZ adjustedXYZ;
                if (!m_TessellatedFaceVertices.TryGetValue(fuzzyXYZ, out adjustedXYZ))
                    adjustedXYZ = m_TessellatedFaceVertices[fuzzyXYZ] = loopVertices[ii];
                
                adjustedLoopVertices.Add(adjustedXYZ);
                createdVertices[new IFCFuzzyXYZ(adjustedXYZ)] = numCreated;
                numCreated++;
            }

            // Checking start and end points should be covered above.
            if (numCreated < 3)
                throw new InvalidOperationException("Loop has less than 3 distinct vertices, ignoring.");

            m_TessellatedFaceBoundary.Add(adjustedLoopVertices);
        }