/*
 * Copyright (C) The Apache Software Foundation. All rights reserved.
 *
 * This software is published under the terms of the Apache Software License
 * version 1.1, a copy of which has been included with this distribution in
 * the docs/licenses/apache-1.1.txt file.
 */

package org.jboss.axis.attachments;


import org.jboss.axis.Part;
import org.jboss.axis.transport.http.HTTPConstants;
import org.jboss.axis.utils.Messages;
import org.jboss.logging.Logger;

import javax.activation.DataHandler;


/**
 *
 * @author Rick Rineholt 
 */

/**
 * This simulates the multipart stream
 */
public class MultiPartDimeInputStream extends MultiPartInputStream
{
   private static Logger log = Logger.getLogger(MultiPartDimeInputStream.class.getName());

   protected java.util.HashMap parts = new java.util.HashMap();
   protected java.util.LinkedList orderedParts = new java.util.LinkedList();
   protected int rootPartLength = 0;
   protected boolean closed = false; //If true the stream has been closed.
   protected boolean eos = false;  //This is set once the SOAP packet has reached the end of stream.
   //This stream controls and manages the  boundary.
   protected DimeDelimitedInputStream dimeDelimitedStream = null;
   protected java.io.InputStream soapStream = null; //Set the soap stream once found.
   protected byte[] boundary = null;
   protected java.io.ByteArrayInputStream cachedSOAPEnvelope = null; //Caches the soap stream if it is
   //Still open and a reference to read data in a later attachment occurs.
   protected String contentId = null;

   /**
    * Multipart stream.
    *
    * @param is the true input stream from where the source.
    */
   public MultiPartDimeInputStream(java.io.InputStream is)
           throws java.io.IOException
   {
      super(null); //don't cache this stream.
      soapStream = dimeDelimitedStream = new DimeDelimitedInputStream(is); //The Soap stream must always be first
      contentId = dimeDelimitedStream.getContentId();
   }

   public Part getAttachmentByReference(final String[] id)
           throws org.jboss.axis.AxisFault
   {
      //First see if we have read it in yet.
      Part ret = null;

      try
      {
         for (int i = id.length - 1; ret == null && i > -1; --i)
         {
            ret = (AttachmentPartImpl)parts.get(id[i]);
         }

         if (null == ret)
         {
            ret = readTillFound(id);
         }
         log.debug(Messages.getMessage("return02",
                 "getAttachmentByReference(\"" + id + "\"",
                 (ret == null ? "null" : ret.toString())));
      }
      catch (java.io.IOException e)
      {
         throw new org.jboss.axis.AxisFault(e.getClass().getName()
                 + e.getMessage());
      }
      return ret;
   }

   protected void addPart(String contentId, String locationId,
                          AttachmentPartImpl ap)
   {
      //For DIME streams Content-Location is ignored.
      if (contentId != null && contentId.trim().length() != 0)
         parts.put(contentId, ap);
      orderedParts.add(ap);
   }

   //Shouldn't never match
   protected static final String[] READ_ALL = {" * \0 ".intern()};

   protected void readAll() throws org.jboss.axis.AxisFault
   {
      try
      {
         readTillFound(READ_ALL);
      }
      catch (Exception e)
      {
         throw org.jboss.axis.AxisFault.makeFault(e);
      }
   }

   public java.util.Collection getAttachments()
           throws org.jboss.axis.AxisFault
   {
      readAll();
      return new java.util.LinkedList(orderedParts);
   }

   /**
    * This will read streams in till the one that is needed is found.
    *
    * @param id is the stream being sought.
    */

   protected Part readTillFound(final String[] id)
           throws java.io.IOException
   {
      if (dimeDelimitedStream == null)
      {
         //The whole stream has been consumed already
         return null;
      }
      Part ret = null;

      try
      {

         if (soapStream != null)
         { //Still on the SOAP stream.
            if (!eos)
            { //The SOAP packet has not been fully read yet. Need to store it away.

               java.io.ByteArrayOutputStream soapdata =
                       new java.io.ByteArrayOutputStream(1024 * 8);

               byte[] buf = new byte[1024 * 16];
               int byteread = 0;

               do
               {
                  byteread = soapStream.read(buf);
                  if (byteread > 0)
                  {
                     soapdata.write(buf, 0, byteread);
                  }

               }
               while (byteread > -1);
               soapdata.close();
               soapStream.close();
               soapStream = new java.io.ByteArrayInputStream(soapdata.toByteArray());
            }
            dimeDelimitedStream = dimeDelimitedStream.getNextStream();
         }
         //Now start searching for the data.

         if (null != dimeDelimitedStream)
         {
            do
            {
               String contentId = dimeDelimitedStream.getContentId();
               String contentType = dimeDelimitedStream.getType();

               if (contentType != null && !dimeDelimitedStream.getDimeTypeNameFormat().equals(DimeTypeNameFormat.MIME))
               {
                  contentType = "application/uri; uri='" + contentType + "'";
               }


               ManagedMemoryDataSource source = new ManagedMemoryDataSource(dimeDelimitedStream, contentType);
               DataHandler dh = new DataHandler(source);

               AttachmentPartImpl ap = new AttachmentPartImpl(dh);
               if (contentId != null)
               {
                  ap.setMimeHeader(HTTPConstants.HEADER_CONTENT_ID, contentId);
               }

               addPart(contentId, "", ap);

               for (int i = id.length - 1; ret == null && i > -1; --i)
               {
                  if (contentId != null && id[i].equals(contentId))
                  { //This is the part being sought
                     ret = ap;
                  }
               }

               dimeDelimitedStream =
                       dimeDelimitedStream.getNextStream();

            }
            while (null == ret && null != dimeDelimitedStream);
         }
      }
      catch (Exception e)
      {
         throw org.jboss.axis.AxisFault.makeFault(e);
      }

      return ret;
   }

   /**
    * Return the content location.
    *
    * @return the Content-Location of the stream.
    *         Null if no content-location specified.
    */
   public String getContentLocation()
   {
      return null;
   }

   /**
    * Return the content id of the stream
    *
    * @return the Content-Location of the stream.
    *         Null if no content-location specified.
    */
   public String getContentId()
   {
      return contentId;
   }

   /**
    * Read the root stream.
    */

   public int read(byte[] b, int off, int len)
           throws java.io.IOException
   {
      if (closed)
      {
         throw new java.io.IOException(Messages.getMessage("streamClosed"));
      }
      if (eos)
      {
         return -1;
      }
      int read = soapStream.read(b, off, len);

      if (read < 0)
      {
         eos = true;
      }
      return read;
   }

   public int read(byte[] b) throws java.io.IOException
   {
      return read(b, 0, b.length);
   }

   public int read() throws java.io.IOException
   {
      if (closed)
      {
         throw new java.io.IOException(Messages.getMessage("streamClosed"));
      }
      if (eos)
      {
         return -1;
      }
      int ret = soapStream.read();

      if (ret < 0)
      {
         eos = true;
      }
      return ret;
   }

   public void close() throws java.io.IOException
   {
      closed = true;
      soapStream.close();
   }
}