/*
 *  @author Ranides Atterwim <ranides@gmail.com>
 *  @copyright Ranides Atterwim
 *  @license WTFPL
 *  @url http://ranides.net/projects/assira
 */

package net.ranides.assira.xml;

import java.util.Collection;
import java.util.LinkedList;
import java.util.List;
import javax.xml.xpath.XPathConstants;
import javax.xml.xpath.XPathException;
import javax.xml.xpath.XPathExpression;
import javax.xml.xpath.XPathFactory;
import org.w3c.dom.Document;
import org.w3c.dom.Node;
import org.w3c.dom.NodeList;

/**
 *
 * @author ranides
 */
final public class XMLQuery {

    private XMLQuery() { }

    public static NodeList list(Document doc, String xquery) throws XMLQueryException {
        if (doc == null) {
            throw new XMLQueryException("Empty document");
        }
        if ((xquery == null) || xquery.isEmpty()) {
            throw new XMLQueryException("Empty query");
        }
        try {
            XPathExpression expression = XPathFactory.newInstance().newXPath().compile(xquery);
            return (NodeList)expression.evaluate(doc, XPathConstants.NODESET);
        } catch(XPathException cause) {
            throw new XMLQueryException(cause);
        } catch (ClassCastException cause) {
            throw new XMLInternalError(cause);
        }
    }

    public static Collection<Node> collection(Document doc, String xquery) throws XMLQueryException {
        return NodeCollection.make(list(doc, xquery));
    }

    public static Node node(Document doc, String xquery) throws XMLQueryException {
        return list(doc, xquery).item(0);
        }


    public static String attr(Node node, String attribName) throws XMLQueryException {
        if(node == null) {
            return null;
        }
        Node attrNode = node.getAttributes().getNamedItem(attribName);
        if(attrNode == null) {
            return null;
        }
        return attrNode.getNodeValue();
    }
    
    public static boolean has(Node node, String attribName) {
        if(node == null) {
            return false;
        }
        return null != node.getAttributes().getNamedItem(attribName);
    }

    public static String attr(Document doc, String xPathQuery, String attribName) throws XMLQueryException {
        return attr(node(doc, xPathQuery), attribName);
    }
    
    public static boolean has(Document doc, String xPathQuery, String attribName) throws XMLQueryException {
        return exists(doc, xPathQuery) && has(node(doc, xPathQuery), attribName);
    }


    public static void attr(Node node, String name, String value) {
        Node attr = node.getOwnerDocument().createAttribute(name);
        attr.setNodeValue(value);
        node.getAttributes().setNamedItem(attr);
    }

    public static void attr(Node node, String name, int value) {
        attr(node, name, Integer.toString(value));
    }

    public static void attr(Node node, String name, long value) {
        attr(node, name, Long.toString(value));
    }

    public static void attr(Node node, String name, Object value) {
        attr(node, name, value.toString());
    }


    public static List<Node> filter(Node node, String tagname) {
        List<Node> result = new LinkedList<>();
        if(node == null) {
            return result;
        }
        for(Node value : NodeCollection.nodes(node) )  {
            if( (Node.ELEMENT_NODE==value.getNodeType()) && tagname.equals(value.getNodeName()) ) {
                result.add(value);
            }
        }
        return result;
    }
    
    public static Node node(Node node, String tagname) {
        if(node == null) {
            return null;
        }
        for(Node value : NodeCollection.nodes(node) )  {
            if( (Node.ELEMENT_NODE==value.getNodeType()) && tagname.equals(value.getNodeName()) ) {
                return value;
            }
        }
        return null;
    }

    public static List<Node> filter(Node node, NodeType type) {
        List<Node> result = new LinkedList<>();
        if(node == null) {
            return result;
        }
        for(Node value : NodeCollection.nodes(node) )  {
            if( type.match(value.getNodeType()) ) {
                result.add(value);
            }
        }
        return result;
    }

    public static int count(Node node, String tagname) {
        if(node == null) {
            return 0;
        }
        int count = 0;
        for(Node value : NodeCollection.nodes(node) )  {
            if( (Node.ELEMENT_NODE==value.getNodeType()) && tagname.equals(value.getNodeName()) ) {
                count++;
            }
        }
        return count;
    }

    public static int count(Node node, NodeType type) {
        if(node == null) {
            return 0;
        }
        int count = 0;
        for(Node value : NodeCollection.nodes(node) )  {
            if( type.match(value) ) { count ++; }
        }
        return count;
    }
    
    public static boolean exists(Document doc, String xquery) {
        return list(doc, xquery).getLength() > 0;
    }
    
    public static boolean exists(Node node, String tagname) {
        if(node == null) {
            return false;
        }
        for(Node value : NodeCollection.nodes(node) )  {
            if( (Node.ELEMENT_NODE==value.getNodeType()) && tagname.equals(value.getNodeName()) ) {
                return true;
            }
        }
        return false;
    }
    
    public static boolean exists(Node node, short type) {
        for(Node value : NodeCollection.nodes(node) )  {
            if( type == value.getNodeType() ) { 
                return true; 
            }
        }
        return false;
    }

}
