/**
 * JOnAS: Java(TM) Open Application Server
 * Copyright (C) 2012 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.jndi.internal;

import org.ow2.jonas.configadmin.AdapterException;
import org.ow2.jonas.configadmin.ConfigurationInfo;
import org.ow2.jonas.configadmin.XmlConfigurationAdapter;
import org.ow2.jonas.jndi.JndiService;
import org.ow2.util.log.Log;
import org.ow2.util.log.LogFactory;
import org.w3c.dom.Element;
import org.w3c.dom.Node;
import org.xml.sax.SAXException;

import javax.xml.XMLConstants;
import javax.xml.transform.dom.DOMSource;
import javax.xml.transform.stream.StreamSource;
import javax.xml.validation.Schema;
import javax.xml.validation.SchemaFactory;
import javax.xml.validation.Validator;
import java.io.IOException;
import java.io.InputStream;
import java.util.Collections;
import java.util.Map;
import java.util.Set;
import java.util.Vector;

/**
 * JNDI Adaptater
 * @author Jeremy Cazaux
 */
public class JndiAdapter implements XmlConfigurationAdapter {

    /**
     * PID of the JNDI service
     */
    public static final String JNDI_SERVICE_PID = JndiEntriesService.class.getName();

    /**
     * Entry
     */
    public static final String ENTRY = "entry";

    /**
     * Entry name
     */
    public static final String ENTRY_NAME = "name";

    /**
     * Entry type
     */
    public static final String ENTRY_TYPE = "type";

    /**
     * Entry value
     */
    public static final String ENTRY_VALUE = "value";

    /**
     * Policy of JNDI entries
     */
    public static final String POLICY = "policy";

    /**
     * The XSD resource
     */
    public static final String XSD_RESOURCE = "/META-INF/configadmin-jndi-1.0.xsd";

    /**
     * The jndi service
     */
    private JndiService jndiService;

    /**
     * Logger
     */
    private static final Log logger = LogFactory.getLog(JndiAdapter.class);


    /**
     * Default constructor
     * @param jndiService The JNDI service
     */
    public JndiAdapter(final JndiService jndiService) {
        this.jndiService = jndiService;
    }

    /**
     * {@inheritDoc}
     */
    @Override
    public Set<ConfigurationInfo> convert(final Element node) throws AdapterException {

        try{
            //validate the node
            if (validate(node) == false) {
                throw new AdapterException("Cannot validate the XML Node " + node.getNodeName());
            }
        } catch (AdapterException e) {
            throw new AdapterException("Cannot validate the XML Node " + node.getNodeName(), e);
        }

        // Create an empty configuration that will store the info from Xml
        // Hard-code the target PID
        ConfigurationInfo info = new ConfigurationInfo(JNDI_SERVICE_PID, true);

        // Transfer from Xml format into internal format
        Map<String, Object> props = info.getProperties();

        Vector<String> names = new Vector<String>();
        Vector<String> values = new Vector<String>();
        Vector<String> types = new Vector<String>();

        for (Node child = node.getFirstChild(); child != null; child = child.getNextSibling()) {

            if (child.getNodeType() == Node.ELEMENT_NODE) {

                if (POLICY.equals(child.getNodeName())) {
                    props.put(POLICY, child.getFirstChild().getNodeValue());
                }  else if (ENTRY.equals(child.getNodeName())) {

                    for (Node n = child.getFirstChild(); n != null; n = n.getNextSibling()) {

                        if (n.getNodeType() == Node.ELEMENT_NODE) {

                            Node valueNode = n.getFirstChild();
                            String key = n.getNodeName();

                            String val = new String();
                            if (valueNode != null) {
                                val = valueNode.getNodeValue();
                            }

                            if (ENTRY_NAME.equals(key)) {
                                names.add(val);
                            } else if (ENTRY_TYPE.equals(key)) {
                                types.add(val);
                            } else if (ENTRY_VALUE.equals(key)) {
                                values.add(val);
                            }
                        }
                    }
                }
            }
        }

        if (props.get(POLICY) == null) {
            props.put(POLICY, this.jndiService.getDefaultPolicy());
        }

        props.put(ENTRY_NAME, names);
        props.put(ENTRY_VALUE, values);
        props.put(ENTRY_TYPE, types);

        return Collections.singleton(info);
    }

    /**
     * Allow to validate a Node from an XSD resource
     * @param node The node to validate
     * @throws AdapterException {@link AdapterException}
     * @return true if the node has been valide. Otherwise, false.
     */
    private boolean validate(Node node) throws AdapterException {
        //First create a Validator from the XSD file
        InputStream xsdInputStream = getClass().getResourceAsStream(XSD_RESOURCE);

        if (xsdInputStream != null) {
            SchemaFactory factory = SchemaFactory.newInstance(XMLConstants.W3C_XML_SCHEMA_NS_URI);
            Schema schema = null;
            try {
                schema = factory.newSchema(new StreamSource(xsdInputStream));
            } catch (SAXException e) {
                throw new AdapterException("Cannot create a new Schema from the resource " + XSD_RESOURCE, e);
            }
            Validator validator = schema.newValidator();

            //then try to validate the node
            try {
                validator.validate(new DOMSource(node));
                return true;
            } catch (SAXException e) {
                logger.error("Cannot validate the Node " + node.getNodeName() + " from the XSD " +
                        "resource " + XSD_RESOURCE, e);
                return false;
            } catch (IOException e) {
                logger.error("Cannot validate the Node " + node.getNodeName() + " from the XSD " +
                        "resource " + XSD_RESOURCE, e);
                return false;
            }

        } else {
            throw new AdapterException("Cannot get the inputstream of the resource configadmin-jndi-1.0.xsd");
        }

    }
}
