/**
 * 
 */
package gr.uoa.di.webui.search;

import eu.dnetlib.domain.data.Document;
import eu.dnetlib.domain.functionality.LayoutField;

import java.io.StringReader;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
import java.util.Map;

import javax.xml.XMLConstants;
import javax.xml.namespace.NamespaceContext;
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;


/**
 * TODO : add vocabulary support in document reader
 * 
 * @author stoumpos
 */
public class DocumentReader {

	static Logger logger = Logger.getLogger(DocumentReader.class);

	private ArrayList<String> fieldNames = null; 
	private ArrayList<XPathExpression> fieldPaths = null; 
	private ArrayList<String> propertyNames = null; 
	
	public DocumentReader(List<LayoutField> fields,
			Map<String, String> nameMap) {
		
		fieldNames = new ArrayList<String>(); 
		fieldPaths = new ArrayList<XPathExpression>(); 
		propertyNames = new ArrayList<String>();
		
		try {
		    XPathFactory factory = XPathFactory.newInstance();
		    XPath xpath = factory.newXPath();
		    xpath.setNamespaceContext(new DocumentNamespaceContext());
		
		    for (LayoutField field : fields) {
		    	String fieldName = field.getName();
				fieldNames.add(fieldName);
				fieldPaths.add(xpath.compile(field.getXpath()));
				propertyNames.add(fieldName);
				
			}
			
		    
	    	if (logger.isDebugEnabled()) {
	    		StringBuffer sb = new StringBuffer();
	    		sb.append("Reader configuration [");
	    		for (int i = 0; i < fields.size(); i++) {
	    			sb.append("\n\tfield ").append(fieldNames.get(i));
	    			sb.append(" maps to ").append(propertyNames.get(i));
	    			sb.append(" (with xpath ").append(fieldPaths.get(i));
				}
	    		sb.append("]");
	    	//	logger.debug(sb.toString());
			}
		    
	    } catch (XPathExpressionException e) {
			throw new RuntimeException("Failed to build xpath expression.", e);
		}
	}
	
	public Document read(String xml) {
		
		logger.debug("Got xml " + xml );
		
		Document document = new Document();
		
		for (int i = 0; i < fieldNames.size(); i++) {
			String property = propertyNames.get(i);
			List<String> values = parseExprValues(xml, fieldPaths.get(i));

		//	logger.debug( propertyNames.get(i) + " - " +values);
			
			if (document.getMap().containsKey(property)) {
				document.getMap().get(property).addAll(values);
			} else {
				document.getMap().put(property, values);
			}
		}	
		
	//	logger.debug("document: " + document.getMap());
		
		return document;
	}
	
	protected List<String> parseExprValues(String xml, XPathExpression expr) {
		
		ArrayList<String> values = new ArrayList<String>();
		
		try {
			NodeList list = (NodeList) expr.evaluate(
					new InputSource(new StringReader(xml)),
					XPathConstants.NODESET);
			
			for (int i = 0; i < list.getLength(); i++) {
				// TODO don't remove index highlight. find a way to overcome it
				String text = list.item(i).getTextContent().replace("[hl]", "").replace("[/hl]", "");
				if (text != null && text.trim().length() > 0) {
					values.add(text);
				}
			}
			
		} catch (XPathExpressionException xpee) {
			logger.error("Cannot parse dmf", xpee);
		}
		
		return values;
	}
}

class DocumentNamespaceContext implements NamespaceContext {

    public String getNamespaceURI(String prefix) {
        if (prefix == null) {
        	throw new NullPointerException("Null prefix");
        
        } else if ("dr".equals(prefix)) {
        	return "http://www.driver-repository.eu/namespace/dr";
        
        } else if ("dc".equals(prefix)) {
        	return "http://purl.org/dc/elements/1.1/";
        	
        } else if ("xsi".equals(prefix)) {
        	return "http://www.w3.org/2001/XMLSchema-instance";
        
        } else if ("dri".equals(prefix)) {
        	return "http://www.driver-repository.eu/namespace/dri";
        
        } else if ("xml".equals(prefix)) {
        	return XMLConstants.XML_NS_URI;
        
        }
        return XMLConstants.NULL_NS_URI;
    }
    
    // This method isn't necessary for XPath processing.
    public String getPrefix(String uri) {
        throw new UnsupportedOperationException();
    }
    
    // This method isn't necessary for XPath processing either.
    public Iterator<Object> getPrefixes(String uri) {
        throw new UnsupportedOperationException();
    }
   
}
