/*
 * @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.util.List;
import java.util.NoSuchElementException;
import java.util.function.Predicate;
import net.ranides.assira.collection.lists.ListUtils;
import net.ranides.assira.collection.maps.MapUtils;
import net.ranides.assira.collection.maps.MultiMap;
import net.ranides.assira.collection.query.CQueryBuilder;
import net.ranides.assira.xml.XMLContext;
import net.ranides.assira.xml.XMLElement;
import net.ranides.assira.xml.XMLElements;
import net.ranides.assira.xml.XMLException;
import net.ranides.assira.xml.XMLType;
import org.w3c.dom.Document;
import org.w3c.dom.NamedNodeMap;
import org.w3c.dom.Node;

/**
 *
 * @author Ranides Atterwim <ranides@gmail.com>
 */
public class W3Attributes extends CElements {
    
    private final VNodeMap map;

    public W3Attributes(Document doc, NamedNodeMap map) {
        this(new VNodeMap(doc, map));
    }
    
    private W3Attributes(VNodeMap map) {
        super(CQueryBuilder.fromCollection(map.values()).map(e -> W3Element.wrap(e)));
        this.map = map;
    }
    
    @Override
    public int size() {
        return map.size();
    }

    @Override
    public List<Node> nodes() {
        return map.values();
    }
    
    @Override
    public List<String> names() {
        return map.keySet();
    }

    @Override
    public List<String> texts() {
        return ListUtils.map(map.values(), n -> n.getTextContent());
    }

    @Override
    public List<String> contents() {
        return ListUtils.map(map.values(), n -> n.getNodeValue());
    }
    
    @Override
    public MultiMap<String, String> map() {
        return MapUtils.map(map, this::$value, map::putValue);
    }
    
    private String $value(Node node) {
        return node == null ? null : node.getNodeValue();
    }

    @Override
    public XMLElements children() {
        return new CElements(CQueryBuilder.fromCollection(map.values()).map(e -> W3Element.wrap(e)));
    }
    
    @Override
    public XMLElements children(String name) {
        Node node = map.get(name);
        return null == node ? EMPTY : new CElements(CQueryBuilder.of(W3Element.wrap(node)));
    }

    @Override
    public XMLElements children(Predicate<XMLContext> selector) {
        if(selector instanceof W3Selectors.Name) {
            return children(((W3Selectors.Name)selector).name() );
        } else {
            return children().filter(selector);
        }
    }
    
    @Override
    public XMLElement first(String name) {
        Node node = map.get(name);
        if(null == node) {
            throw new NoSuchElementException();
        }
        return  W3Element.wrap(node);
    }
    
    @Override
    public XMLElement first(Predicate<XMLContext> selector) {
        if(selector instanceof W3Selectors.Name) {
            return first(((W3Selectors.Name)selector).name() );
        }
        return super.first(selector);
    }
    
    @Override
    public boolean has(String name) {
        return map.containsKey(name);
    }
    
    @Override
    public boolean has(Predicate<XMLContext> selector) {
        if(selector instanceof W3Selectors.Name) {
            return has(((W3Selectors.Name)selector).name() );
        } else {
            return children().filter(selector).matches();
        }
    }
    
    @Override
    public XMLElements attrs(String name) {
        return EMPTY;
    }

    @Override
    public XMLElements attrs() {
        return EMPTY;
    }
    
    @Override
    public XMLElements append(XMLElement element) {
        isattr(element);
        map.putValue(element.name(), element.text());
        return this;
    }
    
    @Override
    public XMLElements prepend(XMLElement element) {
        isattr(element);
        map.putValue(element.name(), element.text());
        return this;
    }

    private static void isattr(XMLElement element) throws XMLException {
        if(element.nodetype() != XMLType.ATTRIBUTE) {
            throw new XMLException("Invalid node type:" + element.nodetype());
        }
    }
    
}
