/// <summary>
        /// Exports materials for host object.
        /// </summary>
        /// <param name="exporterIFC">The ExporterIFC object.</param>
        /// <param name="hostObject">The host object.</param>
        /// <param name="elemHnds">The host IFC handles.</param>
        /// <param name="geometryElement">The geometry element.</param>
        /// <param name="productWrapper">The ProductWrapper.</param>
        /// <param name="levelId">The level id.</param>
        /// <param name="direction">The IFCLayerSetDirection.</param>
        /// <param name="containsBRepGeometry">True if the geometry contains BRep geoemtry.  If so, we will export an IfcMaterialList</param>
        /// <returns>True if exported successfully, false otherwise.</returns>
        public static bool ExportHostObjectMaterials(ExporterIFC exporterIFC, HostObject hostObject,
                                                     IList <IFCAnyHandle> elemHnds, GeometryElement geometryElement, ProductWrapper productWrapper,
                                                     ElementId levelId, Toolkit.IFCLayerSetDirection direction, bool containsBRepGeometry, IFCAnyHandle typeHnd = null)
        {
            if (hostObject == null)
            {
                return(true); //nothing to do
            }
            if (elemHnds == null || (elemHnds.Count == 0))
            {
                return(true); //nothing to do
            }
            IFCFile file = exporterIFC.GetFile();

            using (IFCTransaction tr = new IFCTransaction(file))
            {
                //if (productWrapper != null)
                //    productWrapper.ClearFinishMaterials();

                double scaledOffset = 0.0, scaledWallWidth = 0.0, wallHeight = 0.0;
                Wall   wall = hostObject as Wall;

                if (wall != null)
                {
                    scaledWallWidth = UnitUtil.ScaleLength(wall.Width);
                    scaledOffset    = -scaledWallWidth / 2.0;
                    BoundingBoxXYZ boundingBox = wall.get_BoundingBox(null);
                    if (boundingBox != null)
                    {
                        wallHeight = boundingBox.Max.Z - boundingBox.Min.Z;
                    }
                }

                List <ElementId> matIds;
                IFCAnyHandle     primaryMaterialHnd;
                IFCAnyHandle     materialLayerSet = ExporterUtil.CollectMaterialLayerSet(exporterIFC, hostObject, productWrapper, out matIds, out primaryMaterialHnd);

                // For IFC4 RV, material layer may still be created even if the geometry is Brep/Tessellation
                if ((containsBRepGeometry && matIds.Count > 0) &&
                    !(ExporterCacheManager.ExportOptionsCache.ExportAs4ReferenceView && !IFCAnyHandleUtil.IsNullOrHasNoValue(materialLayerSet)))
                {
                    foreach (IFCAnyHandle elemHnd in elemHnds)
                    {
                        CategoryUtil.CreateMaterialAssociation(exporterIFC, elemHnd, matIds);
                    }
                }

                if (!IFCAnyHandleUtil.IsNullOrHasNoValue(materialLayerSet))
                {
                    // If there is an override for creating IfcMaterial instead of IfcMaterialLayer for a single material/layer, assign the material directly to type or element(s)
                    if (IFCAnyHandleUtil.IsTypeOf(materialLayerSet, IFCEntityType.IfcMaterial))
                    {
                        if (typeHnd != null)
                        {
                            CategoryUtil.CreateMaterialAssociation(exporterIFC, typeHnd, materialLayerSet);
                        }
                        //else
                        //{
                        foreach (IFCAnyHandle elemHnd in elemHnds)
                        {
                            CategoryUtil.CreateMaterialAssociation(exporterIFC, elemHnd, materialLayerSet);
                        }
                        //}
                    }
                    else
                    {
                        // IfcMaterialLayerSetUsage is not supported for IfcWall, only IfcWallStandardCase.
                        IFCAnyHandle layerSetUsage = null;
                        for (int ii = 0; ii < elemHnds.Count; ii++)
                        {
                            IFCAnyHandle elemHnd = elemHnds[ii];
                            if (IFCAnyHandleUtil.IsNullOrHasNoValue(elemHnd))
                            {
                                continue;
                            }

                            SpaceBoundingElementUtil.RegisterSpaceBoundingElementHandle(exporterIFC, elemHnd, hostObject.Id, levelId);

                            // Even if it is Tessellated geometry in IFC4RV, the material layer will still be assigned
                            if (containsBRepGeometry && !ExporterCacheManager.ExportOptionsCache.ExportAs4ReferenceView)
                            {
                                continue;
                            }

                            HashSet <IFCAnyHandle> relDecomposesSet = IFCAnyHandleUtil.GetRelDecomposes(elemHnd);

                            IList <IFCAnyHandle> subElemHnds = null;
                            if (relDecomposesSet != null && relDecomposesSet.Count == 1)
                            {
                                IFCAnyHandle relAggregates = relDecomposesSet.First();
                                if (IFCAnyHandleUtil.IsTypeOf(relAggregates, IFCEntityType.IfcRelAggregates))
                                {
                                    subElemHnds = IFCAnyHandleUtil.GetAggregateInstanceAttribute <List <IFCAnyHandle> >(relAggregates, "RelatedObjects");
                                }
                            }

                            bool hasSubElems          = (subElemHnds != null && subElemHnds.Count != 0);
                            bool isRoof               = IFCAnyHandleUtil.IsTypeOf(elemHnd, IFCEntityType.IfcRoof);
                            bool isDisallowedWallType = (IFCAnyHandleUtil.IsTypeOf(elemHnd, IFCEntityType.IfcWall) && !ExporterCacheManager.ExportOptionsCache.ExportAs4);

                            // Create IfcMaterialLayerSetUsage unless we have sub-elements, are exporting a Roof, or are exporting a pre-IFC4 IfcWall.
                            if (!hasSubElems && !isRoof && !isDisallowedWallType)
                            {
                                bool materialAlreadyAssoc = false;
                                if (typeHnd != null)
                                {
                                    CategoryUtil.CreateMaterialAssociation(exporterIFC, typeHnd, materialLayerSet);
                                    //materialAlreadyAssoc = true;
                                }

                                if (ExporterCacheManager.ExportOptionsCache.ExportAs4ReferenceView)
                                {
                                    if (!materialAlreadyAssoc)
                                    {
                                        CategoryUtil.CreateMaterialAssociation(exporterIFC, elemHnd, materialLayerSet);
                                    }
                                }
                                else
                                {
                                    if (layerSetUsage == null)
                                    {
                                        bool flipDirSense = true;
                                        if (wall != null)
                                        {
                                            // if we have flipped the center curve on export, we need to take that into account here.
                                            // We flip the center curve on export if it is an arc and it has a negative Z direction.
                                            LocationCurve locCurve = wall.Location as LocationCurve;
                                            if (locCurve != null)
                                            {
                                                Curve     curve        = locCurve.Curve;
                                                Transform lcs          = Transform.Identity;
                                                bool      curveFlipped = GeometryUtil.MustFlipCurve(lcs, curve);
                                                flipDirSense = !(wall.Flipped ^ curveFlipped);
                                            }
                                        }
                                        else if (hostObject is CeilingAndFloor)
                                        {
                                            flipDirSense = false;
                                        }

                                        double            offsetFromReferenceLine = flipDirSense ? -scaledOffset : scaledOffset;
                                        IFCDirectionSense sense = flipDirSense ? IFCDirectionSense.Negative : IFCDirectionSense.Positive;

                                        layerSetUsage = IFCInstanceExporter.CreateMaterialLayerSetUsage(file, materialLayerSet, direction, sense, offsetFromReferenceLine);
                                    }
                                    ExporterCacheManager.MaterialLayerRelationsCache.Add(layerSetUsage, elemHnd);
                                }
                            }
                            else
                            {
                                if (hasSubElems)
                                {
                                    foreach (IFCAnyHandle subElemHnd in subElemHnds)
                                    {
                                        // TODO: still need to figure out the best way to create type for the sub elements because at this time a lot of information is not available, e.g.
                                        //    the Revit Element to get the type, other information for name, GUID, etc.
                                        if (!IFCAnyHandleUtil.IsNullOrHasNoValue(subElemHnd))
                                        {
                                            CategoryUtil.CreateMaterialAssociation(exporterIFC, subElemHnd, materialLayerSet);
                                        }
                                    }
                                }
                                else if (!isRoof)
                                {
                                    if (typeHnd != null)
                                    {
                                        CategoryUtil.CreateMaterialAssociation(exporterIFC, typeHnd, materialLayerSet);
                                    }
                                    else
                                    {
                                        CategoryUtil.CreateMaterialAssociation(exporterIFC, elemHnd, materialLayerSet);
                                    }
                                }
                                else if (primaryMaterialHnd != null)
                                {
                                    if (typeHnd != null)
                                    {
                                        CategoryUtil.CreateMaterialAssociation(exporterIFC, typeHnd, materialLayerSet);
                                    }
                                    else
                                    {
                                        CategoryUtil.CreateMaterialAssociation(exporterIFC, elemHnd, materialLayerSet);
                                    }
                                }
                            }
                        }
                    }
                }
                tr.Commit();
                return(true);
            }
        }
        /// <summary>
        /// Exports materials for host object.
        /// </summary>
        /// <param name="exporterIFC">The ExporterIFC object.</param>
        /// <param name="hostObject">The host object.</param>
        /// <param name="elemHnds">The host IFC handles.</param>
        /// <param name="geometryElement">The geometry element.</param>
        /// <param name="productWrapper">The ProductWrapper.</param>
        /// <param name="levelId">The level id.</param>
        /// <param name="direction">The IFCLayerSetDirection.</param>
        /// <param name="containsBRepGeometry">True if the geometry contains BRep geoemtry.  If so, we will export an IfcMaterialList</param>
        /// <returns>True if exported successfully, false otherwise.</returns>
        public static bool ExportHostObjectMaterials(ExporterIFC exporterIFC, HostObject hostObject,
                                                     IList <IFCAnyHandle> elemHnds, GeometryElement geometryElement, ProductWrapper productWrapper,
                                                     ElementId levelId, Toolkit.IFCLayerSetDirection direction, bool containsBRepGeometry)
        {
            if (hostObject == null)
            {
                return(true); //nothing to do
            }
            if (elemHnds == null || (elemHnds.Count == 0))
            {
                return(true); //nothing to do
            }
            IFCFile file = exporterIFC.GetFile();

            using (IFCTransaction tr = new IFCTransaction(file))
            {
                //if (productWrapper != null)
                //    productWrapper.ClearFinishMaterials();

                double scaledOffset = 0.0, scaledWallWidth = 0.0, wallHeight = 0.0;
                Wall   wall = hostObject as Wall;
                if (wall != null)
                {
                    scaledWallWidth = UnitUtil.ScaleLength(wall.Width);
                    scaledOffset    = -scaledWallWidth / 2.0;
                    BoundingBoxXYZ boundingBox = wall.get_BoundingBox(null);
                    if (boundingBox != null)
                    {
                        wallHeight = boundingBox.Max.Z - boundingBox.Min.Z;
                    }
                }

                //ElementId typeElemId = hostObject.GetTypeId();
                //IFCAnyHandle materialLayerSet = ExporterCacheManager.MaterialSetCache.Find(typeElemId);
                //// Roofs with no components are only allowed one material.  We will arbitrarily choose the thickest material.
                //IFCAnyHandle primaryMaterialHnd = ExporterCacheManager.MaterialSetCache.FindPrimaryMaterialHnd(typeElemId);
                //if (IFCAnyHandleUtil.IsNullOrHasNoValue(materialLayerSet))
                //{
                //    HostObjAttributes hostObjAttr = hostObject.Document.GetElement(typeElemId) as HostObjAttributes;
                //    if (hostObjAttr == null)
                //        return true; //nothing to do

                //    List<ElementId> matIds = new List<ElementId>();
                //    List<double> widths = new List<double>();
                //    List<MaterialFunctionAssignment> functions = new List<MaterialFunctionAssignment>();
                //    ElementId baseMatId = CategoryUtil.GetBaseMaterialIdForElement(hostObject);
                //    CompoundStructure cs = hostObjAttr.GetCompoundStructure();
                //    if (cs != null)
                //    {
                //        //TODO: Vertically compound structures are not yet supported by export.
                //        if (!cs.IsVerticallyHomogeneous() && !MathUtil.IsAlmostZero(wallHeight))
                //            cs = cs.GetSimpleCompoundStructure(wallHeight, wallHeight / 2.0);

                //        for (int i = 0; i < cs.LayerCount; ++i)
                //        {
                //            ElementId matId = cs.GetMaterialId(i);
                //            if (matId != ElementId.InvalidElementId)
                //            {
                //                matIds.Add(matId);
                //            }
                //            else
                //            {
                //                matIds.Add(baseMatId);
                //            }
                //            widths.Add(cs.GetLayerWidth(i));
                //            // save layer function into ProductWrapper,
                //            // it's used while exporting "Function" of Pset_CoveringCommon
                //            functions.Add(cs.GetLayerFunction(i));
                //        }
                //    }

                //    if (matIds.Count == 0)
                //    {
                //        matIds.Add(baseMatId);
                //        widths.Add(cs != null ? cs.GetWidth() : 0);
                //        functions.Add(MaterialFunctionAssignment.None);
                //    }

                //    // We can't create IfcMaterialLayers without creating an IfcMaterialLayerSet.  So we will simply collate here.
                //    IList<IFCAnyHandle> materialHnds = new List<IFCAnyHandle>();
                //    IList<int> widthIndices = new List<int>();
                //    double thickestLayer = 0.0;
                //    for (int ii = 0; ii < matIds.Count; ++ii)
                //    {
                //        // Require positive width for IFC2x3 and before, and non-negative width for IFC4.
                //        if (widths[ii] < -MathUtil.Eps())
                //            continue;

                //        bool almostZeroWidth = MathUtil.IsAlmostZero(widths[ii]);
                //        if (!ExporterCacheManager.ExportOptionsCache.ExportAs4 && almostZeroWidth)
                //            continue;

                //        if (almostZeroWidth)
                //            widths[ii] = 0.0;

                //        IFCAnyHandle materialHnd = CategoryUtil.GetOrCreateMaterialHandle(exporterIFC, matIds[ii]);
                //        if (primaryMaterialHnd == null || (widths[ii] > thickestLayer))
                //        {
                //            primaryMaterialHnd = materialHnd;
                //            thickestLayer = widths[ii];
                //        }

                //        widthIndices.Add(ii);
                //        materialHnds.Add(materialHnd);

                //        if ((productWrapper != null) && (functions[ii] == MaterialFunctionAssignment.Finish1 || functions[ii] == MaterialFunctionAssignment.Finish2))
                //        {
                //            productWrapper.AddFinishMaterial(materialHnd);
                //        }
                //    }

                //    int numLayersToCreate = widthIndices.Count;
                //    if (numLayersToCreate == 0)
                //        return false;

                //    if (!containsBRepGeometry)
                //    {
                //        IList<IFCAnyHandle> layers = new List<IFCAnyHandle>(numLayersToCreate);

                //        for (int ii = 0; ii < numLayersToCreate; ii++)
                //        {
                //            int widthIndex = widthIndices[ii];
                //            double scaledWidth = UnitUtil.ScaleLength(widths[widthIndex]);
                //            IFCAnyHandle materialLayer = IFCInstanceExporter.CreateMaterialLayer(file, materialHnds[ii], scaledWidth, null);
                //            layers.Add(materialLayer);
                //        }

                //        string layerSetName = exporterIFC.GetFamilyName();
                //        materialLayerSet = IFCInstanceExporter.CreateMaterialLayerSet(file, layers, layerSetName);

                //        ExporterCacheManager.MaterialSetCache.Register(typeElemId, materialLayerSet);
                //        ExporterCacheManager.MaterialSetCache.RegisterPrimaryMaterialHnd(typeElemId, primaryMaterialHnd);
                //    }

                List <ElementId> matIds;
                IFCAnyHandle     primaryMaterialHnd;
                IFCAnyHandle     materialLayerSet = ExporterUtil.CollectMaterialLayerSet(exporterIFC, hostObject, productWrapper, out matIds, out primaryMaterialHnd);
                if (containsBRepGeometry && matIds.Count > 0)
                {
                    foreach (IFCAnyHandle elemHnd in elemHnds)
                    {
                        CategoryUtil.CreateMaterialAssociation(exporterIFC, elemHnd, matIds);
                    }
                }

                if (!IFCAnyHandleUtil.IsNullOrHasNoValue(materialLayerSet))
                {
                    // IfcMaterialLayerSetUsage is not supported for IfcWall, only IfcWallStandardCase.
                    IFCAnyHandle layerSetUsage = null;
                    for (int ii = 0; ii < elemHnds.Count; ii++)
                    {
                        IFCAnyHandle elemHnd = elemHnds[ii];
                        if (IFCAnyHandleUtil.IsNullOrHasNoValue(elemHnd))
                        {
                            continue;
                        }

                        SpaceBoundingElementUtil.RegisterSpaceBoundingElementHandle(exporterIFC, elemHnd, hostObject.Id, levelId);
                        if (containsBRepGeometry)
                        {
                            continue;
                        }

                        HashSet <IFCAnyHandle> relDecomposesSet = IFCAnyHandleUtil.GetRelDecomposes(elemHnd);

                        IList <IFCAnyHandle> subElemHnds = null;
                        if (relDecomposesSet != null && relDecomposesSet.Count == 1)
                        {
                            IFCAnyHandle relAggregates = relDecomposesSet.First();
                            if (IFCAnyHandleUtil.IsTypeOf(relAggregates, IFCEntityType.IfcRelAggregates))
                            {
                                subElemHnds = IFCAnyHandleUtil.GetAggregateInstanceAttribute <List <IFCAnyHandle> >(relAggregates, "RelatedObjects");
                            }
                        }

                        bool hasSubElems          = (subElemHnds != null && subElemHnds.Count != 0);
                        bool isRoof               = IFCAnyHandleUtil.IsTypeOf(elemHnd, IFCEntityType.IfcRoof);
                        bool isDisallowedWallType = (IFCAnyHandleUtil.IsTypeOf(elemHnd, IFCEntityType.IfcWall) && !ExporterCacheManager.ExportOptionsCache.ExportAs4);

                        // Create IfcMaterialLayerSetUsage unless we have sub-elements, are exporting a Roof, or are exporting a pre-IFC4 IfcWall.
                        if (!hasSubElems && !isRoof && !isDisallowedWallType)
                        {
                            if (layerSetUsage == null)
                            {
                                bool flipDirSense = true;
                                if (wall != null)
                                {
                                    // if we have flipped the center curve on export, we need to take that into account here.
                                    // We flip the center curve on export if it is an arc and it has a negative Z direction.
                                    LocationCurve locCurve = wall.Location as LocationCurve;
                                    if (locCurve != null)
                                    {
                                        Curve curve        = locCurve.Curve;
                                        Plane defPlane     = new Plane(XYZ.BasisX, XYZ.BasisY, XYZ.Zero);
                                        bool  curveFlipped = GeometryUtil.MustFlipCurve(defPlane, curve);
                                        flipDirSense = !(wall.Flipped ^ curveFlipped);
                                    }
                                }
                                else if (hostObject is Floor)
                                {
                                    flipDirSense = false;
                                }

                                double            offsetFromReferenceLine = flipDirSense ? -scaledOffset : scaledOffset;
                                IFCDirectionSense sense = flipDirSense ? IFCDirectionSense.Negative : IFCDirectionSense.Positive;

                                layerSetUsage = IFCInstanceExporter.CreateMaterialLayerSetUsage(file, materialLayerSet, direction, sense, offsetFromReferenceLine);
                            }
                            ExporterCacheManager.MaterialLayerRelationsCache.Add(layerSetUsage, elemHnd);
                        }
                        else
                        {
                            if (hasSubElems)
                            {
                                foreach (IFCAnyHandle subElemHnd in subElemHnds)
                                {
                                    if (!IFCAnyHandleUtil.IsNullOrHasNoValue(subElemHnd))
                                    {
                                        ExporterCacheManager.MaterialLayerRelationsCache.Add(materialLayerSet, subElemHnd);
                                    }
                                }
                            }
                            else if (!isRoof)
                            {
                                ExporterCacheManager.MaterialLayerRelationsCache.Add(materialLayerSet, elemHnd);
                            }
                            else if (primaryMaterialHnd != null)
                            {
                                ExporterCacheManager.MaterialLayerRelationsCache.Add(primaryMaterialHnd, elemHnd);
                            }
                        }
                    }
                }
                tr.Commit();
                return(true);
            }
        }