/*
 * 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.common;

import java.util.Iterator;
import java.util.Set;

import javax.xml.ws.WebServiceException;
import javax.xml.ws.handler.soap.SOAPMessageContext;
import javax.xml.ws.handler.soap.SOAPHandler;
import javax.xml.ws.handler.MessageContext;
import javax.xml.soap.SOAPMessage;
import javax.xml.soap.SOAPHeader;
import javax.xml.soap.SOAPBody;
import javax.xml.soap.SOAPElement;
import javax.xml.soap.SOAPException;
import javax.xml.soap.Text;
import javax.xml.namespace.QName;

import org.jboss.logging.Logger;
import org.w3c.dom.Node;
import org.w3c.dom.NodeList;
import org.w3c.dom.Element;

public class WsaBaseSOAPHandler implements SOAPHandler<SOAPMessageContext>
{
   private static final Logger log = Logger.getLogger(WsaBaseSOAPHandler.class);

   public WsaBaseSOAPHandler()
   {
   }

   public boolean handleMessage(SOAPMessageContext context)
   {
      boolean outbound = (Boolean) context.get(MessageContext.MESSAGE_OUTBOUND_PROPERTY);
      if (outbound)
      {
         log.debug("Direction=outbound");
         if (whichHandler().equals("ServerSOAPHandler"))
            System.out.println("Direction=outbound");
      }
      else
      {
         log.debug("Direction=inbound");
         if (whichHandler().equals("ServerSOAPHandler"))
            System.out.println("Direction=inbound");
      }
      try
      {
         SOAPMessage msg = ((SOAPMessageContext) context).getMessage();
         JAXWS_Util.dumpSOAPMessage(msg, true);
         if (whichHandler().equals("ServerSOAPHandler"))
            JAXWS_Util.dumpSOAPMessage(msg, false);

         SOAPBody soapBody = getSOAPBody(context);
         String oper = getOperationName(soapBody);
         String testName = getTestName(soapBody);
         context.put("op.name", oper);
         context.put("test.name", testName);
         if (!outbound)
         {
            checkInboundAction(context, oper, getAction(context));
            checkInboundTo(context);
            checkInboundReplyTo(context);
            checkInboundMessageId(context);
            checkInboundRelationship(context);
            checkInboundRelatesTo(context);
            processInboundMessage(context, oper, testName);
         }
         else
         {
            processOutboundMessage(context, oper, testName);
         }
      }
      catch (SOAPException e)
      {
         e.printStackTrace();
      }

      return true;
   }

   public boolean handleFault(SOAPMessageContext context)
   {
      boolean outbound = (Boolean) context.get(MessageContext.MESSAGE_OUTBOUND_PROPERTY);
      if (outbound)
      {
         log.debug("Direction=outbound");
         if (whichHandler().equals("ServerSOAPHandler"))
            System.out.println("Direction=outbound");
      }
      else
      {
         log.debug("Direction=inbound");
         if (whichHandler().equals("ServerSOAPHandler"))
            System.out.println("Direction=inbound");
      }
      if (!outbound)
      {
         try
         {
            SOAPMessage msg = ((SOAPMessageContext) context).getMessage();
            JAXWS_Util.dumpSOAPMessage(msg, true);
            if (whichHandler().equals("ServerSOAPHandler"))
               JAXWS_Util.dumpSOAPMessage(msg, false);
            if (context.getMessage().getSOAPBody().getFault() != null)
            {
               String detailName = null;
               try
               {
                  detailName = context.getMessage().getSOAPBody().getFault().getDetail().getFirstChild().getLocalName();
               }
               catch (Exception e)
               {
               }
               checkFaultActions((String) context.get("op.name"), detailName, getAction(context));
            }
         }
         catch (SOAPException e)
         {
            e.printStackTrace();
         }
      }
      return true;
   }

   public Set<QName> getHeaders()
   {
      return null;
   }

   public void close(MessageContext messageContext)
   {
   }

   protected SOAPBody getSOAPBody(SOAPMessageContext context) throws SOAPException
   {
      SOAPBody soapBody = context.getMessage().getSOAPBody();
      return soapBody;
   }

   protected String getTo(SOAPMessageContext context) throws SOAPException
   {
      String to = null;
      SOAPMessage message = context.getMessage();
      SOAPHeader header = message.getSOAPHeader();
      Iterator iter = header.getChildElements(W3CAddressingConstants.WSA_TO_QNAME);
      if (iter.hasNext())
      {
         to = "invalid";
         NodeList nodes = ((Element) iter.next()).getChildNodes();
         for (int i = 0; i < nodes.getLength(); i++)
         {
            Node node = (Node) nodes.item(i);
            return node.getNodeValue();
         }
      }
      else
      {
         throw new AddressingHeaderException("WsaBaseSOAPHandler:getTo: Element not found:"
               + W3CAddressingConstants.WSA_TO_QNAME);
      }
      return to;
   }

   protected String getReplyTo(SOAPMessageContext context) throws SOAPException
   {
      String replyTo = null;
      SOAPMessage message = context.getMessage();
      SOAPHeader header = message.getSOAPHeader();
      Iterator iter = header.getChildElements(W3CAddressingConstants.WSA_REPLYTO_QNAME);
      if (iter.hasNext())
      {
         replyTo = "invalid";
         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))
            {
               return node.getFirstChild().getNodeValue();
            }
         }
      }
      else
      {
         throw new AddressingHeaderException("WsaBaseSOAPHandler:getReplyTo: Element not found:"
               + W3CAddressingConstants.WSA_REPLYTO_QNAME);
      }
      return replyTo;
   }

   protected String getRequiredMessageId(SOAPMessageContext context) throws SOAPException
   {
      SOAPMessage message = context.getMessage();
      SOAPHeader header = message.getSOAPHeader();
      Iterator iter = header.getChildElements(W3CAddressingConstants.WSA_MESSAGEID_QNAME);
      if (!iter.hasNext())
      {
         throw new AddressingHeaderException("WsaBaseSOAPHandler:getRequiredMessageId: Element not found:"
               + W3CAddressingConstants.WSA_MESSAGEID_QNAME);
      }
      Node node = (Node) iter.next();
      String msgid = node.getFirstChild().getNodeValue();
      return msgid;
   }

   protected String getMessageId(SOAPMessageContext context) throws SOAPException
   {
      SOAPMessage message = context.getMessage();
      SOAPHeader header = message.getSOAPHeader();
      Iterator iter = header.getChildElements(W3CAddressingConstants.WSA_MESSAGEID_QNAME);
      if (!iter.hasNext())
      {
         throw new AddressingHeaderException("WsaBaseSOAPHandler:getMessageId: Element not found:"
               + W3CAddressingConstants.WSA_MESSAGEID_QNAME);
      }
      Node node = (Node) iter.next();
      String msgid = node.getFirstChild().getNodeValue();
      return msgid;
   }

   protected String getRequiredRelationship(SOAPMessageContext context) throws SOAPException
   {
      SOAPMessage message = context.getMessage();
      SOAPHeader header = message.getSOAPHeader();
      Iterator iter = header.getChildElements(W3CAddressingConstants.WSA_RELATIONSHIPTYPE_QNAME);
      if (!iter.hasNext())
      {
         throw new AddressingHeaderException("WsaBaseSOAPHandler:getRequiredRelationship: Element not found:"
               + W3CAddressingConstants.WSA_RELATIONSHIPTYPE_QNAME);
      }
      Node node = (Node) iter.next();
      String relationship = node.getFirstChild().getNodeValue();
      return relationship;
   }

   protected String getRelationship(SOAPMessageContext context) throws SOAPException
   {
      SOAPMessage message = context.getMessage();
      SOAPHeader header = message.getSOAPHeader();
      Iterator iter = header.getChildElements(W3CAddressingConstants.WSA_RELATIONSHIPTYPE_QNAME);
      if (!iter.hasNext())
      {
         throw new AddressingHeaderException("WsaBaseSOAPHandler:getRelationship: Element not found:"
               + W3CAddressingConstants.WSA_RELATIONSHIPTYPE_QNAME);
      }
      Node node = (Node) iter.next();
      String relationship = node.getFirstChild().getNodeValue();
      return relationship;
   }

   protected String getRequiredRelatesTo(SOAPMessageContext context) throws SOAPException
   {
      SOAPMessage message = context.getMessage();
      SOAPHeader header = message.getSOAPHeader();
      Iterator iter = header.getChildElements(W3CAddressingConstants.WSA_RELATESTO_QNAME);
      if (!iter.hasNext())
      {
         throw new AddressingHeaderException("WsaBaseSOAPHandler:getRequiredRelatesTo: Element not found:"
               + W3CAddressingConstants.WSA_RELATESTO_QNAME);
      }
      Node node = (Node) iter.next();
      String relationship = node.getFirstChild().getNodeValue();
      return relationship;
   }

   protected String getRelatesTo(SOAPMessageContext context) throws SOAPException
   {
      SOAPMessage message = context.getMessage();
      SOAPHeader header = message.getSOAPHeader();
      Iterator iter = header.getChildElements(W3CAddressingConstants.WSA_RELATESTO_QNAME);
      if (!iter.hasNext())
      {
         throw new AddressingHeaderException("WsaBaseSOAPHandler:getRelatesTo: Element not found:"
               + W3CAddressingConstants.WSA_RELATESTO_QNAME);
      }
      Node node = (Node) iter.next();
      String relationship = node.getFirstChild().getNodeValue();
      return relationship;
   }

   protected String getActionDoesNotExist(SOAPMessageContext context) throws SOAPException
   {
      SOAPMessage message = context.getMessage();
      SOAPHeader header = message.getSOAPHeader();
      Iterator iter = header.getChildElements(W3CAddressingConstants.WSA_ACTION_QNAME);
      if (!iter.hasNext())
      {
         return null;
      }
      Node node = (Node) iter.next();
      String action = node.getFirstChild().getNodeValue();
      return action;
   }

   protected String getAction(SOAPMessageContext context) throws SOAPException
   {
      String action = null;
      SOAPMessage message = context.getMessage();
      SOAPHeader header = message.getSOAPHeader();
      Iterator iter = header.getChildElements(W3CAddressingConstants.WSA_ACTION_QNAME);
      if (!iter.hasNext())
      {
         throw new AddressingHeaderException("WsaBaseSOAPHandler:getAction: Element not found:"
               + W3CAddressingConstants.WSA_ACTION_QNAME);
      }
      Node node = (Node) iter.next();
      action = node.getFirstChild().getNodeValue();
      return action;
   }

   protected String getOperationName(SOAPBody soapBody) throws SOAPException
   {
      return soapBody.getFirstChild().getLocalName();
   }

   protected String getTestName(SOAPBody soapbody) throws SOAPException
   {
      String testName = null;
      SOAPElement se = null;
      Iterator i = soapbody.getChildElements();
      if (i.hasNext())
      {
         se = (SOAPElement) i.next();
      }
      i = se.getChildElements();
      while (i.hasNext())
      {
         se = (SOAPElement) i.next();
         String elementName = se.getElementName().getLocalName();
         Node node = (Node) se;
         String elementValue = node.getFirstChild().getNodeValue();
         if (elementName.equals("testName"))
         {
            testName = elementValue;
            break;
         }
      }
      return testName;
   }

   protected void checkFaultActions(String requestName, String detailName, String action)
   {
   }

   protected void checkInboundAction(SOAPMessageContext context, String oper, String action)
   {
   };

   protected void checkInboundTo(SOAPMessageContext context)
   {
   }

   protected void checkInboundReplyTo(SOAPMessageContext context)
   {
   }

   protected void checkInboundMessageId(SOAPMessageContext context)
   {
   }

   protected void checkInboundRelationship(SOAPMessageContext context)
   {
   }

   protected void checkInboundRelatesTo(SOAPMessageContext context)
   {
   }

   protected void processOutboundMessage(SOAPMessageContext context, String oper, String testName)
   {
   }

   protected void processInboundMessage(SOAPMessageContext context, String oper, String testName)
   {
   }

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

   protected void checkInboundToExist(SOAPMessageContext context)
   {
      String to = null;
      try
      {
         to = getTo(context);
         log.debug(whichHandler() + ".checkInboundToExist: [To=" + to + "]");
         if (whichHandler().equals("ServerSOAPHandler"))
            System.out.println(whichHandler() + ".checkInboundToExist: [To=" + to + "]");
      }
      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 an invalid value");
      }
      if (to.equals(""))
      {
         throw new AddressingPropertyException("wsa:To contains an empty value");
      }
   }

   protected void checkInboundRelatesToExist(SOAPMessageContext context)
   {
      String r = null;
      try
      {
         r = getRelatesTo(context);
         log.debug(whichHandler() + ".checkInboundRelatesToExist: [RelatesTo=" + r + "]");
         if (whichHandler().equals("ServerSOAPHandler"))
            System.out.println(whichHandler() + ".checkInboundRelatesToExist: [RelatesTo=" + r + "]");
      }
      catch (Exception e)
      {
         throw new WebServiceException(e);
      }
      if (r == null)
      {
         throw new AddressingPropertyException("wsa:RelatesTo contains null");
      }
      if (r.equals(""))
      {
         throw new AddressingPropertyException("wsa:RelatesTo contains an empty value");
      }
   }

   protected void checkInboundMessageIdExist(SOAPMessageContext context)
   {
      String mid = null;
      try
      {
         mid = getMessageId(context);
         log.debug(whichHandler() + ".checkInboundMessageIdExist: [MessageId=" + mid + "]");
         if (whichHandler().equals("ServerSOAPHandler"))
            System.out.println(whichHandler() + ".checkInboundMessageIdExist: [MessageId=" + mid + "]");
      }
      catch (Exception e)
      {
         throw new WebServiceException(e);
      }
      if (mid == null)
      {
         throw new AddressingPropertyException("wsa:MessageId contains null");
      }
      if (mid.equals(""))
      {
         throw new AddressingPropertyException("wsa:MessageId contains an empty value");
      }
   }

   protected void checkInboundReplyToExist(SOAPMessageContext context)
   {
      String to = null;
      try
      {
         to = getReplyTo(context);
         log.debug(whichHandler() + ".checkInboundReplyToExist: [ReplyTo=" + to + "]");
         if (whichHandler().equals("ServerSOAPHandler"))
            System.out.println(whichHandler() + ".checkInboundReplyToExist: [ReplyTo=" + to + "]");
      }
      catch (Exception e)
      {
         throw new WebServiceException(e);
      }
      if (to == null)
      {
         throw new AddressingPropertyException("wsa:ReplyTo contains null");
      }
      if (to.equals("invalid"))
      {
         throw new AddressingPropertyException("wsa:ReplyTo contains an invalid value");
      }
      if (to.equals(""))
      {
         throw new AddressingPropertyException("wsa:ReplyTo contains an empty value");
      }
   }

   protected void checkActionDoesNotExist(String action)
   {
      log.debug(whichHandler() + ".checkActionDoesNotExist");
      if (whichHandler().equals("ServerSOAPHandler"))
         System.out.println(whichHandler() + ".checkActionDoesNotExist");
      if (action != null)
      {
         throw new AddressingPropertyException("wsa:Action header exists (unexpected)");
      }
   }

   protected void checkInboundToDoesNotExist(SOAPMessageContext context)
   {
      log.debug(whichHandler() + ".checkInboundToDoesNotExist");
      if (whichHandler().equals("ServerSOAPHandler"))
         System.out.println(whichHandler() + ".checkInboundToDoesNotExist");
      String to = null;
      boolean pass = false;
      try
      {
         to = getTo(context);
      }
      catch (Exception e)
      {
         pass = true;
      }
      if (!pass)
         throw new AddressingPropertyException("wsa:To header exists (unexpected)");
   }

   protected void checkInboundRelatesToDoesNotExist(SOAPMessageContext context)
   {
      log.debug(whichHandler() + ".checkInboundRelatesToDoesNotExist");
      if (whichHandler().equals("ServerSOAPHandler"))
         System.out.println(whichHandler() + ".checkInboundRelatesToDoesNotExist");
      String r = null;
      boolean pass = false;
      try
      {
         r = getRelatesTo(context);
      }
      catch (Exception e)
      {
         pass = true;
      }
      if (!pass)
         throw new AddressingPropertyException("wsa:RelatesTo header exists (unexpected)");
   }

   protected void checkInboundMessageIdDoesNotExist(SOAPMessageContext context)
   {
      log.debug(whichHandler() + ".checkInboundMessageIdDoesNotExist");
      if (whichHandler().equals("ServerSOAPHandler"))
         System.out.println(whichHandler() + ".checkInboundMessageIdDoesNotExist");
      String mid = null;
      boolean pass = false;
      try
      {
         mid = getRequiredMessageId(context);
      }
      catch (Exception e)
      {
         pass = true;
      }
      if (!pass)
         throw new AddressingPropertyException("wsa:MessageId header exists (unexpected)");
   }

   protected void checkInboundReplyToDoesNotExist(SOAPMessageContext context)
   {
      log.debug(whichHandler() + ".checkInboundReplyToDoesNotExist");
      if (whichHandler().equals("ServerSOAPHandler"))
         System.out.println(whichHandler() + ".checkInboundReplyToDoesNotExist");
      String replyTo = null;
      boolean pass = false;
      try
      {
         replyTo = getReplyTo(context);
      }
      catch (Exception e)
      {
         pass = true;
      }
      if (!pass)
         throw new AddressingPropertyException("wsa:ReplyTo header exists (unexpected)");
   }

   protected void assertAction(final String currentOperation, final String currentAction,
         final String expectedOperation, final String expectedAction)
   {
      final boolean operationMatch = currentOperation.equals(expectedOperation);
      final boolean actionMatch = currentAction.equals(expectedAction);

      if (operationMatch && !actionMatch)
         throwActionNotSupportedException(expectedAction, currentAction);
   }

   protected void assertFaultAction(
         final String currentRequestName, final String currentDetailName, final String currentAction,
         final String expectedRequestName, final String expectedDetailName, final String expectedAction)
   {
      final boolean requestNameMatch = currentRequestName.equals(expectedRequestName);
      final boolean detailNameMatch = currentDetailName.equals(expectedDetailName);
      final boolean actionMatch = currentAction.equals(expectedAction);

      if (requestNameMatch && detailNameMatch && !actionMatch)
         throwActionNotSupportedException(expectedAction, currentAction);
   }

   protected void throwActionNotSupportedException(final String expected, final String actual)
   {
      throw new ActionNotSupportedException("Expected: " + expected + ", Actual: " + actual);
   }

}
