private IElement CreateElement(RenderContext renderContext, XmlNode currentNode, IEnumerable<Type> availableTypes)
        {
            var nodeName = currentNode.LocalName;
            Type controlType = availableTypes.SingleOrDefault(a => a.GetCustomAttributes(typeof(ElementNameAttribute), false).Cast<ElementNameAttribute>().Any(c => c.ElementName.Equals(nodeName)));

            if (controlType == null)
            {
                throw new ControlNotSupportedException($"Control {nodeName} is not supported.");
            }

            IElement element = (IElement)Activator.CreateInstance(controlType, renderContext);

            foreach(XmlAttribute attribute in currentNode.Attributes)
            {
                // skip namespaced attributes, they belong to other implementations.
                if (attribute.Name.Contains(":"))
                    continue;

               PropertyInfo property = controlType.GetProperties().SingleOrDefault(p => p.GetCustomAttributes(typeof(AttributeNameAttribute), false).Cast<AttributeNameAttribute>().Any(c => c.Name.Equals(attribute.Name)));

                if (property != null)
                {
                    if (property.PropertyType == typeof(Boolean))
                    {
                        property.SetValue(element, Boolean.Parse(attribute.Value));
                    }
                    else
                    {
                        property.SetValue(element, attribute.Value);
                    }
                    
                }
                else
                {
                    throw new AttributeNotSupportedException($"Control {nodeName} is does not support attribute {attribute.Name}.");
                }
            }

            IElementContainer container = element as IElementContainer;

            if (container != null)
            {
                var childNodes = currentNode.ChildNodes;

                foreach (XmlNode node in childNodes)
                {
                    container.Elements.Add(CreateElement(renderContext, node, availableTypes));
                }

                return container;
            }

            return element;
            
        }          
        public IWebControlTemplate BindTemplate(IElement element, RenderContext renderContext)
        {
            Type type = element.GetType();

            var types = this.GetType().Assembly.GetTypes().Where(t => typeof(IWebControlTemplate).IsAssignableFrom(t));

            Type template = types.Where(t => t.GetInterfaces().Any(i => i.IsGenericType && i.GenericTypeArguments.All(a => a.Equals(type)))).Single(t => typeof(IWebControlTemplate).IsAssignableFrom(t));

            var templateInstance = (IWebControlTemplate)Activator.CreateInstance(template, element, renderContext);

            var collectionTemplate = templateInstance as IWebControlCollectionTemplate;
            
            if (collectionTemplate != null)
            {
                var elementContainer = element as IElementContainer;
                foreach(IElement childElement in elementContainer.Elements)
                {
                    collectionTemplate.ChildTemplates.Add(BindTemplate(childElement, renderContext));
                }
            }

            return templateInstance;
        }
        public WebRequestOutput Create(string applicationPath, Uri uri, XmlDocument xmlDocument)
        {
            if (uri == null)
                throw new ArgumentNullException(nameof(uri));
            if (xmlDocument == null)
                throw new ArgumentNullException(nameof(xmlDocument));

            StringBuilder stringBuilder = new StringBuilder();
            TextWriter textWriter = new StringWriter(stringBuilder);
            RenderContext renderContext = new RenderContext(new HtmlTextWriter(textWriter), referenceService, uri, applicationPath);

            WebRequestOutput output = new WebRequestOutput { ContentType = "text/html" };

            IEnumerable<Type> availableTypes = this.GetType().Assembly.GetTypes().Where(t => typeof(IElement).IsAssignableFrom(t) && t.IsAbstract == false && t.IsInterface == false);

            var currentNode = xmlDocument.SelectSingleNode("/*[1]");

            try
            {
                var element = CreateElement(renderContext, currentNode, availableTypes);
                var template = controlTemplateBinder.BindTemplate(element, renderContext);

                // TODO: Initialization to add scripts.
                template.Render();                

                output.Response = stringBuilder.ToString();
                renderContext.HtmlTextWriter.Close();

            }
            catch(ControlNotSupportedExecption cne)
            {
                output.Error = new ControlNotSupportedError(cne.NodeName);
            }
            return output;

        }