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

package net.ranides.assira.xml.impl;

import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.io.Reader;
import java.io.StringReader;
import java.net.URI;
import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
import javax.xml.parsers.ParserConfigurationException;
import net.ranides.assira.collection.query.CQueryBuilder;
import net.ranides.assira.functional.CheckedSupplier;
import net.ranides.assira.trace.ExceptionUtils;
import net.ranides.assira.xml.XMLElement;
import net.ranides.assira.xml.XMLElements;
import net.ranides.assira.xml.XMLException;
import org.w3c.dom.DOMException;
import org.w3c.dom.Document;
import org.w3c.dom.Node;
import org.xml.sax.InputSource;
import org.xml.sax.SAXException;

/**
 *
 * @author Ranides Atterwim <ranides@gmail.com>
 */
@SuppressWarnings("PMD.MethodNameRules")
public final class W3CQuery {

    private W3CQuery() {
        /* utility class */
    }
    
    public static XMLElement $(File doc) {
        return $(() -> W3Element.wrap(builder().parse(doc)));
    }
    
    public static XMLElement $(URI doc) {
        return $(() -> W3Element.wrap(builder().parse(doc.toString())));
    }
    
    public static XMLElement $(Reader doc) {
        return $(() -> W3Element.wrap(builder().parse(new InputSource(doc))));
    }
    
    public static XMLElement $(InputStream doc) {
        return $(() -> W3Element.wrap(builder().parse(new InputSource(doc))));
    }
    
    private static XMLElement $(CheckedSupplier<XMLElement,Exception> supplier) {
        try {
            return supplier.get();
        } catch (SAXException | ParserConfigurationException | IOException cause) {
            throw new XMLException(cause);
        } catch(Exception cause) {
            throw ExceptionUtils.rethrow(cause);
        }
    }
    
    public static XMLElements $(XMLElements... elements) {
        return new CElements(CQueryBuilder.fromArray(elements).unfold(e -> e.stream()));
    }
    
    public static XMLElements $(XMLElement... elements) {
        return new CElements(CQueryBuilder.fromArray(elements));
    }
    
    static XMLElement append(XMLElement target, String xml) {
        Node content = parse(target, xml);
        Node ntarget = target.node();
        while (content.hasChildNodes()) {
            ntarget.appendChild(content.removeChild(content.getFirstChild()));
        }
        return target;
    }
    
    static XMLElement prepend(XMLElement target, String xml) {
        Node content = parse(target, xml);
        Node ntarget = target.node();
        while (content.hasChildNodes()) {
            ntarget.insertBefore(content.removeChild(content.getLastChild()), ntarget.getFirstChild());
        }
        return target;
    }

    private static Node parse(XMLElement target, String xml) {
        try {
            String wxml="<__fragment__>" + xml + "</__fragment__>";
            Document cdoc = builder().parse(new InputSource(new StringReader(wxml)));
            return target.document().node().importNode(cdoc.getDocumentElement(),true);
        } catch(SAXException | DOMException | ParserConfigurationException | IOException cause) {
            throw new XMLException(cause);
        }
    }
    
    private static DocumentBuilder builder() throws ParserConfigurationException {
        DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
        factory.setNamespaceAware(true);
        return factory.newDocumentBuilder();
    }
    
}
