/*
 * JBoss, the OpenSource J2EE webOS
 *
 * Distributable under LGPL license.
 * See terms of license at gnu.org.
 */
package org.jboss.jaxr.juddi.axis;

/*import org.apache.axis.AxisFault;
import org.apache.axis.Message;
import org.apache.axis.client.Call;
import org.apache.axis.client.Service;
import org.apache.axis.message.SOAPBodyElementAxisImpl;
import org.apache.axis.utils.XMLUtils;  */

import org.jboss.axis.AxisFault;
import org.jboss.axis.Message;
import org.jboss.axis.client.Call;
import org.jboss.axis.client.Service;
import org.jboss.axis.message.SOAPBodyElementAxisImpl;
import org.jboss.axis.utils.XMLUtils;


import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.apache.juddi.error.RegistryException;
import org.apache.juddi.proxy.Transport;
import org.w3c.dom.Document;
import org.w3c.dom.Element;
import org.xml.sax.SAXException;

import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
import javax.xml.parsers.ParserConfigurationException;
import javax.xml.transform.TransformerFactory;
import javax.xml.transform.Transformer;
import javax.xml.transform.Result;
import javax.xml.transform.dom.DOMSource;
import javax.xml.transform.stream.StreamResult;
import java.io.ByteArrayInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.io.ByteArrayOutputStream;
import java.net.URL;
import java.util.Vector;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

/**
 * Overrides the AxisTransport class used by jUDDI
 *
 * @author <mailto:Anil.Saldhana@jboss.org>Anil Saldhana
 * @since Nov 20, 2004
 */
public class JBossJuddiAxisTransport implements Transport
{
   // private reference to the jUDDI logger
   private static Log log = LogFactory.getLog(JBossJuddiAxisTransport.class);

   public Element send(Element request, URL endpointURL)
           throws RegistryException
   {
      return this.send(request, endpointURL, false, null, 0);
   }

   public Element send(Element request, URL endpointURL, boolean proxySet, String proxyHost, int proxyPort)
           throws RegistryException
   {
      // make sure we're using the correct proxy
      if (proxySet)
      {
         System.setProperty("http.proxySet", String.valueOf(proxySet));
         System.setProperty("http.proxyHost", proxyHost);
         System.setProperty("http.proxyPort", String.valueOf(proxyPort));
      }

      Service service = null;
      Call call = null;
      Element response = null;

      log.debug("\nRequest message:\n" + XMLUtils.ElementToString(request));

      try
      {
         service = new Service();
         call = (Call)service.createCall();
         call.setTargetEndpointAddress(endpointURL);

         String requestString = XMLUtils.ElementToString(request);
         /** Hack for Jboss axis-ws4ee  */
         requestString = updateNS(requestString);
         SOAPBodyElementAxisImpl body =
                 new SOAPBodyElementAxisImpl(new ByteArrayInputStream(requestString.getBytes("UTF-8")));
         Object[] soapBodies = new Object[]{body};

         SOAPBodyElementAxisImpl res = null;
         Vector result = null;
         Object r = call.invoke(soapBodies);
         if(r instanceof Vector)
             result = (Vector)r;
         else
         if( r instanceof SOAPBodyElementAxisImpl)
           res = (SOAPBodyElementAxisImpl) r;
         // response = ((SOAPBodyElementAxisImpl) result.elementAt(0)).getAsDOM();


         //Need to Hack again for namespaces for JBoss axis-wsee
         if(result!=null) res= (SOAPBodyElementAxisImpl)result.elementAt(0);
         String retstr = res.getAsString();
         retstr = removeNS(retstr);
         retstr = updateNS(retstr);
         //System.out.println(retstr);
         SOAPBodyElementAxisImpl updatedbody =
                 new SOAPBodyElementAxisImpl(new ByteArrayInputStream(retstr.getBytes("UTF-8")));
         response = updatedbody.getAsDOM();
         //System.out.println(response.getNodeName());

         //if(res!=null)
           //System.out.println(res.getAsString());
         //response = res.getAsDOM();
      }
      catch (AxisFault fault)
      {

         fault.printStackTrace();

         try
         {
            Message msg = call.getResponseMessage();
            response = msg.getSOAPEnvelope().getFirstBody().getAsDOM();
         }
         catch (Exception ex)
         {
            throw new RegistryException(ex);
         }
      }
      catch (Exception ex)
      {
         ex.printStackTrace();
         throw new RegistryException(ex);
      }

      log.debug("\nResponse message:\n" + XMLUtils.ElementToString(response));


      return response;
   }

   public String send(String request, URL endpointURL)
           throws RegistryException
   {
      Element response = null;
      try
      {
         response = this.send(parse(request), endpointURL);
      }
      catch (Exception ex)
      {
         ex.printStackTrace();
         throw new RegistryException(ex);
      }

      log.debug("\nResponse message:\n" + response);

      return toString(response);
   }

   /**
    * A Hack for JBoss axis-ws4ee
    *
    * @param str
    * @return
    */
   private String updateNS(String str)
   {
      str = replaceElements(str);
      str = replaceJustElements(str);

      return str;
   }

   /**
    * Update namespace for elements only (no attribs)
    *
    * @param str
    * @return
    */
   private String replaceJustElements(String str)
   {
      Pattern pIn = Pattern.compile("<[a-zA-Z]*>");
      Matcher matcher = pIn.matcher(str);

      // Replace all occurrences of pattern in input
      StringBuffer buf = new StringBuffer();
      boolean found = false;
      while ((found = matcher.find()))
      {
         // Get the match result
         String replaceStr = matcher.group();
         replaceStr = replaceStr.substring(0, replaceStr.length() - 1); //Chop off last char

         // prepare replacement string
         replaceStr = replaceStr + " xmlns=\"urn:uddi-org:api_v2\"> ";

         // Insert replacement
         matcher.appendReplacement(buf, replaceStr);
      }
      matcher.appendTail(buf);

      return buf.toString();

   }

   /**
    * Update namespace for elements with attribs
    *
    * @param str
    * @return
    */
   private String replaceElements(String str)
   {
      Pattern pIn = Pattern.compile("<[a-zA-Z]* ");
      Matcher matcher = pIn.matcher(str);

      // Replace all occurrences of pattern in input
      StringBuffer buf = new StringBuffer();
      boolean found = false;
      while ((found = matcher.find()))
      {
         // Get the match result
         String replaceStr = matcher.group();

         // prepare replacement string
         replaceStr = replaceStr + " xmlns=\"urn:uddi-org:api_v2\"  ";

         // Insert replacement
         matcher.appendReplacement(buf, replaceStr);
      }
      matcher.appendTail(buf);

      return buf.toString();

   }

   /**
    * Axis 1.1 seems to have a bug wherein it sets the namespace
    * xmlns:xmlns and the parser will complain if present.
    *
    * @param str
    * @return
    */
   private String removeNS(String str)
   {
      String ns = "xmlns=\"urn:uddi-org:api_v2\"";
      String ns1 = "xmlns:xmlns=\"http://www.w3.org/2000/xmlns/\"";
      str = str.replaceAll(ns, " ");
      str = str.replaceAll(ns1, " ");
      return str;
   }


   /** Parse the given XML string and return the root Element
    */
   private Element parse(String xmlString) throws IOException
   {
      try
      {
         return parse(new ByteArrayInputStream(xmlString.getBytes()));
      }
      catch (IOException e)
      {
         log.error("Cannot parse: " + xmlString);
         throw e;
      }
   }

   DocumentBuilder builder = getDocumentBuilder();

   /** Initialise the the DocumentBuilder
    */
   private DocumentBuilder getDocumentBuilder()
   {
      if (builder == null)
      {
         try
         {
            DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
            factory.setValidating(false);
            factory.setNamespaceAware(true);
            builder = factory.newDocumentBuilder();
         }
         catch (ParserConfigurationException e)
         {
            log.error(e);
         }
      }
      return builder;
   }

   /** Parse the given XML stream and return the root Element
    */
   private Element parse(InputStream xmlStream) throws IOException
   {
      try
      {
         Document doc = builder.parse(xmlStream);
         Element root = doc.getDocumentElement();
         return root;
      }
      catch (SAXException e)
      {
         throw new IOException(e.toString());
      }
   }

   private void writeXML(Element element,OutputStream stream)
  {
    try {
      TransformerFactory xformerFactory = TransformerFactory.newInstance();
      Transformer xformer = xformerFactory.newTransformer();
      Result output = new StreamResult(stream);
      DOMSource source = new DOMSource(element);

      // print the xml to the specified OutputStream
      xformer.transform(source,output);
    }
    catch(Exception ex) {
      ex.printStackTrace();
    }
  }

  private String toString(Element element)
  {
      ByteArrayOutputStream stream = new ByteArrayOutputStream();
      writeXML(element,stream);

      return stream.toString();
  }
}