package eu.dnetlib.common.profile.utils;


import java.io.IOException;
import java.io.StringReader;
import java.util.ArrayList;
import java.util.List;

import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
import javax.xml.parsers.ParserConfigurationException;

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.w3c.dom.Document;
import org.w3c.dom.Element;
import org.w3c.dom.NodeList;
import org.xml.sax.InputSource;
import org.xml.sax.SAXException;

import eu.dnetlib.common.interfaces.nh.IBlackboardMessage;
import eu.dnetlib.common.profile.BlackboardBody;
import eu.dnetlib.common.profile.Profile;
import eu.dnetlib.common.profile.ProfileHeader;
import eu.dnetlib.common.profile.blackboard.Blackboard;
import eu.dnetlib.common.profile.blackboard.BlackboardLastAction;
import eu.dnetlib.common.profile.blackboard.Message;
import eu.dnetlib.common.profile.blackboard.Parameter;

public class Unmarshaller {

	private static final Log log = LogFactory.getLog(Unmarshaller.class);
	
	protected static DocumentBuilder db = null;

	static {
        DocumentBuilderFactory factory =
            DocumentBuilderFactory.newInstance();
        factory.setIgnoringComments(true);
        factory.setValidating(false);
        try {
			db = factory.newDocumentBuilder();
		} catch (ParserConfigurationException e) {
			log.error("Problem occured when creating document builder!",e);
		}

	}

	public static Profile unmarshallServiceResource(String profileContent) {
		try {
			if (profileContent==null)
				return null;
			Document doc = db.parse(new InputSource(new StringReader(profileContent)));
			Element root = doc.getDocumentElement(); 
			if (root==null || !root.getNodeName().equals("RESOURCE_PROFILE")) {
				log.info("Couldn't find RESOURCE_PROFILE as a root element!");
				return null;
			}
			NodeList headerList = root.getElementsByTagName("HEADER");
			if (headerList==null || headerList.getLength()!=1) {
				log.error("Couldn't find one HEADER element within profile!");
				return null;
			}
			NodeList bodyList = root.getElementsByTagName("BODY");
			if (bodyList==null || bodyList.getLength()!=1) {
				log.error("Couldn't find one BODY element within profile!");
				return null;
			}
			Profile profile = new Profile();
			profile.setHeader(unmarshallHeader((Element) headerList.item(0)));
			profile.setBody(unmarshallServiceBody((Element) bodyList.item(0)));
			
			return profile;
		} catch (SAXException e) {
			log.error("Exception occured when getting document element!",e);
			return null;
		} catch (IOException e) {
			log.error("Exception occured when getting document element!",e);
			return null;
		} 
	}
	
	protected static ProfileHeader unmarshallHeader(Element headerElement) {
		if (headerElement==null)
			return null;
		String resourceIdentifier = getSingleElementValueAttr(headerElement, "RESOURCE_IDENTIFIER");
		String resourceType = getSingleElementValueAttr(headerElement, "RESOURCE_TYPE");
		String resourceKind = getSingleElementValueAttr(headerElement, "RESOURCE_KIND");
		ProfileHeader profileHeader = new ProfileHeader(resourceIdentifier,resourceType
				, resourceKind);
		profileHeader.setResourceURI(getSingleElementValueAttr(headerElement, "RESOURCE_URI"));
		profileHeader.setDateOfCreation(getSingleElementValueAttr(headerElement, "DATE_OF_CREATION"));
		profileHeader.setParentId(getSingleElementValueAttr(headerElement, "PARENT_ID"));
		profileHeader.setParentType(getSingleElementAttribute(headerElement, "PARENT_ID", "type"));
		return profileHeader;
	}
	
	private static BlackboardBody unmarshallServiceBody(Element bodyElement) {
		if (bodyElement==null)
			return null;
		BlackboardBody profileBody = new BlackboardBody();
		profileBody.setBlackboard(unmarshallBlackboard(getSingleElement(bodyElement, 
				"BLACKBOARD")));
		return profileBody;
	}
	
	private static Blackboard unmarshallBlackboard(Element blackboardElement) {
		if (blackboardElement==null)
			return null;
		Blackboard blackboard = new Blackboard();
		blackboard.setLastRequest(unmarshallLastAction(getSingleElement(blackboardElement, 
				"LAST_REQUEST")));
		blackboard.setLastResponse(unmarshallLastAction(getSingleElement(blackboardElement, 
		"LAST_RESPONSE")));
		blackboard.setMessages(unmarshallMessages(
				blackboardElement.getElementsByTagName("MESSAGE")));
		return blackboard;
	}
	
	private static List<IBlackboardMessage> unmarshallMessages(NodeList messageNodes) {
		if (messageNodes==null || messageNodes.getLength()==0)
			return null;
		List<IBlackboardMessage> messages = new ArrayList<IBlackboardMessage>();
		for (int i=0; i<messageNodes.getLength(); i++) {
			Message currentMessage = unmarshallMessage((Element) messageNodes.item(i));
			if (currentMessage!=null)
				messages.add(currentMessage);
		}
		return messages;
	}
	
	private static Message unmarshallMessage(Element messageElement) {
		if (messageElement==null)
			return null;
		Message message = new Message();
		message.setDate(messageElement.getAttribute("date"));
		message.setId(messageElement.getAttribute("id"));
		String actionValue = getElementContent(getSingleElement(messageElement, "ACTION"));
		try {
			if (actionValue!=null)
				message.setAction(Message.Action.valueOf(actionValue));
		} catch (IllegalArgumentException e) {
			log.error("Couldn't set action, invalid action value: "+actionValue);
		}
		String actionStatusValue = getElementContent(getSingleElement(messageElement, "ACTION_STATUS"));
		try {
			if (actionStatusValue!=null)
				message.setActionStatus(Message.ActionStatus.valueOf(actionStatusValue));
		} catch (IllegalArgumentException e) {
			log.error("Couldn't set action status, invalid action status value: "+actionStatusValue);
		}
		message.setParamList(unmarshallParameters(
				messageElement.getElementsByTagName("PARAMETER")));
		return message;	
	}
	
	private static List<Parameter> unmarshallParameters(NodeList paramNodes) {
		if (paramNodes==null || paramNodes.getLength()==0)
			return null;
		List<Parameter> params = new ArrayList<Parameter>();
		for (int i=0; i<paramNodes.getLength(); i++) {
			Parameter currentParam = unmarshallParameter((Element) paramNodes.item(i));
			if (currentParam!=null)
				params.add(currentParam);
		}
		return params;
	}
	
	private static Parameter unmarshallParameter(Element paramElement) {
		if (paramElement==null)
			return null;
		return new Parameter(paramElement.getAttribute("name"),
				paramElement.getAttribute("value"));
		
	}
	
	private static BlackboardLastAction unmarshallLastAction(Element lastActionElement) {
		if (lastActionElement==null)
			return null;
		return new BlackboardLastAction(lastActionElement.getAttribute("date"),
				getElementContent(lastActionElement));
	}
	
	protected static String getElementContent(Element element) {
		if (element==null)
			return null;
		if (element.getFirstChild()!=null)
			return element.getFirstChild().getTextContent();
		else
			return null;
		
	}

	protected static Element getSingleElement(Element root, String elementName) {
		if (root==null || elementName==null)
			return null;
		NodeList nodeList = root.getElementsByTagName(elementName);
		if (nodeList==null || nodeList.getLength()==0)
			return null;
		return (Element) nodeList.item(0);
	}

	private static String getSingleElementValueAttr(Element root, String elementName) {
		Element el = getSingleElement(root, elementName);
		if (el!=null)
			return el.getAttribute("value");
		else
			return null;
	}

	private static String getSingleElementAttribute(Element root, String elementName, 
			String attrName) {
		if (attrName==null)
			return null;
		Element el = getSingleElement(root, elementName);
		if (el!=null)
			return el.getAttribute(attrName);
		else
			return null;
	}


}
