/*
 * 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 15 juin 2005
 *
 */
package org.eclipse.mddi.modelbus.adapter.infrastructure.transport.client.ws;

import java.util.List;
import java.util.Properties;

import javax.xml.soap.SOAPBody;
import javax.xml.soap.SOAPConnection;
import javax.xml.soap.SOAPElement;
import javax.xml.soap.SOAPException;
import javax.xml.soap.SOAPFault;
import javax.xml.soap.SOAPMessage;

import org.apache.log4j.Logger;
import org.eclipse.mddi.modelbus.adapter.infrastructure.model_manipulation.DescriptionUtil;
import org.eclipse.mddi.modelbus.adapter.infrastructure.transport.client.AbstractInvocationSender;
import org.eclipse.mddi.modelbus.adapter.infrastructure.transport.marshal.ws.AbstractMarshaler;
import org.eclipse.mddi.modelbus.adapter.infrastructure.transport.marshal.ws.DefaultMarshaler;
import org.eclipse.mddi.modelbus.adapter.infrastructure.transport.marshal.ws.SoapUtil;
import org.eclipse.mddi.modelbus.adapter.user.ModelingServiceError;
import org.eclipse.mddi.modelbus.adapter.user.consumer.ModelBusCommunicationException;
import org.eclipse.mddi.modelbus.adapter.user.consumer.ModelTypeMismatchException;
import org.eclipse.mddi.modelbus.adapter.user.consumer.NoToolAvailableException;
import org.eclipse.mddi.modelbus.adapter.user.consumer.ServiceUnknownException;
import org.eclipse.mddi.modelbus.description.abstract_.ModelingService;
import org.eclipse.mddi.modelbus.description.abstract_.Parameter;

/**
 * 
 * 
 * @author Prawee Sriplakich, Andrey Sadovykh (LIP6)
 * 
 * 
 */
public class DefaultWsInvocationSender extends AbstractInvocationSender {

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

    // AdapterStub adapter;

    public SOAPMessage requestMsg;

    public SOAPMessage responseMsg;

    public String targetUrl;

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

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

    public ModelingService ms;

    public DefaultWsInvocationSender(Properties prop) {
        if (getMarshaler()==null) setMarshaler(new DefaultMarshaler(prop));
    }

    /*
     * (non-Javadoc)
     * 
     * @see org.eclipse.mddi.modelbus.adapter.user.provider.WsInvocationSender#invoke(org.eclipse.mddi.modelbus.description.concrete.Tool,
     *      org.eclipse.mddi.modelbus.description.abstract_.ModelingService,
     *      java.lang.Object[])
     */
    public Object[] invoke(ModelingService ms, Object[] inputs)
            throws ServiceUnknownException, NoToolAvailableException,
            ModelTypeMismatchException, ModelBusCommunicationException,
            ModelingServiceError {

        this.targetUrl = DescriptionUtil.getProperty(getToolDescription(),
                "URL");
        this.ms = ms;
        this.unmarshalledInputs = inputs;

        try {
            createRequestMessage();
            makeConnection();
            readResponseMessage();
            return unmarshalledOutputs;

        } catch (SOAPException e) {
            throw new ModelBusCommunicationException(e.getMessage(), e);
        }

    }

    public void createRequestMessage() throws ModelBusCommunicationException,
            ModelTypeMismatchException {

        if (requestMsg == null)
            try {
                requestMsg = SoapUtil.messageFactory.createMessage();
                SOAPBody requestbody = requestMsg.getSOAPBody();
                AbstractMarshaler.setNamespaces(requestbody);
                SOAPElement reqTopElem = requestbody.addChildElement(ms
                        .getName(), "modelbus");
                List inputParams = DescriptionUtil.get_in_inout_Parameters(ms);
                // check the number of parameters
                if (unmarshalledInputs.length != inputParams.size())
                    throw new ModelTypeMismatchException(
                            "Bad number of input parameters, got "
                                    + unmarshalledInputs.length + " expected "
                                    + inputParams.size());

                // marshal parameters
                Parameter[] params = new Parameter[inputParams.size()];
                for (int i = 0; i < inputParams.size(); i++)
                    params[i] = (Parameter) inputParams.get(i);
                //logger.debug("Serializer:"+getMarshaler().getSerializer());


                getMarshaler().marshal(params, unmarshalledInputs, reqTopElem);
            } catch (Exception e) {
                throw new ModelBusCommunicationException(
                        "Can't create request message", e);
            }

    }

    public void makeConnection() throws SOAPException {

        logger.debug("Call URL: " + targetUrl);

        SOAPConnection con = SoapUtil.connectionFactory.createConnection();

        logger.debug("Call with Message: " + requestMsg.getSOAPBody().toString());

        responseMsg = con.call(requestMsg, targetUrl);
        con.close();
        logger.debug("Response Message: " + responseMsg.getSOAPBody().toString());

    }

    // unmarshall response message
    public void readResponseMessage() throws ModelBusCommunicationException,
            ModelingServiceError {

        SOAPBody responsebody = null;

        try {
            responsebody = responseMsg.getSOAPBody();
        } catch (SOAPException e1) {
            throw new ModelBusCommunicationException(
                    "Can't parse response message", e1);
        }
        if (responsebody.hasFault()) {
            SOAPFault f = responsebody.getFault();
            if (AbstractMarshaler.isModelingServiceError(f)) {
                ModelingServiceError err = AbstractMarshaler
                        .unmarshallModelingServiceError(f);
                throw err;
            } else {
                throw new ModelBusCommunicationException(f.toString()
                        + "[ uri = " + targetUrl + "]");
            }

        } else {
            SOAPElement resTopElem = SoapUtil.getFirstChild(responsebody);
            if (resTopElem == null) {
                logger.error("Empty SOAP response");
                logger.debug(responsebody.toString());
                throw new ModelBusCommunicationException("Empty SOAP response");
            }

            // unmarchal params
            List outputParams = DescriptionUtil.get_out_inout_Parameters(ms);

            Parameter[] params = new Parameter[outputParams.size()];
            for (int i = 0; i < outputParams.size(); i++)
                params[i] = (Parameter) outputParams.get(i);
            try {
                unmarshalledOutputs = getMarshaler().unmarshal(params,
                        resTopElem);
            } catch (Exception e) {
                throw new ModelBusCommunicationException(
                        "Can't unmarshal response message", e);
            }
        }
    }
}