/*
* JBoss, Home of Professional Open Source
* Copyright 2005, JBoss Inc., and individual contributors as indicated
* by the @authors tag. See the copyright.txt 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.remoting.marshal.http;

import org.jboss.logging.Logger;
import org.jboss.remoting.marshal.UnMarshaller;
import org.jboss.remoting.marshal.serializable.SerializableUnMarshaller;
import org.jboss.remoting.transport.http.HTTPMetadataConstants;
import org.jboss.remoting.transport.web.WebUtil;

import java.io.BufferedReader;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.EOFException;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.util.List;
import java.util.Map;

/**
 * @author <a href="mailto:telrod@e2technologies.net">Tom Elrod</a>
 */
public class HTTPUnMarshaller extends SerializableUnMarshaller
{
   static final long serialVersionUID = 1085086661310576768L;

   public final static String DATATYPE = "http";

   protected final Logger log = Logger.getLogger(getClass());

   /**
    * Will try to unmarshall data from inputstream.  Will try to convert to either an object
    * or a string.  If there is no data to read, will return null.
    *
    * @param inputStream
    * @param metadata
    * @param version
    * @return
    * @throws IOException
    * @throws ClassNotFoundException
    */
   public Object read(InputStream inputStream, Map metadata, int version) throws IOException, ClassNotFoundException
   {
      if (isBinaryData(metadata))
      {
         try
         {
            return super.read(inputStream, metadata, version);
         }
         catch (EOFException e)
         {
            return null;
         }
      }
      
      int contentLength = -1;
      Object ret = null;
      int bufferSize = 1024;
      byte[] byteBuffer = new byte[bufferSize];
      ByteArrayOutputStream byteOutputStream = new ByteArrayOutputStream();

      // check the metadat to see if is entry for content length
      if(metadata != null)
      {
         Object value = metadata.get("Content-Length");
         if(value == null)
         {
            value = metadata.get("content-length");
         }
         if(value != null)
         {
            if(value instanceof List)
            {
               List valueList = (List) value;
               if(valueList != null && valueList.size() > 0)
               {
                  value = valueList.get(0);
               }
            }
            if(value instanceof String)
            {
               try
               {
                  contentLength = Integer.parseInt((String) value);
               }
               catch(NumberFormatException e)
               {
                  log.warn("Error converting Content-Length value (" + value + ") from metadata into int value.");
               }
            }
            else
            {
               log.warn("Can not get Content-Length from header for http unmarshalling.");
            }
         }
      }

      int pointer = 0;
      int amtRead = inputStream.read(byteBuffer);
      while(amtRead > 0)
      {
         byteOutputStream.write(byteBuffer, pointer, amtRead);
         if(amtRead < bufferSize && byteOutputStream.size() >= contentLength)
         {
            //done reading, so process
            break;
         }
         amtRead = inputStream.read(byteBuffer);
      }

      byteOutputStream.flush();

      byte[] totalByteArray = byteOutputStream.toByteArray();

      if(totalByteArray.length == 0)
      {
         //nothing to read, so is null
         return null;
      }

     //boolean isError = isErrorReturn(metadata);
     //if(isBinary || isError)
      
      try
      {
         
         BufferedReader reader = new BufferedReader(new InputStreamReader(new ByteArrayInputStream(totalByteArray)));
         StringBuffer buffer = new StringBuffer();
         String str = null;
         while((str = reader.readLine()) != null)
         {
            buffer.append(str);
         }
         reader.close();
         
         ret = buffer.toString();
         
      }
      catch(Exception e)
      {
         log.error("Can not unmarshall inputstream.  Tried to unmarshall as both an object and string type.", e);
         throw new IOException("Can not unmarshall inputstream.");
      }
      
      return ret;

   }

   public UnMarshaller cloneUnMarshaller() throws CloneNotSupportedException
   {
      HTTPUnMarshaller unmarshaller = new HTTPUnMarshaller();
      unmarshaller.setClassLoader(this.customClassLoader);
      return unmarshaller;
   }

   private boolean isErrorReturn(Map metadata)
   {
      boolean isError = false;
      if(metadata != null)
      {
         // key of null will be the response (http type, response code, and response message)
         Object value = metadata.get(HTTPMetadataConstants.RESPONSE_CODE);
         if(value != null && value instanceof Integer)
         {
            int responseCode = ((Integer) value).intValue();
            if(responseCode > 400)
            {
               isError = true;
            }
         }
      }
      return isError;
   }

   private boolean isBinaryData(Map metadata) throws IOException
   {
      boolean isBinary = false;

      if(metadata != null)
      {
         // need to get the content type
         Object value = metadata.get("Content-Type");
         if(value == null)
         {
            value = metadata.get("content-type");
         }
         if(value != null)
         {
            if(value instanceof List)
            {
               List valueList = (List) value;
               if(valueList != null && valueList.size() > 0)
               {
                  value = valueList.get(0);
               }
            }
            isBinary = WebUtil.isBinary((String) value);
         }
      }
      return isBinary;
   }

}