/*
 * Licensed to the Apache Software Foundation (ASF) under one
 * or more contributor license agreements. See the NOTICE file
 * distributed with this work for additional information
 * regarding copyright ownership. The ASF licenses this file
 * to you 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.
 */

package org.apache.axis2.jaxws.server.endpoint;

import org.apache.axis2.jaxws.ExceptionFactory;
import org.apache.axis2.jaxws.binding.BindingUtils;
import org.apache.axis2.jaxws.core.MessageContext;
import org.apache.axis2.jaxws.core.util.MessageContextUtils;
import org.apache.axis2.jaxws.description.EndpointDescription;
import org.apache.axis2.jaxws.description.EndpointInterfaceDescription;
import org.apache.axis2.jaxws.description.OperationDescription;
import org.apache.axis2.jaxws.description.ServiceDescription;
import org.apache.axis2.jaxws.i18n.Messages;
import org.apache.axis2.jaxws.message.Message;
import org.apache.axis2.jaxws.message.Protocol;
import org.apache.axis2.jaxws.message.XMLFault;
import org.apache.axis2.jaxws.message.XMLFaultCode;
import org.apache.axis2.jaxws.message.XMLFaultReason;
import org.apache.axis2.jaxws.message.factory.MessageFactory;
import org.apache.axis2.jaxws.registry.FactoryRegistry;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;

import javax.xml.stream.XMLStreamException;
import javax.xml.ws.http.HTTPBinding;
import java.util.Collection;
import java.util.Iterator;

public class Utils {
    
    public static final Log log = LogFactory.getLog(Utils.class);
    
    @Deprecated
    /**
     * Compares the version of the message in the MessageContext to what's expected
     * given the ServiceDescription.  The behavior is described in the SOAP 1.2
     * specification under Appendix 'A'.
     * 
     * @param mc
     * @param serviceDesc
     * @return
     */
    public static boolean bindingTypesMatch(MessageContext mc, ServiceDescription serviceDesc) {
        Collection<EndpointDescription> eds = serviceDesc.getEndpointDescriptions_AsCollection();

        // Dispatch endpoints do not have SEIs, so watch out for null or empty array
        if ((eds != null) && (eds.size() > 0)) {
            Iterator<EndpointDescription> i = eds.iterator();
            if (i.hasNext()) {
                EndpointDescription ed = eds.iterator().next();
                
                Protocol protocol = mc.getMessage().getProtocol();
                String bindingType = ed.getBindingType();
                
                if (log.isDebugEnabled()) {
                    log.debug("Checking for matching binding types.");
                    log.debug("    message protocol: " + protocol);
                    log.debug("        binding type: " + bindingType);
                }
                
                if (protocol.equals(Protocol.soap11)) { 
                	return (BindingUtils.isSOAP11Binding(bindingType));
                } else if (protocol.equals(Protocol.soap12)) {
                	return (BindingUtils.isSOAP12Binding(bindingType));               	
                } else if (protocol.equals(Protocol.rest)) {
                    return HTTPBinding.HTTP_BINDING.equalsIgnoreCase(bindingType);
                }                
            }
            else {
                if (log.isDebugEnabled()) {
                    log.debug("There were no endpoint descriptions found, thus the binding match failed.");
                }
                return false; 
            }
        }
        return true;
    }
    
    /**
     * Compares the version of the message in the MessageContext to what's expected
     * given the ServiceDescription.  The behavior is described in the SOAP 1.2
     * specification under Appendix 'A'.
     * 
     * @param mc
     * @param serviceDesc
     * @return
     */
    public static boolean bindingTypesMatch(MessageContext mc, EndpointDescription ed) {
        
        Protocol protocol = mc.getMessage().getProtocol();
        String bindingType = ed.getBindingType();
        
        if (log.isDebugEnabled()) {
            log.debug("Checking for matching binding types.");
            log.debug("    message protocol: " + protocol);
            log.debug("        binding type: " + bindingType);
        }
        
        if (protocol.equals(Protocol.soap11)) { 
                return (BindingUtils.isSOAP11Binding(bindingType));
        } else if (protocol.equals(Protocol.soap12)) {
                return (BindingUtils.isSOAP12Binding(bindingType));                     
        } else if (protocol.equals(Protocol.rest)) {
            return HTTPBinding.HTTP_BINDING.equalsIgnoreCase(bindingType);
        }               
        return true;
    }
    
    /**
     * Creates a fault message that reflects a version mismatch for the configured message protocol.
     * The returned message will always be a SOAP 1.1 message per the specification.
     * 
     * @param mc
     * @param msg
     * @return
     */
    public static MessageContext createVersionMismatchMessage(MessageContext mc, Protocol protocol) {
        // Only if protocol is soap12 and MISmatches the endpoint do we halt processing
        if (protocol.equals(Protocol.soap12)) {
            String msg = "Incoming SOAP message protocol is version 1.2, but endpoint is configured for SOAP 1.1";
            return Utils.createFaultMessage(mc, msg);
        } else if (protocol.equals(Protocol.soap11)) {
            // SOAP 1.1 message and SOAP 1.2 binding

            // The canSupport flag indicates that we can support this scenario.
            // Possible Examples of canSupport:  JAXB impl binding, JAXB Provider
            // Possible Example of !canSupport: Application handler usage, non-JAXB Provider
            // Initially I vote to hard code this as false.
            boolean canSupport = false;
            if (canSupport) {
                // TODO: Okay, but we need to scrub the Message create code to make sure that the response message
                // is always built from the receiver protocol...not the binding protocol
                return null;
            } else {
                String msg = "Incoming SOAP message protocol is version 1.1, but endpoint is configured for SOAP 1.2.  This is not supported.";
                return Utils.createFaultMessage(mc, msg);
            }
        } else {
            String msg = "Incoming message protocol does not match endpoint protocol.";
            return Utils.createFaultMessage(mc, msg);
        }
    }
    
    public static MessageContext createFaultMessage(MessageContext mc, String msg) {
        try {
            XMLFault xmlfault =
                    new XMLFault(XMLFaultCode.VERSIONMISMATCH, new XMLFaultReason(msg));
            MessageFactory mf = (MessageFactory) FactoryRegistry.getFactory(MessageFactory.class);
            Message message = mf.create(Protocol.soap11);
            message.setXMLFault(xmlfault);
            
            MessageContext responseMsgCtx = MessageContextUtils.createFaultMessageContext(mc);
            responseMsgCtx.setMessage(message);
            return responseMsgCtx;
        } catch (XMLStreamException e) {
            // Need to fix this !   At least provide logging
            // TODO for now, throw it.  We probably should try to make an XMLFault object and set it on the message
            throw ExceptionFactory.makeWebServiceException(e);
        }
    }
    
    /*
     * Gets the OperationDescription associated with the request that is currently
     * being processed.
     * 
     *  Note that this is not done in the EndpointController since operations are only relevant
     *  to Endpoint-based implementation (i.e. not to Proxy-based ones)s
     */
    public static OperationDescription getOperationDescription(MessageContext mc) {
        OperationDescription op = null;
        
        op = mc.getOperationDescription();
        if (op == null) {
            if (log.isDebugEnabled()) {
                log.debug("No OperationDescription found on MessageContext, searching existing operations");
            }

            EndpointDescription ed = mc.getEndpointDescription();
            EndpointInterfaceDescription eid = ed.getEndpointInterfaceDescription();

            OperationDescription[] ops = eid.getDispatchableOperation(mc.getOperationName());
            // TODO: Implement signature matching.  Currently only matching on the wsdl:OperationName is supported.
            //       That means that overloading of wsdl operations is not supported (although that's not supported in 
            //       WSDL 1.1 anyway).
            if (ops == null || ops.length == 0) {
                throw ExceptionFactory.makeWebServiceException(
                		Messages.getMessage("oprDescrErr",mc.getOperationName().toString()));
            }
            if (ops.length > 1) {
                throw ExceptionFactory.makeWebServiceException(
                		Messages.getMessage("oprDescrErr1",mc.getOperationName().toString()));
            }
            op = ops[0];
            if (log.isDebugEnabled()) {
                log.debug("wsdl operation: " + op.getName());
                log.debug("   java method: " + op.getJavaMethodName());
            }
        }
        else {
            if (log.isDebugEnabled()) {
                log.debug("An existing OperationDescription was found on the MessageContext.");
            }
        }
        
        return op;
    }

}
