package org.jgroups.fork;

import org.jgroups.conf.ProtocolConfiguration;
import org.jgroups.conf.XmlConfigurator;
import org.w3c.dom.*;

import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
import java.io.FileInputStream;
import java.io.InputStream;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

/**
 * Parses the fork-stacks.xsd schema. See conf/fork-stacks.xml for an example
 * @author Bela Ban
 * @since  3.4
 */
public class ForkConfig {
    protected static final String FORK_STACKS   = "fork-stacks";
    protected static final String FORK_STACK    = "fork-stack";
    protected static final String ID            = "id";


    /**
     * Parses the input and returns a map of fork-stack IDs and lists of ProtocolConfigurations
     */
    public static Map<String,List<ProtocolConfiguration>> parse(InputStream input) throws Exception {
        DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
        factory.setValidating(false); // for now
        DocumentBuilder builder=factory.newDocumentBuilder();
        Document document=builder.parse(input);
        Element root=document.getDocumentElement();
        match(FORK_STACKS, root.getNodeName(), true);
        NodeList children=root.getChildNodes();
        if(children == null || children.getLength() == 0)
            return null;

        Map<String,List<ProtocolConfiguration>> map=new HashMap<String,List<ProtocolConfiguration>>();
        for(int i=0; i < children.getLength(); i++) {
            Node node=children.item(i);
            if(node.getNodeType() != Node.ELEMENT_NODE)
                continue;
            String element_name=node.getNodeName();
            if(FORK_STACK.equals(element_name))
                parseForkStack(map, node);
            else
                throw new Exception("expected <" + FORK_STACK + ">, but got " + "<" + element_name + ">");
        }

        return map;
    }


    protected static void parseForkStack(final Map<String,List<ProtocolConfiguration>> map, Node root) throws Exception {
        NodeList children=root.getChildNodes();
        if(children == null || children.getLength() == 0)
            return;

        NamedNodeMap attributes=root.getAttributes();
        String fork_stack_id=attributes.getNamedItem(ID).getNodeValue();
        if(map.containsKey(fork_stack_id))
            throw new IllegalStateException("duplicate fork-stack ID: \"" + fork_stack_id + "\"");

        for(int i=0; i < children.getLength(); i++) {
            Node node=children.item(i);
            if(node.getNodeType() != Node.ELEMENT_NODE)
                continue;

            List<ProtocolConfiguration> protocols=XmlConfigurator.parseProtocols((Element)node);
            map.put(fork_stack_id, protocols);
        }
    }



    protected static void match(String expected_name, String name, boolean is_element) throws Exception {
        if(!expected_name.equals(name))
            throw new Exception((is_element? "Element " : "Attribute ") + "\"" + name + "\" didn't match \"" + expected_name + "\"");
    }



    public static void main(String[] args) throws Exception {
        InputStream input=new FileInputStream("/home/bela/fork-stacks.xml");
        Map<String,List<ProtocolConfiguration>> fork_stacks=parse(input);
        System.out.println("fork_stacks:");
        for(Map.Entry<String,List<ProtocolConfiguration>> entry: fork_stacks.entrySet())
            System.out.println(entry.getKey() + ":\n" + entry.getValue() + "\n");

    }
}
