/**
 * JOnAS : Java(TM) OpenSource Application Server
 * Copyright (C) 1999-2004 Bull S.A.
 * 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.generators.wsgen.generator.axis;

import java.io.File;

import org.ow2.jonas.deployment.ws.MappingFile;
import org.ow2.jonas.deployment.ws.ServiceRefDesc;
import org.ow2.jonas.generators.genbase.GenBaseException;
import org.ow2.jonas.generators.genbase.archive.Archive;
import org.ow2.jonas.generators.genbase.archive.EjbJar;
import org.ow2.jonas.generators.genbase.archive.J2EEArchive;
import org.ow2.jonas.generators.genbase.archive.WebApp;
import org.ow2.jonas.generators.genbase.generator.Config;
import org.ow2.jonas.generators.genbase.utils.XMLUtils;
import org.ow2.jonas.generators.wsgen.WsGenException;
import org.ow2.jonas.generators.wsgen.ddmodifier.WsClientDDModifier;
import org.ow2.jonas.generators.wsgen.generator.WsClientGenerator;
import org.ow2.jonas.generators.wsgen.generator.axis.wsdl2java.JOnASWSDL2Java;
import org.ow2.jonas.lib.loader.AbsModuleClassLoader;
import org.ow2.jonas.lib.util.I18n;
import org.w3c.dom.Document;
import org.w3c.dom.Element;

import org.apache.velocity.VelocityContext;




import org.objectweb.util.monolog.api.BasicLevel;

/**
 * Generate WebServices client files dedicated to axis. <ul><li>
 * client-config.wsdd : if needed</li><li>java sources : from WSDL</li>
 * </ul>
 *
 * @author Guillaume sauthier
 */
public class AxisWsClientGenerator extends WsClientGenerator {

    /** unique JVelocity instance */
    private static JVelocity jvelocity = null;

    /**
     * jonas-init-param name for client configuration file declaration
     */
    private static final String CLIENT_CONFIG = "axis.clientConfigFile";

    /**
     * WSDD Extension suffix
     */
    private static final String WSDD_SUFFIX = ".wsdd";

    /**
     * WSDD Extension prefix
     */
    private static final String WSDD_PREFIX = "deploy-client-";

    /** count generated files to assure names unicity */
    private static int count = 0;

    /** generated config file */
    private File generated = null;

    /**
     * WEB-INF/ prefix
     */
    private static final String WEB_PREFIX = "WEB-INF/";

    /**
     * I18n
     */
    private static I18n i18n = I18n.getInstance(AxisWsClientGenerator.class);

    /**
     * Creates a new AxisWsClientGenerator
     * @param config Generator Configuration
     * @param srd WebService Endpoint description
     * @param ddm Web DD Modifier
     * @param archive client archive containing WSDL
     * @throws GenBaseException When instanciation fails
     * @throws WsGenException When instanciation fails
     */
    public AxisWsClientGenerator(Config config, ServiceRefDesc srd, WsClientDDModifier ddm, Archive archive)
            throws GenBaseException, WsGenException {
        super(config, srd, ddm, archive);

        // init velocity
        if (jvelocity == null) {
            String packageName = this.getClass().getPackage().getName();
            packageName = packageName.replace('.', '/');
            jvelocity = new JVelocity(packageName + "/deploy_client.vm");
        }
    }

    /**
     * generate axis specific files
     * @throws WsGenException if WSDL cannot be found in archive
     */
    public void generate() throws WsGenException {

        // the source generation is possible only when a
        // WSDL Definition is provided
        if ((getRef().getWsdlFileName() != null)
                && !getRef().getServiceInterface().getName().equals("javax.xml.rpc.Service")) {

            try {
                // classpath creation
                J2EEArchive j2eeArchive = (J2EEArchive) getArchive();
                AbsModuleClassLoader cl = (AbsModuleClassLoader) j2eeArchive.getModuleClassloader();
                getConfig().setClasspath(getConfig().getClasspath() + cl.getClasspath());


                JOnASWSDL2Java jWsdl2Java = new JOnASWSDL2Java();
                jWsdl2Java.run(this);
                getLogger().log(BasicLevel.INFO, "Web Services Classes successfully generated by Axis.");
            } catch (Exception e) {
                String err = getI18n().getMessage("AxisWsClientGenerator.generate.WSDL2Java");
                e.printStackTrace(System.err);
                throw new WsGenException(err, e);
            }
        }

        // Client-config.wsdd ??
        // only if handlers are specified in ServiceRef
        // but if noConfig set to true, assume that
        // user configure himself the client. (expert)
        // Could we create configuration ?
        if (hasClientConfigFile(getRef())) {
            // create deploy.wsdd
            // use velocity template
            // build a unique file name
            String filename = WSDD_PREFIX + (count++) + WSDD_SUFFIX;

            VelocityContext vctx = VContextFactory.getContext(getRef());

            if (getLogger().isLoggable(BasicLevel.DEBUG)) {
                getLogger().log(BasicLevel.DEBUG, "Creating '" + filename + "'");
            }
            generated = new File(getSources(), filename);

            jvelocity.generate(generated, vctx);

        }
        // End !noConfig
    }

    /**
     * Returns true if given service-ref need a client configuration files
     * @param ref Service Ref
     * @return true if given service-ref need a client configuration files
     */
    private boolean hasClientConfigFile(ServiceRefDesc ref) {
        // no configuration asked
        if (getConfig().isNoConfig()) {
            return false;
        }

        MappingFile mf = ref.getMappingFile();

        if (mf != null) {
            // mapping file defined
            if (mf.getXmlTypeMappings().hasNext()) {
                // we have 1 mapping at least
                return true;
            } else {
                // no mapping
                // return true if ref has Handlers
                return (ref.getHandlerRefs().size() != 0);
            }
        } else {
            // no mapping file
            // return true if ref has Handlers
            return (ref.getHandlerRefs().size() != 0);
        }
    }

    /**
     * Add generated files in given archive
     * @param archive archive where generated fils will be added.
     * @throws WsGenException when files cannot be added
     */
    public void addFiles(Archive archive) throws WsGenException {
        if (archive instanceof WebApp) {
            archive.addDirectoryIn("WEB-INF/classes/", getClasses());

            if (generated != null) {
                archive.addFileIn("WEB-INF/", generated);

                // ensure the optionnal descriptor exists
                if (!getModifier().hasJonasServiceRef()) {
                    if (!getArchive().getContainedFiles().contains("WEB-INF/jonas-web.xml")) {
                        // jonas-web.xml doesn't exists
                        createEmptyJonasWeb((J2EEArchive) archive);
                    }
                    Element jsr = getModifier().createJonasServiceRef(getRef().getServiceRefName());
                    // update
                    getModifier().setElement(jsr);

                }

                // add init param
                getModifier().addJonasInitParam(CLIENT_CONFIG, WEB_PREFIX + generated.getName());
            }
        } else if (archive instanceof EjbJar) {
            archive.addDirectory(getClasses());

            if (generated != null) {
                archive.addFileIn("META-INF/", generated);

                // ensure the optionnal descriptor exists
                if (!getModifier().hasJonasServiceRef()) {
                    Element jsr = getModifier().createJonasServiceRef(getRef().getServiceRefName());
                    // update
                    getModifier().setElement(jsr);
                }

                // add init param
                getModifier().addJonasInitParam(CLIENT_CONFIG, "META-INF/" + generated.getName());
            }
        } else {
            archive.addDirectory(getClasses());

            if (generated != null) {
                archive.addFileIn("META-INF/", generated);

                // ensure the optionnal descriptor exists
                if (!getModifier().hasJonasServiceRef()) {
                    if (!getArchive().getContainedFiles().contains("META-INF/jonas-client.xml")) {
                        // jonas-client.xml doesn't exists
                        createEmptyJonasClient((J2EEArchive) archive);
                    }
                    Element jsr = getModifier().createJonasServiceRef(getRef().getServiceRefName());
                    // update
                    getModifier().setElement(jsr);

                }

                // add init param
                getModifier().addJonasInitParam(CLIENT_CONFIG, "META-INF/" + generated.getName());
            }
        }
    }

    /**
     * Add an empty jonas-web.xml in given J2EEArchive.
     * @param archive archive to be updated
     */
    private void createEmptyJonasWeb(J2EEArchive archive) {
        Document doc = XMLUtils.newJonasWeb();
        archive.getDescriptors().put("WEB-INF/jonas-web.xml", doc);
        getModifier().setDocument(doc);
    }

    /**
     * Add an empty jonas-client.xml in given J2EEArchive.
     * @param archive archive to be updated
     */
    private void createEmptyJonasClient(J2EEArchive archive) {
        Document doc = XMLUtils.newJonasClient();
        archive.getDescriptors().put("META-INF/jonas-client.xml", doc);
        getModifier().setDocument(doc);
    }

    /**
     * @return Returns the i18n.
     */
    public static I18n getI18n() {
        return i18n;
    }
}