/**
 * JOnAS: Java(TM) Open Application Server
 * Copyright (C) 2009 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.ws.axis2.util;

import java.io.OutputStream;
import java.lang.reflect.Constructor;
import java.util.ArrayList;
import java.util.HashMap;

import org.apache.axiom.om.OMElement;
import org.apache.axis2.context.ConfigurationContext;
import org.apache.axis2.context.ConfigurationContextFactory;
import org.apache.axis2.deployment.util.Utils;
import org.apache.axis2.description.AxisService;
import org.apache.axis2.description.AxisService2WSDL20;
import org.apache.axis2.description.WSDL2Constants;
import org.apache.axis2.description.java2wsdl.DefaultNamespaceGenerator;
import org.apache.axis2.description.java2wsdl.DefaultSchemaGenerator;
import org.apache.axis2.description.java2wsdl.DocLitBareSchemaGenerator;
import org.apache.axis2.description.java2wsdl.Java2WSDLConstants;
import org.apache.axis2.description.java2wsdl.Java2WSDLUtils;
import org.apache.axis2.description.java2wsdl.NamespaceGenerator;
import org.apache.axis2.description.java2wsdl.SchemaGenerator;
import org.apache.axis2.engine.AxisConfiguration;
import org.apache.axis2.engine.MessageReceiver;
import org.apache.axis2.util.Loader;
import org.apache.axis2.util.XMLPrettyPrinter;
import org.apache.ws.java2wsdl.Java2WSDLBuilder;

public class Axis2Java2WSDLBuilder extends Java2WSDLBuilder {


    public static final String ALL = "all";
    private OutputStream out;
    private String className;
    private ClassLoader classLoader;

    private String serviceName = null;

    //these apply for the WSDL
    private String targetNamespace = null;
    private String targetNamespacePrefix = null;

    private String style = Java2WSDLConstants.DOCUMENT;
    private String use = Java2WSDLConstants.LITERAL;

    private String nsGenClassName = null;

    private String wsdlVersion = WSDL_VERSION_1;
    private String schemaGenClassName = null;
    private boolean generateDocLitBare = false;
    private AxisConfiguration axisConfig;

    private String epr = null;

    public Axis2Java2WSDLBuilder(final String epr, final OutputStream out, final String className, final ClassLoader classLoader) {
        super(out, className, classLoader);
        try {
            ConfigurationContext configCtx =
                    ConfigurationContextFactory.createDefaultConfigurationContext();
            axisConfig = configCtx.getAxisConfiguration();
            this.out = out;
            this.className = className;
            this.classLoader = classLoader;
        } catch (Exception e) {
            throw new RuntimeException(e);
        }
        this.epr = epr;
    }

    @Override
    public void generateWSDL() throws Exception {
        SchemaGenerator schemaGenerator = resolveSchemaGen(classLoader,
                                                           className,
                                                           getSchemaTargetNamespace(),
                                                           getSchemaTargetNamespacePrefix());

        ArrayList excludedOperation = new ArrayList();
        Utils.addExcludeMethods(excludedOperation);
        schemaGenerator.setExcludeMethods(excludedOperation);
        schemaGenerator.setAttrFormDefault(getAttrFormDefault());
        schemaGenerator.setElementFormDefault(getElementFormDefault());
        schemaGenerator.setExtraClasses(getExtraClasses());
        schemaGenerator.setNsGen(resolveNSGen());
        schemaGenerator.setPkg2nsmap(getPkg2nsMap());
        if (getPkg2nsMap() != null && !getPkg2nsMap().isEmpty() &&
            (getPkg2nsMap().containsKey(ALL) || getPkg2nsMap().containsKey(ALL.toUpperCase()))) {
            schemaGenerator.setUseWSDLTypesNamespace(true);
        }

        HashMap messageReciverMap = new HashMap();
        Class inOnlyMessageReceiver = Loader.loadClass(
                "org.apache.axis2.rpc.receivers.RPCInOnlyMessageReceiver");
        MessageReceiver messageReceiver =
                (MessageReceiver) inOnlyMessageReceiver.newInstance();
        messageReciverMap.put(
                WSDL2Constants.MEP_URI_IN_ONLY,
                messageReceiver);
        Class inoutMessageReceiver = Loader.loadClass(
                "org.apache.axis2.rpc.receivers.RPCMessageReceiver");
        MessageReceiver inOutmessageReceiver =
                (MessageReceiver) inoutMessageReceiver.newInstance();
        messageReciverMap.put(
                WSDL2Constants.MEP_URI_IN_OUT,
                inOutmessageReceiver);
        AxisService service = new AxisService();
        schemaGenerator.setAxisService(service);
        AxisService axisService = AxisService.createService(className,
                                                            serviceName == null ? Java2WSDLUtils.getSimpleClassName(className) : serviceName,
                                                            axisConfig,
                                                            messageReciverMap,
                                                            targetNamespace == null ? Java2WSDLUtils.namespaceFromClassName(className, classLoader, resolveNSGen()).toString() : targetNamespace,
                                                            classLoader,
                                                            schemaGenerator, service);
        schemaGenerator.setAxisService(axisService);
        axisService.setTargetNamespacePrefix(targetNamespacePrefix);
        axisService.setSchemaTargetNamespace(getSchemaTargetNamespace());
        axisService.setSchematargetNamespacePrefix(getSchemaTargetNamespacePrefix());
        axisService.setEndpointURL(epr);

        axisConfig.addService(axisService);

        if (WSDL_VERSION_1.equals(wsdlVersion)) {
            AxisService2WSDL11 g = new AxisService2WSDL11(axisService);
            g.setStyle(this.style);
            g.setUse(this.use);
            OMElement wsdlElement = g.generateOM();
            if (!isPretty()) {
                wsdlElement.serialize(out);
            } else {
                XMLPrettyPrinter.prettify(wsdlElement, out);
            }
        } else {
            AxisService2WSDL20 g = new AxisService2WSDL20(axisService);
            OMElement wsdlElement = g.generateOM();
            if (!isPretty()) {
                wsdlElement.serialize(out);
            } else {
                XMLPrettyPrinter.prettify(wsdlElement, out);
            }
        }
        out.flush();
        out.close();
    }

    private NamespaceGenerator resolveNSGen() {
        NamespaceGenerator nsGen;
        if (this.nsGenClassName == null) {
            nsGen = new DefaultNamespaceGenerator();
        } else {
            try {
                nsGen = (NamespaceGenerator) Class.forName(this.nsGenClassName).newInstance();
            } catch (Exception e) {
                nsGen = new DefaultNamespaceGenerator();
            }
        }
        return nsGen;
    }

    private SchemaGenerator resolveSchemaGen(final ClassLoader loader, final String className,
                                             final String schematargetNamespace,
                                             final String schematargetNamespacePrefix) throws Exception {
        SchemaGenerator schemaGen;
        if (this.schemaGenClassName == null) {
            if (generateDocLitBare) {
                schemaGen = new DocLitBareSchemaGenerator(
                        loader, className, schematargetNamespace,
                        schematargetNamespacePrefix, null);
            } else {
                schemaGen = new DefaultSchemaGenerator(
                        loader, className, schematargetNamespace,
                        schematargetNamespacePrefix, null);
            }

        } else {
            try {
                Class clazz = Class.forName(this.schemaGenClassName);
                Constructor constructor = clazz.getConstructor(
                        new Class[]{ClassLoader.class, String.class, String.class, String.class});
                schemaGen = (SchemaGenerator) constructor.newInstance(
                        new Object[]{loader, className, schematargetNamespace, schematargetNamespacePrefix});
            } catch (Exception e) {
                if (generateDocLitBare) {
                    schemaGen = new DocLitBareSchemaGenerator(
                            loader, className, schematargetNamespace,
                            schematargetNamespacePrefix, null);
                } else {
                    schemaGen = new DefaultSchemaGenerator(
                            loader, className, schematargetNamespace,
                            schematargetNamespacePrefix, null);
                }

            }
        }
        return schemaGen;
    }
}
