/*
 * Copyright 2001-2004 The Apache Software Foundation.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
/**
 * JOnAS : Java(TM) OpenSource Application Server
 * Copyright (C) 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.ews.wsdltoj2ee.writer;

import java.io.IOException;
import java.io.PrintWriter;
import java.util.ArrayList;
import java.util.HashSet;
import java.util.Iterator;
import java.util.List;
import java.util.Map;

import javax.wsdl.Binding;
import javax.wsdl.BindingOperation;
import javax.wsdl.Definition;
import javax.wsdl.Operation;
import javax.wsdl.OperationType;
import javax.wsdl.Port;
import javax.wsdl.Service;
import javax.wsdl.extensions.UnknownExtensibilityElement;
import javax.wsdl.extensions.soap.SOAPBinding;
import javax.xml.namespace.QName;

import org.ow2.jonas.deployment.common.HandlerDesc;
import org.ow2.jonas.deployment.ws.PortComponentDesc;
import org.ow2.jonas.deployment.ws.SSBPortComponentDesc;
import org.ow2.jonas.deployment.ws.ServiceDesc;

import org.apache.axis.Constants;
import org.apache.axis.constants.Scope;
import org.apache.axis.constants.Style;
import org.apache.axis.constants.Use;
import org.apache.axis.deployment.wsdd.WSDDConstants;
import org.apache.axis.utils.JavaUtils;
import org.apache.axis.utils.Messages;
import org.apache.axis.wsdl.symbolTable.BindingEntry;
import org.apache.axis.wsdl.symbolTable.Parameters;
import org.apache.axis.wsdl.symbolTable.SymbolTable;
import org.apache.axis.wsdl.toJava.Emitter;
import org.apache.axis.wsdl.toJava.Utils;



/**
 * This is Wsdl2java's deploy Writer. It writes the server-deploy-XX.wsdd file.
 * Based on J2eeDeployWriter from Ias
 * (http://cvs.apache.org/viewcvs.cgi/ws-axis/contrib/ews/src/org/apache/geronimo/ews/ws4j2ee/toWs/ws/J2eeDeployWriter.java?rev=1.13&view=markup)
 */
public class J2EEServerDeployWriter extends JOnASDeployWriter {

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

    /**
     * Axis parameter name for SE interface name
     */
    private static final String SERVICE_ENDPOINT_INTERFACE_NAME = "serviceEndpointInterfaceName";

    /**
     * Axis parameter name for SE jndi name
     */
    private static final String SERVICE_ENDPOINT_JNDI_NAME = "serviceEndpointJndiName";

    /**
     * Constructor.
     * @param emitter J2EEEmitter
     * @param definition Definition
     * @param symbolTable SymbolTable
     */
    public J2EEServerDeployWriter(Emitter emitter, Definition definition, SymbolTable symbolTable) {
        super(emitter, definition, symbolTable);
    } // ctor

    /**
     * Write out deployment and undeployment instructions for each WSDL service
     * @param pw PrintWriter
     * @throws IOException when services cannot be created
     */
    protected void writeDeployServices(PrintWriter pw) throws IOException {

        // deploy the ports on each service
        Map serviceMap = getDefinition().getServices();
        ServiceDesc desc = getJonasWSContext().getServiceDesc();

        for (Iterator mapIterator = serviceMap.values().iterator(); mapIterator.hasNext();) {
            Service myService = (Service) mapIterator.next();

            pw.println();
            pw.println("  <!-- " + Messages.getMessage("wsdlService00", myService.getQName().getLocalPart()) + " -->");
            pw.println();

            for (Iterator portIterator = myService.getPorts().values().iterator(); portIterator.hasNext();) {
                Port myPort = (Port) portIterator.next();
                BindingEntry bEntry = getSymbolTable().getBindingEntry(myPort.getBinding().getQName());

                // If this isn't an SOAP binding, skip it
                if (bEntry.getBindingType() != BindingEntry.TYPE_SOAP) {
                    continue;
                }

                PortComponentDesc portDesc = findPortComponentDesc(desc, myPort);

                if (portDesc != null) {
                    // write ports described by the current ServiceDesc
                    writeDeployPort(pw, myService, bEntry, portDesc);
                }
            }
        }
    } // writeDeployServices

    /**
     * @param desc JOnAS Service Description
     * @param myPort wsdl:port
     * @return Returns the JOnAS PortComponentDesc associated with the given wsdl:port
     */
    private static PortComponentDesc findPortComponentDesc(ServiceDesc desc, Port myPort) {

        PortComponentDesc port = null;
        List ports = desc.getPortComponents();
        for (Iterator i = ports.iterator(); i.hasNext() && port == null;) {
            PortComponentDesc pcd = (PortComponentDesc) i.next();
            if (pcd.getQName().getLocalPart().equals(myPort.getName())) {
                port = pcd;
            }
        }
        return port;
    }

    /**
     * Write out deployment and undeployment instructions for given WSDL port
     * @param pw PrintWriter
     * @param service wsdl:service
     * @param bEntry BindingEntry
     * @param portComponentDesc JOnAS Port representation
     * @throws IOException IOException
     */
    protected void writeDeployPort(PrintWriter pw, Service service, BindingEntry bEntry,
            PortComponentDesc portComponentDesc) throws IOException {

        String serviceName = portComponentDesc.getServiceName();
        boolean hasLiteral = bEntry.hasLiteral();
        boolean hasMIME = Utils.hasMIME(bEntry);
        String prefix = WSDDConstants.NS_PREFIX_WSDD_JAVA;
        String styleStr = "";
        Iterator iterator = bEntry.getBinding().getExtensibilityElements().iterator();

        // iterate throught extensibilityElements of given Port's Binding
        while (iterator.hasNext()) {
            Object obj = iterator.next();

            if (obj instanceof SOAPBinding) {
                use = Use.ENCODED;
            } else if (obj instanceof UnknownExtensibilityElement) {

                // TODO After WSDL4J supports soap12, change this code
                UnknownExtensibilityElement unkElement = (UnknownExtensibilityElement) obj;
                QName name = unkElement.getElementType();

                if (name.getNamespaceURI().equals(Constants.URI_WSDL12_SOAP) && name.getLocalPart().equals("binding")) {
                    use = Use.ENCODED;
                }
            }
        }

        if (getSymbolTable().isWrapped()) {
            styleStr = " style=\"" + Style.WRAPPED + "\"";
            use = Use.LITERAL;
        } else {
            styleStr = " style=\"" + bEntry.getBindingStyle().getName() + "\"";

            if (hasLiteral) {
                use = Use.LITERAL;
            }
        }

        String useStr = " use=\"" + use + "\"";

        if (portComponentDesc.hasBeanImpl()) {
            pw.println("  <service name=\"" + serviceName + "\" provider=\"" + prefix + ":JOnASEJB\"" + styleStr + useStr + ">");

            SSBPortComponentDesc ssbPCD = (SSBPortComponentDesc) portComponentDesc;

            pw.println("      <parameter name=\"" + SERVICE_ENDPOINT_INTERFACE_NAME + "\" value=\""
                    + ssbPCD.getServiceEndpointInterface().getName() + "\"/>");
            pw.println("      <parameter name=\"" + SERVICE_ENDPOINT_JNDI_NAME + "\" value=\""
                    + ssbPCD.getSessionStatelessDesc().getJndiServiceEndpointName() + "\"/>");

        } else {
            pw.println("  <service name=\"" + serviceName + "\" provider=\"" + prefix + ":RPC\"" + styleStr + useStr + ">");
            pw.println("      <parameter name=\"className\" value=\"" + portComponentDesc.getSIBClassname() + "\"/>");

        }

        pw.println("      <parameter name=\"wsdlTargetNamespace\" value=\"" + service.getQName().getNamespaceURI()
                + "\"/>");
        pw.println("      <parameter name=\"wsdlServiceElement\" value=\"" + service.getQName().getLocalPart()
                        + "\"/>");
        pw.println("      <parameter name=\"wsdlServicePort\" value=\"" + serviceName + "\"/>");

        // MIME attachments don't work with multiref, so turn it off.
        if (hasMIME) {
            pw.println("      <parameter name=\"sendMultiRefs\" value=\"false\"/>");
        }

        writeDeployBinding(pw, bEntry);
        writeDeployTypes(pw, bEntry.getBinding(), hasLiteral, hasMIME, use);

        List handlers = portComponentDesc.getHandlers();
        if (!handlers.isEmpty()) {
            pw.println("    <handlerInfoChain>");
            for (Iterator i = handlers.iterator(); i.hasNext();) {
                writeHandler(pw, (HandlerDesc) i.next());
            }
            pw.println("    </handlerInfoChain>");
        }
        pw.println("  </service>");
    }

    /**
     * Write out deployment instructions for given WSDL binding
     * @param pw PrintWriter
     * @param bEntry BindingEntry
     * @throws IOException IOException
     */
    protected void writeDeployBinding(PrintWriter pw, BindingEntry bEntry) throws IOException {

        Binding binding = bEntry.getBinding();

        pw.println("      <parameter name=\"wsdlPortType\" value=\"" + binding.getPortType().getQName().getLocalPart()
                + "\"/>");
        // force JAXRPC 1.1 Type Mapping
        pw.println("      <parameter name=\"typeMappingVersion\" value=\"" + emitter.getTypeMappingVersion() + "\"/>");

        HashSet allowedMethods = new HashSet();

        if (!emitter.isSkeletonWanted()) {
            Iterator operationsIterator = binding.getBindingOperations().iterator();

            for (; operationsIterator.hasNext();) {
                BindingOperation bindingOper = (BindingOperation) operationsIterator.next();
                Operation operation = bindingOper.getOperation();
                OperationType type = operation.getStyle();
                String javaOperName = JavaUtils.xmlNameToJava(operation.getName());

                // These operation types are not supported. The signature
                // will be a string stating that fact.
                if ((type == OperationType.NOTIFICATION) || (type == OperationType.SOLICIT_RESPONSE)) {
                    continue;
                }

                allowedMethods.add(javaOperName);

                // We pass "" as the namespace argument because we're just
                // interested in the return type for now.
                Parameters params = getSymbolTable().getOperationParameters(operation, "", bEntry);

                if (params != null) {

                    // Get the operation QName
                    QName elementQName = Utils.getOperationQName(bindingOper, bEntry, getSymbolTable());

                    // Get the operation's return QName and type
                    QName returnQName = null;
                    QName returnType = null;

                    if (params.returnParam != null) {
                        returnQName = params.returnParam.getQName();
                        returnType = Utils.getXSIType(params.returnParam);
                    }

                    // Get the operations faults
                    Map faultMap = bEntry.getFaults();
                    ArrayList faults = null;

                    if (faultMap != null) {
                        faults = (ArrayList) faultMap.get(bindingOper);
                    }

                    String soapAction = Utils.getOperationSOAPAction(bindingOper);

                    // Write the operation metadata
                    writeOperation(pw, javaOperName, elementQName, returnQName, returnType, params, faults, soapAction);
                }
            }
        }

        pw.print("      <parameter name=\"allowedMethods\" value=\"");

        if (allowedMethods.isEmpty()) {
            pw.println("*\"/>");
        } else {
            boolean first = true;

            for (Iterator i = allowedMethods.iterator(); i.hasNext();) {
                String method = (String) i.next();

                if (first) {
                    pw.print(method);

                    first = false;
                } else {
                    pw.print(" " + method);
                }
            }

            pw.println("\"/>");
        }

        Scope scope = emitter.getScope();

        if (scope != null) {
            pw.println("      <parameter name=\"scope\" value=\"" + scope.getName() + "\"/>");
        }
    }

    /**
     * @param pw PrintWriter
     * @param handler the server Handler to write
     */
    protected void writeHandler(PrintWriter pw, HandlerDesc handler) {
        pw.println("      <handlerInfo classname=\"" + handler.getHandlerClassName() + "\">");
        Map params = handler.getInitParams();
        for (Iterator i = params.keySet().iterator(); i.hasNext();) {
            String pName = (String) i.next();
            pw.println("        <parameter name=\"" + pName + "\" value=\""
                    + params.get(pName) + "\"/>");
        }
        List headers = handler.getSOAPHeaders();
        for (Iterator i = headers.iterator(); i.hasNext();) {
            QName sh = (QName) i.next();
            pw.println("        <header xmlns:ns=\"" + sh.getNamespaceURI() + "\" qname=\"ns:"
                    + sh.getLocalPart() + "\"/>");
        }
        pw.println("      </handlerInfo>");
        List roles = handler.getSOAPRoles();
        for (Iterator i = roles.iterator(); i.hasNext();) {
            String role = (String) i.next();
            pw.println("      <role soapActorName=\"" + role + "\"/>");
        }
    }

    /**
     * @return Returns "deploy-server-"
     */
    protected String getPrefix() {
        return WSDD_PREFIX;
    }

}
