/*
 * JBoss, Home of Professional Open Source.
 * Copyright 2010, Red Hat Middleware LLC, and individual contributors
 * as indicated by the @author tags. See the copyright.txt file in the
 * distribution for a full listing of individual contributors.
 *
 * This 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 (at your option) any later version.
 *
 * This software 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 software; if not, write to the Free
 * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
 * 02110-1301 USA, or see the FSF site: http://www.fsf.org.
 */
package org.jboss.test.ws.jaxws.jbws2991;

import java.util.Iterator;

import javax.xml.soap.SOAPException;
import javax.xml.soap.SOAPHeader;
import javax.xml.soap.SOAPMessage;
import javax.xml.soap.Text;
import javax.xml.ws.WebServiceException;
import javax.xml.ws.handler.soap.SOAPMessageContext;

import org.jboss.logging.Logger;
import org.jboss.test.ws.jaxws.common.ActionNotSupportedException;
import org.jboss.test.ws.jaxws.common.AddressingPropertyException;
import org.jboss.test.ws.jaxws.common.Handler_Util;
import org.jboss.test.ws.jaxws.common.SOAPConstants;
import org.jboss.test.ws.jaxws.common.W3CAddressingConstants;
import org.jboss.test.ws.jaxws.common.WsaBaseSOAPHandler;
import org.jboss.test.ws.jaxws.jbws2634.shared.handlers.TestHandler;
import org.jboss.test.ws.jaxws.jbws2991.common.TestConstants;
import org.w3c.dom.Element;
import org.w3c.dom.NamedNodeMap;
import org.w3c.dom.Node;
import org.w3c.dom.NodeList;

public class ClientSOAPHandler extends WsaBaseSOAPHandler
{
   private static final Logger log = Logger.getLogger(TestHandler.class);

   String testName = null;

   protected void checkInboundAction(SOAPMessageContext context, String oper, String action)
   {
      log.debug("ClientSOAPHandler.checkInboundAction: [operation=" + oper + ", input action=" + action + "]");
      //figure out which testname i'm checking for and call appropropriate verify*(...)
      if (Handler_Util.checkForMsg(context, "ClientEnabledNotREQServerEnabledNotREQ"))
      {
         verifyClientEnabledNotREQServerEnabledNotREQ(context, action);
      }
      else if (Handler_Util.checkForMsg(context, "ClientEnabledNotREQServerEnabledREQ"))
      {
         verifyClientEnabledNotREQServerEnabledREQ(context, action);
      }
      else if (Handler_Util.checkForMsg(context, "ClientEnabledNotREQServerUsingDefaults"))
      {
         verifyClientEnabledNotREQServerUsingDefaults(context, action);
      }
      else if (Handler_Util.checkForMsg(context, "ClientEnabledNotREQServerNotEnabled"))
      {
         verifyClientEnabledNotREQServerNotEnabled(context, action);
      }
      else if (Handler_Util.checkForMsg(context, "ClientNotEnabledServerEnabledNotREQ"))
      {
         verifyClientNotEnabledServerEnabledNotREQ(context, action);
         //} else if (Handler_Util.checkForMsg(context,"ClientNotEnabledServerEnabledREQ")){
         //verifyClientNotEnabledServerEnabledREQ(context,action);
         //} else if (Handler_Util.checkForMsg(context,"ClientEnabledREQServerNotEnabled")){
         //verifyClientEnabledREQServerNotEnabled(context,action);
      }
   }

   private void verifyClientEnabledNotREQServerEnabledNotREQ(SOAPMessageContext context, String action)
   {
      //verify specific headers for ClientEnabledNotREQServerEnabledNotREQ here...
      checkAddressingHeadersExist(context, action);
   }

   private void verifyClientEnabledNotREQServerUsingDefaults(SOAPMessageContext context, String action)
   {
      //verify specific headers for ClientEnabledNotREQServerUsingDefaults here...
      checkAddressingHeadersExist(context, action);
   }

   private void verifyClientNotEnabledServerEnabledNotREQ(SOAPMessageContext context, String action)
   {
      //verify specific headers for ClientNotEnabledServerEnabledNotREQ here...
      checkAddressingHeadersDoNotExist(context, action);
   }

   private void verifyClientEnabledNotREQServerEnabledREQ(SOAPMessageContext context, String action)
   {
      //verify specific headers for ClientEnabledNotREQServerEnabledREQ here...
      checkAddressingHeadersExist(context, action);
   }

   private void verifyClientNotEnabledServerEnabledREQ(SOAPMessageContext context, String action)
   {
      //verify specific headers for ClientNotEnabledServerEnabledREQ here...
      checkAddressingHeadersDoNotExist(context, action);
   }

   private void verifyClientEnabledNotREQServerNotEnabled(SOAPMessageContext context, String action)
   {
      //verify specific headers for ClientEnabledNotREQServerNotEnabled here...
      checkAddressingHeadersDoNotExist(context, action);
   }

   private void verifyClientEnabledREQServerNotEnabled(SOAPMessageContext context, String action)
   {
      //verify specific headers for ClientEnabledREQServerNotEnabled here...
      checkAddressingHeadersDoNotExist(context, action);
   }

   private void verifyAction(String action)
   {
      log.debug("ClientSOAPHandler.verifyAction: [action=" + action + "]");
      if (!TestConstants.ADD_NUMBERS_OUT_ACTION.equals(action))
      {
         throw new ActionNotSupportedException("Expected:" + TestConstants.ADD_NUMBERS_OUT_ACTION + ", Actual:"
               + action);
      }
   }

   private void checkOptionalAddressingHeadersForMustUnderstandAttributeExist(SOAPMessageContext context)
         throws SOAPException
   {
      String mustUnderstandAttributes = null;
      if (_getToHasMustUnderstandAttribute(context))
      {
         mustUnderstandAttributes = mustUnderstandAttributes + "wsa:To, ";
      }
      if (_getReplyToHasMustUnderstandAttribute(context))
      {
         mustUnderstandAttributes = mustUnderstandAttributes + "wsa:ReplyTo, ";
      }
      if (_getMessageIdHasMustUnderstandAttribute(context))
      {
         mustUnderstandAttributes = mustUnderstandAttributes + "wsa:MessageID, ";
      }
      if (_getRelationshipHasMustUnderstandAttribute(context))
      {
         mustUnderstandAttributes = mustUnderstandAttributes + "wsa:Relationship, ";
      }
      if (_getRelatesToHasMustUnderstandAttribute(context))
      {
         mustUnderstandAttributes = mustUnderstandAttributes + "wsa:RelatesTo, ";
      }
      if (_getActionHasMustUnderstandAttribute(context))
      {
         mustUnderstandAttributes = mustUnderstandAttributes + "wsa:Action ";
      }
      if (mustUnderstandAttributes != null)
      {
         throw new AddressingPropertyException(
               "If using SOAP, MAP headers MUST NOT have a soap:mustUnderstand attribute with a value of true. \n The following optional addressing headers contain invalid mustUnderstand attribute with a value of true ["
                     + mustUnderstandAttributes + "]");
      }
   }

   private void checkAddressingHeadersExist(SOAPMessageContext context, String action)
   {
      log.debug("ClientSOAPHandler.checkAddressingHeadersExist");
      verifyAction(action);
      checkInboundToExist(context);
      checkInboundRelatesToExist(context);
   }

   private void checkAddressingHeadersDoNotExist(SOAPMessageContext context, String action)
   {
      log.debug("ClientSOAPHandler.checkAddressingHeadersDoNotExist");
      checkActionDoesNotExist(action);
      checkInboundToDoesNotExist(context);
      checkInboundRelatesToDoesNotExist(context);
   }

   private void handleMessageInboundCheckAddressingHeadersDoNotExist(SOAPMessageContext context)
   {
      String headerValue = null;
      String whichHeaders = null;
      try
      {
         headerValue = getTo(context);
         whichHeaders = whichHeaders + "wsa:To, ";
      }
      catch (Exception e)
      {
      }
      try
      {
         headerValue = getReplyTo(context);
         whichHeaders = whichHeaders + "wsa:ReplyTo, ";
      }
      catch (Exception e)
      {
      }
      try
      {
         headerValue = getMessageId(context);
         whichHeaders = whichHeaders + "wsa:MessageId, ";
      }
      catch (Exception e)
      {
      }
      try
      {
         headerValue = getRelationship(context);
         whichHeaders = whichHeaders + "wsa:Relationship, ";
      }
      catch (Exception e)
      {
      }
      try
      {
         headerValue = getAction(context);
         whichHeaders = whichHeaders + "wsa:Action";
      }
      catch (Exception e)
      {
      }
      if (whichHeaders != null)
      {
         throw new AddressingPropertyException("ERROR: The following headers exist in soap message: [" + whichHeaders
               + "]");
      }
   }

   protected void checkInboundToExist(SOAPMessageContext context)
   {
      String to = null;
      try
      {
         to = getTo(context);
         if (to != null)
         {
            log.debug("ClientSOAPHandler.checkInboundToExist: [To=" + to + "]");
            if (W3CAddressingConstants.WSA_ANONYMOUS_ADDRESS_URI.equals(to))
            {
               log.debug("wsa:To matches anonymous uri: " + W3CAddressingConstants.WSA_ANONYMOUS_ADDRESS_URI);
            }
         }
      }
      catch (Exception e)
      {
         throw new WebServiceException(e);
      }
      if (to == null)
      {
         throw new AddressingPropertyException("wsa:To contains null");
      }
      if (to.equals("invalid"))
      {
         throw new AddressingPropertyException("wsa:To contains invalid value");
      }
      if (to.equals(""))
      {
         throw new AddressingPropertyException("wsa:To contains an empty value");
      }
   }

   private boolean nodeHasMustUnderstandAttribute(Node node)
   {
      boolean containsMustUnderstandAttribute = false;
      try
      {
         if (node.hasAttributes())
         {
            //A NamedNodeMap containing attributes of this node
            NamedNodeMap attributes = node.getAttributes();
            if (attributes != null)
            {
               // see if it has an mustUnderstand attribute and fail if you find one
               Node mustUnderstandNode = attributes.getNamedItem(SOAPConstants.SOAP_MUST_UNDERSTAND_ATTR);
               if (mustUnderstandNode != null)
               {
                  // is the value actually set to true, if so then fail
                  if ("true".equals(mustUnderstandNode.getNodeValue()))
                  {
                     containsMustUnderstandAttribute = true;
                  }
               }
            }
         }
      }
      catch (Exception e)
      {
         log.debug("ClientSOAPHandler.nodeHasMustUnderstandAttribute: caught Exception [" + e.getMessage()
               + "], setting Node has mustUnderstand attribute boolean value to false");
      }
      return containsMustUnderstandAttribute;
   }

   private SOAPHeader getSOAPHeaderFromContext(SOAPMessageContext context) throws SOAPException
   {
      SOAPMessage message = context.getMessage();
      SOAPHeader header = message.getSOAPHeader();
      return header;
   }

   private boolean _getToHasMustUnderstandAttribute(SOAPMessageContext context) throws SOAPException
   {
      SOAPHeader header = getSOAPHeaderFromContext(context);
      Iterator iter = header.getChildElements(W3CAddressingConstants.WSA_TO_QNAME);
      if (!iter.hasNext())
      {
         return false;
      }
      Node node = (Node) iter.next();
      Node toNode = node.getFirstChild();
      return nodeHasMustUnderstandAttribute(toNode);
   }

   private boolean _getReplyToHasMustUnderstandAttribute(SOAPMessageContext context) throws SOAPException
   {
      boolean containsMustUnderstandAttribute = false;
      SOAPHeader header = getSOAPHeaderFromContext(context);
      Iterator iter = header.getChildElements(W3CAddressingConstants.WSA_REPLYTO_QNAME);
      if (!iter.hasNext())
      {
         return false;
      }
      NodeList nodes = ((Element) iter.next()).getChildNodes();
      for (int i = 0; i < nodes.getLength(); i++)
      {
         Node node = (Node) nodes.item(i);
         if (node instanceof Text)
         {
            continue;
         }
         if (node.getLocalName().equals("Address")
               && node.getNamespaceURI().equals(W3CAddressingConstants.WSA_NAMESPACE_NAME))
         {
            Node replyToNode = node.getFirstChild();
            return nodeHasMustUnderstandAttribute(replyToNode);
         }
      }
      return containsMustUnderstandAttribute;
   }

   private boolean _getMessageIdHasMustUnderstandAttribute(SOAPMessageContext context) throws SOAPException
   {
      boolean containsMustUnderstandAttribute = false;
      SOAPHeader header = getSOAPHeaderFromContext(context);
      Iterator iter = header.getChildElements(W3CAddressingConstants.WSA_MESSAGEID_QNAME);
      if (!iter.hasNext())
      {
         return containsMustUnderstandAttribute;
      }
      Node node = (Node) iter.next();
      Node messageIdNode = node.getFirstChild();
      return nodeHasMustUnderstandAttribute(messageIdNode);
   }

   private boolean _getRelationshipHasMustUnderstandAttribute(SOAPMessageContext context) throws SOAPException
   {
      boolean containsMustUnderstandAttribute = false;
      SOAPHeader header = getSOAPHeaderFromContext(context);
      Iterator iter = header.getChildElements(W3CAddressingConstants.WSA_RELATIONSHIPTYPE_QNAME);
      if (!iter.hasNext())
      {
         return containsMustUnderstandAttribute;
      }
      Node node = (Node) iter.next();
      Node relationshipNode = node.getFirstChild();
      return nodeHasMustUnderstandAttribute(relationshipNode);
   }

   private boolean _getRelatesToHasMustUnderstandAttribute(SOAPMessageContext context) throws SOAPException
   {
      boolean containsMustUnderstandAttribute = false;
      SOAPHeader header = getSOAPHeaderFromContext(context);
      Iterator iter = header.getChildElements(W3CAddressingConstants.WSA_RELATESTO_QNAME);
      if (!iter.hasNext())
      {
         return containsMustUnderstandAttribute;
      }
      Node node = (Node) iter.next();
      Node relatesToNode = node.getFirstChild();
      return nodeHasMustUnderstandAttribute(relatesToNode);
   }

   private boolean _getActionHasMustUnderstandAttribute(SOAPMessageContext context) throws SOAPException
   {
      boolean containsMustUnderstandAttribute = false;
      SOAPHeader header = getSOAPHeaderFromContext(context);
      Iterator iter = header.getChildElements(W3CAddressingConstants.WSA_ACTION_QNAME);
      if (!iter.hasNext())
      {
         return containsMustUnderstandAttribute;
      }
      Node node = (Node) iter.next();
      Node actionNode = node.getFirstChild();
      return nodeHasMustUnderstandAttribute(actionNode);
   }

   protected String getAction(SOAPMessageContext context) throws SOAPException
   {
      testName = (String) context.get("test.name");
      log.debug("ClientSOAPHandler.getAction(): testName=" + testName);
      if (testName == null)
         return super.getAction(context);
      /* Headers MUST be present on SOAPResponse */
      else if (testName.equals("ClientEnabledNotREQServerEnabledNotREQ")
            || testName.equals("ClientEnabledNotREQServerEnabledREQ")
            || testName.equals("ClientEnabledNotREQServerUsingDefaults"))
         return super.getAction(context);
      /* Headers MUST NOT be present on SOAPResponse */
      else if (testName.equals("ClientEnabledNotREQServerNotEnabled")
            || testName.equals("ClientNotEnabledServerEnabledNotREQ"))
         return super.getActionDoesNotExist(context);
      /* Fault case just return null */
      else
         return null;
   }

   protected String whichHandler()
   {
      return "ClientSOAPHandler";
   }
}
