/*
 * @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.AbstractList;
import java.util.Arrays;
import java.util.Collection;
import java.util.Collections;
import java.util.List;
import net.ranides.assira.collection.maps.AMap;
import net.ranides.assira.collection.maps.MultiMap;
import net.ranides.assira.collection.sets.OrderedSet;
import org.w3c.dom.Document;
import org.w3c.dom.NamedNodeMap;
import org.w3c.dom.Node;

/**
 *
 * @author Ranides Atterwim <ranides@gmail.com>
 */
final class VNodeMap extends AMap<String, Node> implements MultiMap<String, Node> {

    private static final long serialVersionUID = 1L;
    
    private final Document doc;
    private final NamedNodeMap nnmap;
    
    public VNodeMap(Document doc, NamedNodeMap nnmap) {
        this.doc = doc;
        this.nnmap = nnmap;
    }

    @Override
    public OrderedSet<Entry<String, Node>> entrySet() {
        return new VEntries();
    }

    @Override
    public List<Node> values() {
        return new VValues();
    }

    @Override
    public OrderedSet<String> keySet() {
        return new VKeys();
    }

    @Override
    public int size() {
        return nnmap.getLength();
    }

    @Override
    public boolean containsKey(Object key) {
        return (key instanceof String) && null != nnmap.getNamedItem((String) key);
    }

    @Override
    @SuppressWarnings(value = "element-type-mismatch")
    public boolean containsValue(Object value) {
        return values().contains(value);
    }

    @Override
    public Node get(Object key) {
        return (key instanceof String) ? nnmap.getNamedItem((String) key) : null;
    }

    @Override
    public Collection<Node> getAll(Object key) {
        if (!(key instanceof String)) {
            return Collections.emptySet();
        }
        Node node = nnmap.getNamedItem((String) key);
        return (null == node) ? Collections.emptySet() : Arrays.asList(node);
    }

    @Override
    public Node remove(Object key) {
        if (!(key instanceof String)) {
            return null;
        }
        String name = (String) key;
        if(null != nnmap.getNamedItem(name)) {
            return nnmap.removeNamedItem(name);
        } else {
            return null;
        }
    }

    public String putValue(String key, String value) {
        Node attr = nnmap.getNamedItem(key);
        if (null != attr) {
            String prev = attr.getNodeValue();
            attr.setNodeValue(value);
            return prev;
        } else {
            attr = doc.createAttribute(key);
            attr.setNodeValue(value);
            nnmap.setNamedItem(attr);
            return null;
        }
    }
    
    private class VEntries extends AbstractList<Entry<String, Node>> implements OrderedSet<Entry<String, Node>> {

        @Override
        public int size() {
            return nnmap.getLength();
        }

        @Override
        public Entry<String, Node> get(int index) {
            return new VNodeEntry(nnmap.item(index));
        }

        @Override
        public boolean contains(Object o) {
            if (o instanceof Entry<?, ?>) {
                Entry<String, String> that = (Entry<String, String>) o;
                return containsEntry(that.getKey(), that.getValue());
            }
            return false;
        }
        
    }

    private class VValues extends AbstractList<Node> {

        @Override
        public int size() {
            return nnmap.getLength();
        }

        @Override
        public Node get(int index) {
            return nnmap.item(index);
        }
    }

    private class VKeys extends AbstractList<String> implements OrderedSet<String> {
        
        @Override
        public int size() {
            return nnmap.getLength();
        }

        @Override
        public String get(int index) {
            return nnmap.item(index).getNodeName();
        }

        @Override
        @SuppressWarnings(value = "element-type-mismatch")
        public boolean contains(Object o) {
            return containsKey(o);
        }
    }
    
    private static final class VNodeEntry implements Entry<String, Node> {
        
        private final Node node;

        public VNodeEntry(Node node) {
            this.node = node;
        }
        
        @Override
        public String getKey() {
            return node.getNodeName();
        }

        @Override
        public Node getValue() {
            return node;
        }

        @Override
        public Node setValue(Node value) {
            throw new UnsupportedOperationException();
        }
        
    }

}
