package eu.dnetlib.utils.parser;

import java.io.StringReader;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.Date;

import javax.xml.xpath.XPath;
import javax.xml.xpath.XPathConstants;
import javax.xml.xpath.XPathExpression;
import javax.xml.xpath.XPathExpressionException;
import javax.xml.xpath.XPathFactory;

import org.apache.log4j.Logger;
import org.w3c.dom.NodeList;
import org.xml.sax.InputSource;

public class Parser {
	private String xml = null;
	private XPathFactory factory = XPathFactory.newInstance();
	private XPath xPath = factory.newXPath();
	
	private Logger logger = Logger.getLogger(Parser.class);
	
	public Parser(String xml) {
		this.xml = xml;
	}
	
	public String getValue(String field) {
		NodeList nodes = extractXPath(field);
		if (nodes != null && nodes.getLength() > 0) {
			return nodes.item(0).getAttributes().getNamedItem("value").getNodeValue();
		}
		
		return null;
	}
	
	public String getValue(String field, String[] values) {		
		if (field.split("\\.").length <= values.length) {
			logger.debug("Wrong value request");
			return null;
		}
		
		NodeList nodes = extractXPath(field, values);	
		if (nodes != null && nodes.getLength() > 0) {
			return nodes.item(0).getAttributes().getNamedItem("value").getNodeValue();
		}
		
		return null;
	}
	
	public Boolean getBoolean(String field) {
		NodeList nodes = extractXPath(field);
		if (nodes != null && nodes.getLength() > 0) {			
			return Boolean.parseBoolean(nodes.item(0).getAttributes().getNamedItem("value").getNodeValue());
		}
		
		return null;
	}
	
	public Date getDate(String field, String format) {
		NodeList nodes = extractXPath(field);
		try {
			if (nodes != null && nodes.getLength() > 0) {
				return new SimpleDateFormat(format).parse(nodes.item(0).getAttributes().getNamedItem("value").getNodeValue());
			}
		} catch (ParseException pe) {
			logger.debug("Parsing of date failed. ", pe);
			
		} catch (IllegalArgumentException iae) {
			logger.debug("Parsing of date failed. ", iae);
		}
		
		return null;
	}
	
	public Integer getInteger(String field) {
		NodeList nodes = extractXPath(field);
		try {
			if (nodes != null && nodes.getLength() > 0) {
				return Integer.parseInt(nodes.item(0).getAttributes().getNamedItem("value").getNodeValue());
			}
			
		} catch (NumberFormatException pe) {
			logger.debug("Parsing integer failed. " +  pe);
		}
			
		return null;
	}
	
	public String[] getValues(String field) {
		NodeList nodes = extractXPath(field);
		
		if (nodes != null && nodes.getLength() > 0) { 
			String[] values = new String[nodes.getLength()];			
			for (int i = 0; i < nodes.getLength(); i++) {
				values[i] = nodes.item(i).getAttributes().getNamedItem("value").getNodeValue();
			}
			return values;
		}
		
		return null;
	}
	
	public String[] getValues(String field, String[] values) {		
		if (field.split("\\.").length <= values.length) {
			logger.debug("Wrong value request");
			return null;
		}
		
		NodeList nodes = extractXPath(field, values);	
		if (nodes != null && nodes.getLength() > 0) {
			String[] fieldvalues = new String[nodes.getLength()];			
			for (int i = 0; i < nodes.getLength(); i++) {
				fieldvalues[i] = nodes.item(i).getAttributes().getNamedItem("value").getNodeValue();
			}			
			return fieldvalues;
		}		
		return null;
	}
	

	public Integer[] getIntegers(String field) {
		NodeList nodes = extractXPath(field);
			if (nodes != null && nodes.getLength() > 0) { 
				Integer[] values = new Integer[nodes.getLength()];			
				for (int i = 0; i < nodes.getLength(); i++) {
					try {
						values[i] = Integer.parseInt(nodes.item(i).getAttributes().getNamedItem("value").getNodeValue());
					} catch (NumberFormatException pe) {
						values[i] = null;
						logger.debug("Parsing integer failed. " +  pe);
					}
				}
				return values;
			}
		
		return null;
	}
	
	private NodeList extractXPath(String field) {
		String expression = "/result";		
		String[] fieldnames = field.split("\\.");
		
		for ( int i=0; i < fieldnames.length; i++ ) {
			expression += "/field[@name=\"" + fieldnames[i] +"\"]";
		}
		
		try {
			XPathExpression xPathExpression  = xPath.compile(expression);
			NodeList nodes = (NodeList) xPathExpression.evaluate(new InputSource(new StringReader(xml)), XPathConstants.NODESET);
			return nodes;
			
		} catch (XPathExpressionException e) {
			return null;
		}
	}
	
	private NodeList extractXPath(String field, Object values) {
		String expression = "/result";		
		String[] fieldnames = field.split("\\.");
		String[] fieldvalues = (String[]) values;
		
		for ( int i=0; i < fieldnames.length; i++ ) {
			if (i < fieldnames.length-1) {
				expression += "/field[@name=\"" + fieldnames[i] +"\"]" + "[@value=\"" + fieldvalues[i] + "\"]";
			} else {
				expression += "/field[@name=\"" + fieldnames[i] +"\"]";
			}
		}

		try {
			XPathExpression xPathExpression  = xPath.compile(expression);
			NodeList nodes = (NodeList) xPathExpression.evaluate(new InputSource(new StringReader(xml)), XPathConstants.NODESET);			
			if(nodes.getLength() == 0) return null;
			
			return nodes;
			
		} catch (XPathExpressionException e) {
			return null;
		}
	}
	
	
}
