/**
 * JOnAS : Java(TM) OpenSource Application Server
 * Copyright (C) 2004-2005 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.ews;

import java.io.File;
import java.util.Iterator;
import java.util.List;

import org.ow2.jonas.deployment.common.HandlerDesc;
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.Client;
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.ews.wsdltoj2ee.JOnASJ2EEWebServicesContext;
import org.ow2.jonas.generators.wsgen.generator.ews.wsdltoj2ee.ServiceReferenceContextImpl;
import org.ow2.jonas.generators.wsgen.generator.ews.wsdltoj2ee.emitter.FullEmitter;
import org.ow2.jonas.generators.wsgen.generator.ews.wsdltoj2ee.factory.JOnASClientGeneratorFactory;
import org.ow2.jonas.generators.wsgen.generator.ews.wsdltoj2ee.wscf.JOnASWSCFHandler;
import org.ow2.jonas.lib.loader.AbsModuleClassLoader;
import org.ow2.jonas.lib.util.I18n;
import org.ow2.jonas.lib.util.Log;
import org.w3c.dom.Document;
import org.w3c.dom.Element;

import org.apache.axis.constants.Scope;
import org.apache.axis.wsdl.toJava.GeneratedFileInfo;
import org.apache.ws.ews.context.webservices.client.ServiceReferenceContext;




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

/**
 * 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 EWSWsClientGenerator extends WsClientGenerator {

    /**
     * logger
     */
    private static Logger logger = Log.getLogger(Log.JONAS_WSGEN_EWS_PREFIX);

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

    /** 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(EWSWsClientGenerator.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
     */
    public EWSWsClientGenerator(Config config, ServiceRefDesc srd, WsClientDDModifier ddm, Archive archive)
            throws GenBaseException {
        super(config, srd, ddm, archive);
    }

    /**
     * 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
        // we need :
        // - a: wsdl-file
        // - b: jaxrpc-mapping-file
        // - c: service-interface != javax.xml.rpc.Service
        // - d: some port-component-ref
        // = ((a && b) && (c || d))

        boolean hasWSDL = getRef().getWsdlFileName() != null;

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

            try {
                // WSDL + mapping file
                FullEmitter wsEmitter = new FullEmitter();
                wsEmitter.setMappingFileInputStream(getRef().getMappingFileURL().openStream());
                JOnASClientGeneratorFactory factory = new JOnASClientGeneratorFactory();
                factory.setEmitter(wsEmitter);

                wsEmitter.setTypeMappingVersion("1.3");
                wsEmitter.setScope(Scope.REQUEST);
                wsEmitter.setWrapArrays(true);

                // Classloader to use to check if classes are present before
                // generating java classes
                wsEmitter.setClassLoader(cl);

                wsEmitter.setFactory(factory);
                //wsEmitter.setNowrap(true);

                wsEmitter.setJOnASWsContext(prepareJ2EEWebServicesContext(getRef()));

                wsEmitter.setOutputDir(this.getSources().getCanonicalPath());
                wsEmitter.setAllowInvalidURL(true);

                wsEmitter.run(getRef().getLocalWSDLURL().toExternalForm());

                generated = new File(findClientConfigFile(wsEmitter.getGeneratedFileInfo()));

                getLogger().log(BasicLevel.INFO, "Web Services Classes successfully generated by EWS.");
                getLogger().log(BasicLevel.INFO,
                        "Webservice client WSDD file '" + generated + "' sucessfully generated by EWS.");
            } catch (Exception e) {
                String err = getI18n().getMessage("EWSWsClientGenerator.generate.WSDL2Java", e.getMessage());
                logger.log(BasicLevel.ERROR, err, e);
                throw new WsGenException(err, e);
            }

        }

    }

    /**
     * @param generatedFileInfo files generated by Axis
     * @return Returns the first filename that matches "deploy-client-*.wsdd"
     */
    private static String findClientConfigFile(GeneratedFileInfo generatedFileInfo) {
        List generated = generatedFileInfo.getFileNames();
        boolean found = false;
        String filename = null;
        for (Iterator i = generated.iterator(); i.hasNext() && !found;) {
            String entry = (String) i.next();
            // */deploy-client-*.wsdd
            if (entry.matches(".*deploy-client-\\d+\\.wsdd$")) {
                found = true;
                filename = entry;
            }
        }
        return filename;
    }

    /**
     * @param ref the ServiceRefDesc to be used in J2EE WS Context
     * @return Returns a configured J2EEWebServiceContext
     */
    private JOnASJ2EEWebServicesContext prepareJ2EEWebServicesContext(ServiceRefDesc ref) {

        JOnASJ2EEWebServicesContext wsCtx = new JOnASJ2EEWebServicesContext();
        ServiceReferenceContext sRefCtx = new ServiceReferenceContextImpl();
        sRefCtx.setServiceQName(ref.getServiceQName());

        for (Iterator i = ref.getHandlerRefs().iterator(); i.hasNext();) {
            HandlerDesc handler = (HandlerDesc) i.next();
            sRefCtx.addHandler(new JOnASWSCFHandler(handler));
        }
        wsCtx.addServiceReferenceContext(sRefCtx);
        return wsCtx;
    }

    /**
     * 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) {
            WebApp web = (WebApp) archive;
            archive.addDirectoryIn("WEB-INF/classes/", getClasses());
            archive.addDirectoryIn("WEB-INF/sources/", getSources());

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

                // ensure the optionnal descriptor exists
                if (!getModifier().hasJonasServiceRef()) {
                    boolean jwaFileExists = getArchive().getContainedFiles().contains("WEB-INF/jonas-web.xml");
                    boolean jwaDocumentExists = web.getDescriptors().containsKey("WEB-INF/jonas-web.xml");
                    if (!(jwaFileExists || jwaDocumentExists)) {
                        // 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 {
            // We have a Client archive
            Client client = (Client) archive;
            archive.addDirectory(getClasses());

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

                // ensure the optionnal descriptor exists
                if (!getModifier().hasJonasServiceRef()) {
                    boolean jcFileExists = getArchive().getContainedFiles().contains("META-INF/jonas-client.xml");
                    boolean jcDocumentExists = client.getDescriptors().containsKey("META-INF/jonas-client.xml");
                    if (!(jcFileExists || jcDocumentExists)) {
                        // 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;
    }
}