/**
 * JOnAS: Java(TM) Open Application Server
 * Copyright (C) 2004-2011 Bull S.A.S.
 * Contact: jonas-team@ow2.org
 *
 * This library is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Lesser General Public
 * License as published by the Free Software Foundation; either
 * version 2.1 of the License, or any later version.
 *
 * This library is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 * Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public
 * License along with this library; if not, write to the Free Software
 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307
 * USA
 *
 * --------------------------------------------------------------------------
 * $Id$
 * --------------------------------------------------------------------------
 */
package org.ow2.jonas.antmodular.jonasbase.discovery.multicast;

import org.apache.tools.ant.BuildException;
import org.ow2.jonas.antmodular.jonasbase.bootstrap.JOnASBaseTask;
import org.ow2.jonas.antmodular.jonasbase.bootstrap.JReplace;
import org.ow2.jonas.antmodular.jonasbase.bootstrap.XMLSerializerTask;
import org.ow2.jonas.antmodular.jonasbase.discovery.base.Discovery;
import org.w3c.dom.Document;
import org.w3c.dom.Element;
import org.w3c.dom.Node;
import org.w3c.dom.NodeList;
import org.w3c.dom.Text;
import org.xml.sax.SAXException;

import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
import javax.xml.parsers.ParserConfigurationException;
import java.io.File;
import java.io.IOException;

/**
 * Defines properties for discovery-multicast service.
 * @author Jeremy Cazaux
 */
public class DiscoveryMulticast extends Discovery {

      /**
     * Default mcast addr.
     */
    private static final String DEFAULT_DISCOVERY_MCASTADDR = "224.224.224.224";

    /**
     * Default mcast port.
     */
    private static final String DEFAULT_DISCOVERY_MCASTPORT = "9080";

    /**
     * Default greeting port.
     */
    private static final String DEFAULT_DISCOVERY_GREETING_PORT = "9899";

    /**
     * Default source port.
     */
    private static final String DEFAULT_DISCOVERY_SOURCE_PORT = "9888";

    /**
     * Default ttl.
     */
    private static final String DEFAULT_DISCOVERY_TTL = "1";

    /**
     * Discovery greeting port property.
     */
    private static final String DISCOVERY_GREETING_PORT_PROPERTY = "jonas.service.discovery.greeting.port";

    /**
     * Discovery greeting timeout property.
     */
    private static final String DISCOVERY_GREETING_TIMEOUT_PROPERTY = "jonas.service.discovery.greeting.timeout";

    /**
     * Discovery source port property.
     */
    private static final String DISCOVERY_SOURCE_PORT_PROPERTY = "jonas.service.discovery.source.port";

    /**
     * Discovery ttl property.
     */
    private static final String DISCOVERY_TTL_PROPERTY = "jonas.service.discovery.ttl";

    /**
     * Discovery mcast addr property.
     */
    private static final String DISCOVERY_MCASTADDR_PROPERTY = "jonas.service.discovery.multicast.address";

    /**
     * Discovery mcast port property.
     */
    private static final String DISCOVERY_MCASTPORT_PROPERTY = "jonas.service.discovery.multicast.port";

    /**
     * Discovery master property.
     */
    private static final String DISCOVERY_MASTER_PROPERTY = "jonas.service.discovery.master";

    /**
     * domain management configuration file.
     */
    private static final String DOMAIN_MNGT_CONF_FILE = "domain.xml";

    /**
     * Domain.xml structure.
     */
    private Document domainDoc = null;

    /**
     * Flag indicating if the domain document has been loaded.
     */
    private boolean domainDocLoaded = false;

    /**
     * Name of the implementation class for Multicast.
     */
    private static final String MULTICAST_SERVICE = "org.ow2.jonas.discovery.multicast.MulticastDiscoveryServiceImpl";

    /**
     * Enable greeting part of discovery service.
     */
    private void enableMaster() {
        // 1st Token to replace
        String token1 = "#" + DISCOVERY_MASTER_PROPERTY;
        String value1 = DISCOVERY_MASTER_PROPERTY;
        JReplace propertyReplace1 = new JReplace();
        propertyReplace1.setLogInfo(INFO + "Enable master node for discovery");
        propertyReplace1.setConfigurationFile(JOnASBaseTask.JONAS_CONF_FILE);
        propertyReplace1.setToken(token1);
        propertyReplace1.setValue(value1);
        addTask(propertyReplace1);

        // 2nd Token to replace
        String token2 = "#" + DISCOVERY_SOURCE_PORT_PROPERTY;
        String value2 = DISCOVERY_SOURCE_PORT_PROPERTY;
        JReplace propertyReplace2 = new JReplace();
        propertyReplace2.setLogInfo(INFO + "Enable greeting timeout for discovery");
        propertyReplace2.setConfigurationFile(JOnASBaseTask.JONAS_CONF_FILE);
        propertyReplace2.setToken(token2);
        propertyReplace2.setValue(value2);
        addTask(propertyReplace2);
    }

    /**
     * Enable greeting part of discovery service.
     */
    private void enableGreeting() {
        // 1st Token to replace
        String token1 = "#" + DISCOVERY_GREETING_PORT_PROPERTY;
        String value1 = DISCOVERY_GREETING_PORT_PROPERTY;
        JReplace propertyReplace1 = new JReplace();
        propertyReplace1.setLogInfo(INFO + "Enable greeting port for discovery");
        propertyReplace1.setConfigurationFile(JOnASBaseTask.JONAS_CONF_FILE);
        propertyReplace1.setToken(token1);
        propertyReplace1.setValue(value1);
        addTask(propertyReplace1);

        // 2nd Token to replace
        String token2 = "#" + DISCOVERY_GREETING_TIMEOUT_PROPERTY;
        String value2 = DISCOVERY_GREETING_TIMEOUT_PROPERTY;
        JReplace propertyReplace2 = new JReplace();
        propertyReplace2.setLogInfo(INFO + "Enable greeting timeout for discovery");
        propertyReplace2.setConfigurationFile(JOnASBaseTask.JONAS_CONF_FILE);
        propertyReplace2.setToken(token2);
        propertyReplace2.setValue(value2);
        addTask(propertyReplace2);
    }

    /**
     * Set the source port for the discovery service.
     * @param portNumber port number (-1 means to disable to master node)
     */
    public void setSourcePort(final String portNumber) {

        int pn = new Integer(portNumber).intValue();
        if (pn != -1) {
            enableMaster();

            // Token to replace
            String token = DISCOVERY_SOURCE_PORT_PROPERTY + "=" + DEFAULT_DISCOVERY_SOURCE_PORT;
            String value = DISCOVERY_SOURCE_PORT_PROPERTY + "=" + portNumber;
            JReplace propertyReplace = new JReplace();
            propertyReplace.setLogInfo(INFO + "Setting source port for discovery");
            propertyReplace.setConfigurationFile(JOnASBaseTask.JONAS_CONF_FILE);
            propertyReplace.setToken(token);
            propertyReplace.setValue(value);
            addTask(propertyReplace);
        }
    }

    /**
     * Set the port for the discovery service.
     * @param portNumber port number
     */
    public void setGreetingPort(final String portNumber) {
        enableGreeting();
        // Token to replace
        String token = DISCOVERY_GREETING_PORT_PROPERTY + "=" + DEFAULT_DISCOVERY_GREETING_PORT;
        String value = DISCOVERY_GREETING_PORT_PROPERTY + "=" + portNumber;
        JReplace propertyReplace = new JReplace();
        propertyReplace.setLogInfo(INFO + "Setting greeting port for discovery");
        propertyReplace.setConfigurationFile(JOnASBaseTask.JONAS_CONF_FILE);
        propertyReplace.setToken(token);
        propertyReplace.setValue(value);
        addTask(propertyReplace);
    }

    /**
     * Set mcastAddr.
     * @param mcastAddr multicast address
     */
    public void setMcastAddr(final String mcastAddr) {

        // Token to replace
        String token = DISCOVERY_MCASTADDR_PROPERTY + "=" + DEFAULT_DISCOVERY_MCASTADDR;
        String value = DISCOVERY_MCASTADDR_PROPERTY + "=" + mcastAddr;
        JReplace propertyReplace = new JReplace();
        propertyReplace.setLogInfo(INFO + "Setting mcastaddr for discovery");
        propertyReplace.setConfigurationFile(JOnASBaseTask.JONAS_CONF_FILE);
        propertyReplace.setToken(token);
        propertyReplace.setValue(value);
        addTask(propertyReplace);

    }

    /**
     * Set mcastPort.
     * @param mcastPort multicast port
     */
    public void setMcastPort(final String mcastPort) {

        // Token to replace
        String token = DISCOVERY_MCASTPORT_PROPERTY + "=" + DEFAULT_DISCOVERY_MCASTPORT;
        String value = DISCOVERY_MCASTPORT_PROPERTY + "=" + mcastPort;
        JReplace propertyReplace = new JReplace();
        propertyReplace.setLogInfo(INFO + "Setting mcastport for discovery");
        propertyReplace.setConfigurationFile(JOnASBaseTask.JONAS_CONF_FILE);
        propertyReplace.setToken(token);
        propertyReplace.setValue(value);
        addTask(propertyReplace);

    }

    /**
     * Set ttl.
     * @param ttl paquet time to live
     */
    public void setTtl(final String ttl) {

        // Token to replace
        String token = DISCOVERY_TTL_PROPERTY + "=" + DEFAULT_DISCOVERY_TTL;
        String value = DISCOVERY_TTL_PROPERTY + "=" + ttl;
        JReplace propertyReplace = new JReplace();
        propertyReplace.setLogInfo(INFO + "Setting ttl for discovery");
        propertyReplace.setConfigurationFile(JOnASBaseTask.JONAS_CONF_FILE);
        propertyReplace.setToken(token);
        propertyReplace.setValue(value);
        addTask(propertyReplace);
    }


    /**
     * load the domain.xml file in a DOM structure.
     */
    private void loadDomainXmlDoc() {

        if (!domainDocLoaded) {

            // Load the orignal configuration file
            DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
            DocumentBuilder docBuilder = null;
            try {
                factory.setNamespaceAware(true);
                docBuilder = factory.newDocumentBuilder();
            } catch (ParserConfigurationException e) {
                throw new BuildException(INFO + "Exception during loadDomainXmlDoc", e);
            }
            try {
                domainDoc = docBuilder.parse(this.jonasRoot + File.separator + "templates" + File.separator + "conf" + File.separator + "conf"
                        + File.separator + DOMAIN_MNGT_CONF_FILE);
            } catch (SAXException e) {
                throw new BuildException(INFO + "Error during parsing of the file " + DOMAIN_MNGT_CONF_FILE, e);
            } catch (IOException e) {
                throw new BuildException(INFO + "Error during parsing of the file " + DOMAIN_MNGT_CONF_FILE, e);
            }

            Element root = domainDoc.getDocumentElement();

            // Remove the default servers list
            NodeList serverNodeL = root.getElementsByTagName("server");
            for (int i = 0; i < serverNodeL.getLength(); i++) {
                Node n = serverNodeL.item(i);
                root.removeChild(n);
            }

            // Prepare the serialization
            XMLSerializerTask xmlSerTask = new XMLSerializerTask();
            xmlSerTask.setXmlDoc(domainDoc);
            xmlSerTask.setXmlFileName(DOMAIN_MNGT_CONF_FILE);

            addTask(xmlSerTask);

            domainDocLoaded = true;
        }

    }

    /**
     * Set the domain description.
     * @param domainDesc domain description
     */
    public void setDomainDesc(final String domainDesc) {

        // Load the orignal configuration file
        loadDomainXmlDoc();

        Element root = domainDoc.getDocumentElement();

        // update the domain name
        NodeList nameNodeL = root.getElementsByTagName("description");
        nameNodeL.item(0).getFirstChild().setNodeValue(domainDesc);

    }

    /**
     * Add a cluster in the domain.
     * @param clusterName cluster name
     * @param clusterDesc cluster desc
     * @param nodesName prefix of the nodes names within the cluster
     * @param nodesNb number of nodes within the cluster
     * @param protocol protocol
     * @param portRange protocol port range
     * @param cdName Cluster daemon's name
     * @param cdUrl Cluster daemon's URL
     */
    public void setDomainCluster(final String clusterName, final String clusterDesc, final String nodesName, final int nodesNb,
            final String protocol, final String[] portRange, final String cdName, final String cdUrl) {

        // Load the orignal configuration file
        loadDomainXmlDoc();

        Element root = domainDoc.getDocumentElement();

        Text c = domainDoc.createTextNode("\n\t");
        root.appendChild(c);

        // creation of the cluster-daemon node
        Node dnc = domainDoc.createElement("cluster-daemon");
        root.appendChild(dnc);

        Text c1 = domainDoc.createTextNode("\n\t\t");
        dnc.appendChild(c1);

        // Element name
        Node dncn = domainDoc.createElement("name");
        dnc.appendChild(dncn);

        Text dtncn = domainDoc.createTextNode(cdName);
        dncn.appendChild(dtncn);

        Text c2 = domainDoc.createTextNode("\n\t\t");
        dnc.appendChild(c2);

        // Element description
        Node dncd = domainDoc.createElement("description");
        dnc.appendChild(dncd);

        Text dtncd = domainDoc.createTextNode("");
        dncd.appendChild(dtncd);

        Text c3 = domainDoc.createTextNode("\n\t\t");
        dnc.appendChild(c3);

        // Element location
        Node dncl = domainDoc.createElement("location");
        dnc.appendChild(dncl);

        Text c4 = domainDoc.createTextNode("\n\t\t   ");
        dncl.appendChild(c4);

        // Element url
        Node dncu = domainDoc.createElement("url");
        dncl.appendChild(dncu);

        Text dtncu = domainDoc.createTextNode(cdUrl);
        dncu.appendChild(dtncu);

        Text c5 = domainDoc.createTextNode("\n\t");
        root.appendChild(c5);

        // creation of the cluster node
        Node cnc = domainDoc.createElement("cluster");
        root.appendChild(cnc);

        Text c6 = domainDoc.createTextNode("\n\t   ");
        cnc.appendChild(c6);

        Node cncn = domainDoc.createElement("name");
        cnc.appendChild(cncn);

        Text ctncn = domainDoc.createTextNode(clusterName);
        cncn.appendChild(ctncn);

        Text c7 = domainDoc.createTextNode("\n\t  ");
        cnc.appendChild(c7);

        Node cncd = domainDoc.createElement("description");
        cnc.appendChild(cncd);

        Text ctncd = domainDoc.createTextNode(clusterDesc);
        cncd.appendChild(ctncd);

        for (int i = 1; i <= nodesNb; i++) {

            Text c8 = domainDoc.createTextNode("\n\t\t");
            cnc.appendChild(c8);

            // Element server
            Node cncs = domainDoc.createElement("server");
            cnc.appendChild(cncs);

            Text c9 = domainDoc.createTextNode("\n\t\t   ");
            cncs.appendChild(c9);

            // Element name
            Node cncsn = domainDoc.createElement("name");
            cncs.appendChild(cncsn);

            Text ctncsn = domainDoc.createTextNode(nodesName + i);
            cncsn.appendChild(ctncsn);

            Text c10 = domainDoc.createTextNode("\n\t\t   ");
            cncs.appendChild(c10);

            // Element location
            Node cncsl = domainDoc.createElement("location");
            cncs.appendChild(cncsl);

            Text c11 = domainDoc.createTextNode("\n\t\t\t");
            cncsl.appendChild(c11);

            // Element url
            Node cncsu = domainDoc.createElement("url");
            cncsl.appendChild(cncsu);

            String scheme;
            if (protocol.equals("jrmp") || protocol.equals("irmi")) {
                scheme = "rmi";
            } else if (protocol.equals("iiop")) {
                scheme = "iiop";
            } else {
                throw new BuildException(INFO + "Unknown protocol '" + protocol + "' for node '" + i + "'.");
            }
            String url = "service:jmx:" + scheme + "://localhost/jndi/" + scheme + "://localhost:" + portRange[i - 1] + "/"
                    + protocol + "connector_node" + i;

            Text ctncsu = domainDoc.createTextNode(url);
            cncsu.appendChild(ctncsu);

            Text c20 = domainDoc.createTextNode("\n\t\t   ");
            cncsl.appendChild(c20);

            Text c12 = domainDoc.createTextNode("\n\t\t   ");
            cncs.appendChild(c12);

            // Element cluster-daemon
            Node cncscd = domainDoc.createElement("cluster-daemon");
            cncs.appendChild(cncscd);

            Text ctncscd = domainDoc.createTextNode(cdName);
            cncscd.appendChild(ctncscd);

            Text c28 = domainDoc.createTextNode("\n\t\t");
            cncs.appendChild(c28);

        }

        Text c21 = domainDoc.createTextNode("\n\t");
        cnc.appendChild(c21);

        Text c14 = domainDoc.createTextNode("\n\t\t");
        dncl.appendChild(c14);

        Text c13 = domainDoc.createTextNode("\n\t");
        dnc.appendChild(c13);

        Text c18 = domainDoc.createTextNode("\n");
        root.appendChild(c18);

        Text cf = domainDoc.createTextNode("\n");
        root.appendChild(cf);

    }

    /**
     * Execute all tasks
     */
    public void execute() {
        super.execute();
        super.createServiceNameReplace(this.MULTICAST_SERVICE, this.INFO, this.destDir.getAbsolutePath() + this.CONF_DIR);
    }
}
