package eu.dnetlib.contract.spring.beans;

import org.springframework.beans.factory.config.BeanDefinition;
import org.springframework.beans.factory.config.BeanDefinitionHolder;
import org.springframework.beans.factory.support.BeanDefinitionBuilder;
import org.springframework.beans.factory.xml.AbstractSingleBeanDefinitionParser;
import org.springframework.beans.factory.xml.BeanDefinitionParserDelegate;
import org.springframework.beans.factory.xml.ParserContext;
import org.w3c.dom.Element;
import org.w3c.dom.Node;
import org.w3c.dom.NodeList;

/**
 * Abstract contract bean definition parser.
 * Contains common utilities methods for parsing bean definitions.
 * @author mhorst
 *
 */
public abstract class AbstractContractBeanDefinitionParser extends AbstractSingleBeanDefinitionParser {
	
	/* (non-Javadoc)
	 * @see org.springframework.beans.factory.xml.AbstractSingleBeanDefinitionParser#doParse(org.w3c.dom.Element, org.springframework.beans.factory.xml.ParserContext, org.springframework.beans.factory.support.BeanDefinitionBuilder)
	 */
	@Override
	protected void doParse(Element element, ParserContext parserContext,
			BeanDefinitionBuilder builder) {
		super.doParse(element, parserContext, builder);
	}
	
	/**
	 * Checks if attribute is set.
	 * @param attrVal
	 * @return true if attribute is set
	 */
	public static boolean isAttributeSet(String attrVal) {
		return (attrVal!=null && attrVal.length()>0);
	}
	
	
	
	/**
	 * Sets first child of Element as a property.
	 * Part of org.apache.cxf.jaxws.spring.EndpointDefinitionParser source.
	 * @param element
	 * @param ctx
	 * @param bean
	 * @param propertyName
	 */
	protected void setFirstChildAsProperty(Element element, ParserContext ctx,
			BeanDefinitionBuilder bean, String propertyName) {
		
		Element first = getFirstChild(element);

		if (first == null) {
			throw new IllegalStateException(propertyName
					+ " property must have child elements!");
		}

		// Seems odd that we have to do the registration, I wonder if there is a better way
		String id;
		BeanDefinition child;
		if (first.getNamespaceURI().equals(
				BeanDefinitionParserDelegate.BEANS_NAMESPACE_URI)) {
			String name = first.getLocalName();
//			FIXME: local and parent elements are unsupported
			if ("ref".equals(name)) {
				String beanId = first.getAttribute("bean");
				if (beanId == null) {
					throw new IllegalStateException(
							"<ref> elements must have \"bean\" or \"local\" attribute!");
				} else {
					bean.addPropertyReference(propertyName, beanId);
					return;
				}
			} else if ("bean".equals(name)) {
				BeanDefinitionHolder bdh = ctx.getDelegate()
						.parseBeanDefinitionElement(first);
				child = bdh.getBeanDefinition();
				id = bdh.getBeanName();
			} else if ("value".equals(name)) {
				Object resultValue = ctx.getDelegate()
				.parseValueElement(first, null);
				bean.addPropertyValue(propertyName, resultValue);
				return;
			} else if ("null".equals(name)) {
//				simple support for null plug element
				bean.addPropertyValue(propertyName, null);
				return;
			} else {
				throw new UnsupportedOperationException(
						"Elements with the name <" + name
								+ "> are not currently "
								+ "supported as sub elements of <"
								+ element.getLocalName()
								+ ">");
			}

		} else {
			child = ctx.getDelegate().parseCustomElement(first,
					bean.getBeanDefinition());
//			FIXME this was taken from cxf example, but it seems to be tricky!
//			why id is generated in that strange way? 
			id = child.toString();
		}
		ctx.getRegistry().registerBeanDefinition(id, child);
		bean.addPropertyReference(propertyName, id);

	}

	/**
	 * Returns first child of element.
	 * Part of org.apache.cxf.jaxws.spring.EndpointDefinitionParser source.
	 * @param element
	 * @return first child of element
	 */
	protected Element getFirstChild(Element element) {
		Element first = null;
		NodeList children = element.getChildNodes();
		for (int i = 0; i < children.getLength(); i++) {
			Node n = children.item(i);
			if (n.getNodeType() == Node.ELEMENT_NODE) {
				first = (Element) n;
			}
		}
		return first;
	}
	
}
