/*
 * Copyright (c) 2002-2003 IST-2004-2006-511731 ModelWare - ModelBus.
 * All rights reserved.
 *
 * This software is published under the terms of the ModelBus Software License
 * 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.
 * A copy of ModelBus Software License is provided with this distribution in
 * doc/LICENSE.txt file.
 */

/*
 * Created on 20 mai 2005
 *
 */
package org.eclipse.mddi.modelbus.adapter.infrastructure.transport.server.ws;

import java.util.Collection;
import java.util.HashSet;
import java.util.List;

import javax.xml.soap.SOAPBody;
import javax.xml.soap.SOAPElement;
import javax.xml.soap.SOAPEnvelope;
import javax.xml.soap.SOAPException;

import org.apache.log4j.Logger;
import org.eclipse.mddi.modelbus.adapter.infrastructure.LoggerConfigurator;
import org.eclipse.mddi.modelbus.adapter.infrastructure.model_manipulation.DescriptionUtil;
import org.eclipse.mddi.modelbus.adapter.infrastructure.transport.marshal.ws.AbstractMarshaler;
import org.eclipse.mddi.modelbus.adapter.infrastructure.transport.marshal.ws.CopyRestoreFragmentMarshaler;
import org.eclipse.mddi.modelbus.adapter.infrastructure.transport.marshal.ws.SoapUtil;
import org.eclipse.mddi.modelbus.adapter.user.AdapterStub;
import org.eclipse.mddi.modelbus.adapter.user.ModelingServiceError;
import org.eclipse.mddi.modelbus.adapter.user.SessionNeeded;
import org.eclipse.mddi.modelbus.adapter.user.consumer.ModelTypeMismatchException;
import org.eclipse.mddi.modelbus.adapter.user.provider.GenericProvider;
import org.eclipse.mddi.modelbus.description.abstract_.ModelingService;
import org.eclipse.mddi.modelbus.description.abstract_.Parameter;

/**
 * 
 * This class handles the reception of Web Services invocation
 * 
 * @author Prawee Sriplakich (LIP6), Andrey Sadovykh (LIP6)
 * 
 * 
 */
public class CopyRestoreFragmentWsInvocationReceiver extends
        WsInvocationReceiver {

    AdapterStub adapter;

    SOAPEnvelope reqMsg;

    SOAPEnvelope resMsg;

    ModelingService ms;

    // an array of in/inout parameter values
    // if usesScope option is set, it must contain two extra elements:
    // "outScope", "inoutScope"
    Object[] unmarshalledInputs;

    // an array of out/inout parameter values
    Object[] unmarshalledOutputs;

    Collection outScope = null, inoutScope = null;

    private static Logger logger = Logger
            .getLogger(CopyRestoreFragmentWsInvocationReceiver.class);

    public CopyRestoreFragmentWsInvocationReceiver(AdapterStub adapter) {
        LoggerConfigurator.configure();
        this.adapter = adapter;
        setMarshaler(new CopyRestoreFragmentMarshaler(adapter.getProperties()));
    }

    /*
     * (non-Javadoc)
     * 
     * @see org.eclipse.mddi.modelbus.adapter.user.provider.WsInvocationReceiver#process(javax.xml.soap.SOAPMessage,
     *      javax.xml.soap.SOAPMessage)
     */
    public void process(SOAPEnvelope reqMsg, SOAPEnvelope resMsg)
            throws SOAPException {
        this.reqMsg = reqMsg;
        this.resMsg = resMsg;
        try {
            readRequest();
            performService();
            createResponse();
        } catch (Exception e) {
            DefaultWsInvocationReceiver.createSoapFault(resMsg, e);
        }
    }

    /**
     * Reads the content in the HTTP request
     * 
     * @throws Exception
     */
    void readRequest() throws Exception {

        SOAPBody requestbody = reqMsg.getBody();
        // the first child in the SOAP body is a input message conforming to
        // WSDL
        SOAPElement reqTopElem = SoapUtil.getFirstChild(requestbody);
        if (reqTopElem == null) {
            logger.error("SERVER: receive empty request");
            logger.debug(reqMsg);
            throw new SOAPException("Empty SOAP request");
        }

        String serviceName = reqTopElem.getLocalName();

        ms = DescriptionUtil.getServiceDescription(serviceName, adapter
                .getToolDescription());
        if (ms == null) {
            throw new SOAPException("Service unknown - " + serviceName);
        }

        List inputParams = DescriptionUtil.get_in_inout_Parameters(ms);
        if (getCopyRestoreFragmentModelMarshaler().isUseScope()) {
            unmarshalledInputs = new Object[inputParams.size() + 2];

            getCopyRestoreFragmentModelMarshaler().unmarshallScope(reqTopElem);

            if (DescriptionUtil.getOutParameters(ms).size() > 0) {
                outScope = new HashSet();
                unmarshalledInputs[inputParams.size()] = outScope;
            }
            if (DescriptionUtil.getInoutParameters(ms).size() > 0) {
                inoutScope = getCopyRestoreFragmentModelMarshaler()
                        .getInoutScope();
                unmarshalledInputs[inputParams.size() + 1] = inoutScope;
            }

        } else {
            unmarshalledInputs = new Object[inputParams.size()];
        }

        for (int i = 0; i < inputParams.size(); i++) {
            Parameter p = (Parameter) inputParams.get(i);
            unmarshalledInputs[i] = marshaler.unmarshal(p, reqTopElem);
        }
    }

    void performService() throws SessionNeeded, ModelingServiceError {
        GenericProvider si = adapter.getToolStub().getProvider();
        String serviceName = ms.getName();
        unmarshalledOutputs = si.execute(serviceName, unmarshalledInputs);
    }

    /**
     * Produces the HTTP response
     * 
     * @throws Exception
     */
    void createResponse() throws Exception {
        SOAPBody responsebody = resMsg.getBody();
        AbstractMarshaler.setNamespaces(responsebody);

        SOAPElement resTopElem = responsebody.addChildElement(ms.getName()
                + "Response", "modelbus");
        List outputParams = DescriptionUtil.get_out_inout_Parameters(ms);

        if (unmarshalledOutputs.length != outputParams.size()) {
            throw new ModelTypeMismatchException(
                    "Bad number of output parameters, got "
                            + unmarshalledOutputs.length + " expected "
                            + outputParams.size());
        }

        // marshall Scope
        if (getCopyRestoreFragmentModelMarshaler().isUseScope()) {
            if (outScope != null) {
                getCopyRestoreFragmentModelMarshaler().marshallScope(
                        CopyRestoreFragmentMarshaler.OUT_SCOPE, outScope,
                        resTopElem);
            }
            if (inoutScope != null) {
                getCopyRestoreFragmentModelMarshaler().marshallScope(
                        CopyRestoreFragmentMarshaler.INOUT_SCOPE, inoutScope,
                        resTopElem);
            }
        }

        for (int i = 0; i < outputParams.size(); i++) {
            Parameter p = (Parameter) outputParams.get(i);
            marshaler.marshal(p, unmarshalledOutputs[i], resTopElem);
        }
    }

    public CopyRestoreFragmentMarshaler getCopyRestoreFragmentModelMarshaler() {
        return (CopyRestoreFragmentMarshaler) getMarshaler();
    }

}
