/// <summary>
        /// Takes features in the KML element and converts them into equivalent features
        /// and adds them to the FeatureDefinition.
        /// Only the direct children of the KML element are converted.
        /// </summary>
		/// <param name="context">Context containing the XElement with the KML definition to be converted.</param>
		/// <param name="credentials">The credentials.</param>
		/// <returns></returns>
        public FeatureDefinition Convert(KmlLayerContext context, System.Net.ICredentials credentials = null)
        {
			XElement xElement = context.Element;
			XNamespace kmlNS = xElement.Name.Namespace;
			_waitHelper.Reset();

            // Remove any existing features so only those contained in the input KML element file are stored
            featureDefs.Clear();

			// Process the styles if they are not already known (the styles are shared by all folders/documents, so process them only once)
			if (context.Styles == null)
			{
				featureDefs.styles = new Dictionary<string, KMLStyle>();

				// Find all Style elements that have an ID and can thus be referenced by other styleURLs.
				IEnumerable<XElement> styles = xElement.Descendants().Where(e => e.Name.LocalName == "Style" && (string)e.Attribute("id") != null);
				foreach (XElement style in styles)
				{
					KMLStyle kmlStyle = new KMLStyle();
					GetStyle(style, kmlStyle);
					featureDefs.AddStyle(kmlStyle.StyleId, kmlStyle);
				}

				// Find all StyleMap elements that have an ID and can thus be referenced by other styleURLs.
				IEnumerable<XElement> styleMaps = xElement.Descendants().Where(e => e.Name.LocalName == "StyleMap" && (string)e.Attribute("id") != null);
				foreach (XElement map in styleMaps)
				{
					// A style map may need to download styles in other documents.
					// Need to use asynchronous pattern.
					GetStyleMapAsync(map, null, credentials
					                 , kmlStyle =>
					                   	{
					                   		if (kmlStyle != null)
					                   			featureDefs.AddStyle(kmlStyle.StyleId, kmlStyle);
					                   	});
				}

				// Wait for getting all styles before creating the feature definition
				_waitHelper.Wait();
			}
			else
			{
				foreach (var style in context.Styles)
					featureDefs.AddStyle(style.Key, style.Value);
			}

			// Process the optional NetworkLinkControl
			XElement networkLinkControl = xElement.Element(kmlNS + "NetworkLinkControl");
			if (networkLinkControl != null)
			{
				featureDefs.networkLinkControl = new NetworkLinkControl();
				XElement minRefreshPeriod = networkLinkControl.Element(kmlNS + "minRefreshPeriod");
				if (minRefreshPeriod != null)
					featureDefs.networkLinkControl.MinRefreshPeriod = GetDoubleValue(minRefreshPeriod);
			}

			// Find the containers which will be represented by a sublayer i.e Document/Folder/NetworkLink
			foreach (XElement container in xElement.Elements().Where(element => element.Name.LocalName == "Folder" || element.Name.LocalName == "Document" || element.Name.LocalName == "NetworkLink"))
			{
				ContainerInfo containerInfo = new ContainerInfo
				                         	{
				                         		Element = container,
				                         		Url = null, // only for networklink
				                         		Visible = true,
				                         		AtomAuthor = context.AtomAuthor, // Use parent value by default
				                         		AtomHref = context.AtomHref  // Use parent value by default
				                         	};

				XNamespace kmlContainerNS = container.Name.Namespace;
				if (container.Name.LocalName == "NetworkLink")
				{
					string hrefValue = "";
					string composite = "";
					string layerids = "";

					// Link takes precedence over Url from KML version 2.1 and later:
					XElement url = container.Element(kmlContainerNS + "Link") ?? container.Element(kmlContainerNS + "Url");
					if (url != null)
					{
						XElement href = url.Element(kmlContainerNS + "href");
						if (href != null)
						{
							hrefValue = href.Value;
						}

						// This next section is to parse special elements that only occur when an ArcGIS Server KML 
						// is to be processed.
						XElement view = url.Element(kmlContainerNS + "viewFormat");
						if (view != null)
						{
							int begIdx = view.Value.IndexOf("Composite");
							if (begIdx != -1)
							{
								int endIdx = view.Value.IndexOf("&", begIdx);
								if (endIdx != -1)
									composite = view.Value.Substring(begIdx, endIdx - begIdx);
							}

							begIdx = view.Value.IndexOf("LayerIDs");
							if (begIdx != -1)
							{
								int endIdx = view.Value.IndexOf("&", begIdx);
								if (endIdx != -1)
									layerids = view.Value.Substring(begIdx, endIdx - begIdx);
							}
						}

						// If network link URL is successfully extracted, then add to container list
						if (!String.IsNullOrEmpty(hrefValue))
						{
							// extract refreshInterval
							XElement refreshMode = url.Element(kmlContainerNS + "refreshMode");
							if (refreshMode != null && refreshMode.Value == "onInterval")
							{
								XElement refreshInterval = url.Element(kmlContainerNS + "refreshInterval");
								if (refreshInterval != null)
									containerInfo.RefreshInterval = GetDoubleValue(refreshInterval);
								else
									containerInfo.RefreshInterval = 4; // default value 
							}


							// the following values are for processing specialized ArcGIS Server KML links
							// generated from REST endpoints.
							if (!String.IsNullOrEmpty(composite))
								hrefValue += "?" + composite;

							if (!String.IsNullOrEmpty(layerids))
							{
								if (!String.IsNullOrEmpty(hrefValue))
									hrefValue += "&" + layerids;
								else
									hrefValue += "?" + layerids;
							}
							containerInfo.Url = hrefValue;

						}
						else
							containerInfo = null; // Link without href. Should not happen. Skip it.
					}
					else
						containerInfo = null; // NetworkLink without Link/Url. Should not happen. Skip it.
				}
				else
				{
					// Folder or Document XElement 
					XElement linkElement = container.Elements(atomNS + "link").Where(element => element.HasAttributes).FirstOrDefault();
					if (linkElement != null)
					{
						// Overwrite global default value only upon successful extraction from element
						string tempHref = GetAtomHref(linkElement);
						if (!String.IsNullOrEmpty(tempHref))
							containerInfo.AtomHref = new Uri(tempHref);
					}

					XElement authorElement = container.Element(atomNS + "author");
					if (authorElement != null)
					{
						// Overwrite global default value only upon successful extraction from element
						string tempAuthor = GetAtomAuthor(authorElement);
						if (!String.IsNullOrEmpty(tempAuthor))
							containerInfo.AtomAuthor = tempAuthor;
					}
				}

				if (containerInfo != null)
				{
					XElement visibilityElement = container.Element(kmlContainerNS + "visibility");
					if (visibilityElement != null)
					{
						containerInfo.Visible = GetBooleanValue(visibilityElement);
					}

					XElement nameElement = container.Element(kmlContainerNS + "name");
					if (nameElement != null)
					{
						containerInfo.Name = nameElement.Value;
					}

					if (container.HasAttributes && container.Attribute(KmlLayer.FolderIdAttributeName) != null)
					{
						containerInfo.FolderId = (int)container.Attribute(KmlLayer.FolderIdAttributeName);
					}

					featureDefs.AddContainer(containerInfo);
				}
			}


            // Process all children placemarks or groundoverlays
			foreach (XElement element in xElement.Elements().Where(element => element.Name == kmlNS + "Placemark" || element.Name == kmlNS + "GroundOverlay" ))
            {
                // Establish baseline style if a "styleUrl" setting is present
                XElement styleElement = element.Element(kmlNS + "styleUrl");
				if (styleElement != null)
				{
					// get the style asynchronously and create the feature definition as soon as the style is there
					XElement featureElement = element;
					GetStyleUrlAsync(styleElement.Value, null, credentials, kmlStyle => CreateFeatureDefinition(kmlStyle, featureElement, null, context));
				}
				else
				{
					// Create feature definition synchronously using default KML style, meta data and placemark information
					CreateFeatureDefinition(null, element, null, context);
				}
            }

			// Get the name of the XElement
			XElement nameXElement = xElement.Element(kmlNS + "name");
			if (nameXElement != null && string.IsNullOrEmpty(featureDefs.name))
			{
				featureDefs.name = nameXElement.Value;
			}

			// At this point, some inner styles are possibly on the way to being downloaded and so the feature definitions are not created yet
			// Wait for all downloads to be sure all feature definitions are created before terminating the background worker
        	_waitHelper.Wait();

        	int folderId = 0;
			if (xElement.HasAttributes && xElement.Attribute(KmlLayer.FolderIdAttributeName) != null)
			{
				folderId = (int)xElement.Attribute(KmlLayer.FolderIdAttributeName);
			}

			if (!featureDefs.groundOverlays.Any() && !featureDefs.placemarks.Any() && featureDefs.containers.Count() == 1 && folderId == 0
				&& string.IsNullOrEmpty(featureDefs.containers.First().Url))
			{
				// Avoid useless level when there is no groundoverlay, no placemark and only one folder or document at the root level
				ContainerInfo container = featureDefs.containers.First();
				Dictionary<string, KMLStyle> styles = featureDefs.styles.ToDictionary(style => style.Key, style => style.Value);

				KmlLayerContext childContext = new KmlLayerContext
						                      	{
						                      		Element = container.Element, // The XElement that the KML layer has to process
						                      		Styles = styles,
						                      		Images = context.Images,
						                      		AtomAuthor = container.AtomAuthor,
						                      		AtomHref = container.AtomHref
						                      	};

				featureDefs.hasRootContainer = true;
				return Convert(childContext, credentials);
			}

			return featureDefs;
        }
		/// <summary>
		/// Adds a container to the list.
		/// </summary>
		/// <param name="container">The container.</param>
		public void AddContainer(ContainerInfo container)
		{
			containers.Add(container);
		}