package adecWatt.model.xml;

import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.Hashtable;
import java.util.Iterator;
import java.util.List;
import java.util.TreeSet;
import org.w3c.dom.Attr;
import org.w3c.dom.Document;
import org.w3c.dom.Element;
import org.w3c.dom.NamedNodeMap;
import org.w3c.dom.Node;
import org.w3c.dom.NodeList;

import misc.Util;

public class XmlPermanent<T extends Enum<?>, A extends Enum<?>> {

    // ========================================
    public T type;
    private Hashtable<A, String> facets = new Hashtable<A, String> ();
    private String value;

    ArrayList<Node> unknownAttr = new ArrayList<Node> ();
    ArrayList<Node> unknownNodes = new ArrayList<Node> ();

    // ========================================
    @SuppressWarnings ({"unchecked", "rawtypes"})
    public void parseNode (Node node, Class<T> nodeEnum, Class<A> attrEnum) {
	if (Node.ELEMENT_NODE != node.getNodeType ())
	    throw new IllegalArgumentException ("XML unit not a node ("+node+").");
	type = (T) Enum.valueOf ((Class<? extends Enum>)nodeEnum, Util.toCapital (node.getNodeName ()));
	NamedNodeMap allFacets = node.getAttributes ();
	for (int i = 0; i < allFacets.getLength (); i++) {
	    Node facetNode = allFacets.item (i);
	    try {
		if (facetNode.getNodeValue ().isEmpty ())
		    continue;
		facets.put ((A) Enum.valueOf ((Class<? extends Enum>)attrEnum, Util.toCapital (facetNode.getNodeName ())), facetNode.getNodeValue ());
	    } catch (Exception e) {
		unknownAttr.add (facetNode);
	    }
	}
	NodeList childrens = node.getChildNodes ();
	for (int i = 0; i < childrens.getLength (); i++) {
	    Node child = childrens.item (i);
	    if (isEmpty (child))
		continue;
	    if (isText (child)) {
		String val = child.getNodeValue ().trim ();
		if (value != null)
		    value += " "+val;
		else
		    value = val;
		continue;
	    }
	    unknownNodes.add (child);
	}
    }

    // ========================================
    static public boolean isEmpty (Node node) {
	return
	    Node.TEXT_NODE == node.getNodeType () &&
	    0 == node.getChildNodes ().getLength () &&
	    0 == node.getNodeValue ().trim ().length () &&
	    (null == node.getAttributes () || 0 == node.getAttributes ().getLength ());
    }

    static public boolean isText (Node node) {
	return
	    Node.TEXT_NODE == node.getNodeType () &&
	    0 == node.getChildNodes ().getLength () &&
	    (null == node.getAttributes () || 0 == node.getAttributes ().getLength ());
    }

    // ========================================
    public String getValue () {
	String result = value;
	value = null;
	return result;
    }

    public String getFacet (A a) {
	return facets.remove (a);
    }

    public Collection<String> getSplitFacet (A a) {
	String result = facets.remove (a);
	if (result == null)
	    return null;
	return new TreeSet<String> (Arrays.asList (result.toLowerCase ().split ("\\|")));
    }
    public List<String> getOrderedSplitFacet (A a) {
	String result = facets.remove (a);
	if (result == null)
	    return null;
	return Arrays.asList (result.toLowerCase ().split ("\\|"));
    }

    // ========================================
    public ArrayList<XmlProp> getProps () {
	ArrayList<XmlProp> xmlProps = new ArrayList<XmlProp> ();
	for (Iterator<Node> it = unknownNodes.iterator (); it.hasNext (); ) {
	    Node node = it.next ();
	    try {
		xmlProps.add (new XmlProp (node));
		it.remove ();
	    } catch (Exception e) {
	    }
	}
	return xmlProps;
    }
    public ArrayList<XmlSegm> getSegms () {
	ArrayList<XmlSegm> xmlSegms = new ArrayList<XmlSegm> ();
	for (Iterator<Node> it = unknownNodes.iterator (); it.hasNext (); ) {
	    Node node = it.next ();
	    try {
		xmlSegms.add (new XmlSegm (node));
		it.remove ();
	    } catch (Exception e) {
	    }
	}
	return xmlSegms;
    }
    public ArrayList<XmlComp> getComps () {
	ArrayList<XmlComp> xmlComps = new ArrayList<XmlComp> ();
	for (Iterator<Node> it = unknownNodes.iterator (); it.hasNext (); ) {
	    Node node = it.next ();
	    try {
		xmlComps.add (new XmlComp (node));
		it.remove ();
	    } catch (Exception e) {
	    }
	}
	return xmlComps;
    }
    public ArrayList<XmlAcc> getAccs () {
	ArrayList<XmlAcc> xmlAccs = new ArrayList<XmlAcc> ();
	for (Iterator<Node> it = unknownNodes.iterator (); it.hasNext (); ) {
	    Node node = it.next ();
	    try {
		xmlAccs.add (new XmlAcc (node));
		it.remove ();
	    } catch (Exception e) {
	    }
	}
	return xmlAccs;
    }

    public Element getXml (Document document) {
	Element element = document.createElement (type.toString ().toLowerCase ());
	if (value != null)
	    element.appendChild (document.createTextNode (value));
	for (A facet : facets.keySet ())
	    element.setAttribute (facet.toString ().toLowerCase (), facets.get (facet));
	for (Node subNode : unknownAttr)
	    element.setAttributeNode ((Attr) (document.importNode (subNode, true)));				
	for (Node subNode : unknownNodes)
	    element.appendChild (document.importNode (subNode, true));
	return element;
    }

    // ========================================
    static public void putValue (Element element, String value) {
	if (value == null || value.isEmpty ())
	    return;
	element.appendChild (element.getOwnerDocument ().createTextNode (value));
    }

    static public void putFacet (Element element, Enum<?> a, String value) {
	if (value == null || value.isEmpty ())
	    return;
	element.setAttribute (a.toString ().toLowerCase (), value);
    }
    @SuppressWarnings ("unchecked")
    static public void putSplitFacet (Element element, Enum<?> a, Collection<String>... tValues) {
	String result = "";
	String sep = "";
	for (Collection<String> values : tValues) {
	    if (values == null || values.size () == 0)
		continue;
	    result += sep + String.join ("|", values);
	    sep = "|";
	}
	putFacet (element, a, result);
    }

    // ========================================
    // XXX a supprimer
    // public String toString () {
    // 	String result = type.toString ().toLowerCase ()+":";
    // 	for (A facet : facets.keySet ())
    // 	    result+= " "+facet.toString ().toLowerCase ()+"="+facets.get (facet);
    // 	return result;
    // }

    // ========================================
}
