package ro.isdc.wro.http;

import java.io.BufferedOutputStream;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.util.zip.GZIPInputStream;
import java.util.zip.GZIPOutputStream;

import javax.servlet.Filter;
import javax.servlet.FilterChain;
import javax.servlet.FilterConfig;
import javax.servlet.ServletException;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpServletResponseWrapper;

import org.apache.commons.io.IOUtils;
import org.apache.commons.io.output.CountingOutputStream;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import ro.isdc.wro.http.support.HttpHeader;
import ro.isdc.wro.http.support.RedirectedStreamServletResponseWrapper;
import ro.isdc.wro.model.resource.locator.support.DispatcherStreamLocator;
import ro.isdc.wro.util.WroUtil;


/**
 * A filter responsible for gzipping all content served through this filter.
 *
 * @author Alex Objelean
 * @created 11 Apr 2013
 * @since 1.7.0
 */
public class GzipFilter
    implements Filter {
  private static final Logger LOG = LoggerFactory.getLogger(GzipFilter.class);

  /**
   * {@inheritDoc}
   */
  public void init(final FilterConfig filterConfig)
      throws ServletException {
  }

  /**
   * {@inheritDoc}
   */
  public void doFilter(final ServletRequest req, final ServletResponse res, final FilterChain chain)
      throws IOException, ServletException {
    final HttpServletRequest request = (HttpServletRequest) req;
    final HttpServletResponse response = (HttpServletResponse) res;

    if (isGzipAllowed(request)) {
      doGzipResponse(request, response, chain);
    } else {
      LOG.debug("Gzip not allowed. Proceeding with chain.");
      chain.doFilter(request, response);
    }
  }

  /**
   * Performs actual gzip of the filtered content.
   */
  private void doGzipResponse(final HttpServletRequest req, final HttpServletResponse response, final FilterChain chain)
      throws IOException, ServletException {
    LOG.debug("Applying gzip on resource: " + req.getRequestURI());
    response.setHeader(HttpHeader.CONTENT_ENCODING.toString(), "gzip");
    final ByteArrayOutputStream baos = new ByteArrayOutputStream();
    final CountingOutputStream countingStream = new CountingOutputStream(new GZIPOutputStream(new BufferedOutputStream(baos)));
    //final GZIPOutputStream gzout = new GZIPOutputStream(new BufferedOutputStream(baos));
    //Perform gzip operation in-memory before sending response
    final HttpServletResponseWrapper wrappedResponse = new RedirectedStreamServletResponseWrapper(countingStream, response);
    chain.doFilter(req, wrappedResponse);
    //close underlying stream
    countingStream.close();
    response.setContentLength(countingStream.getCount());
    //avoid NO CONTENT error thrown by jetty when gzipping empty response
    if (countingStream.getCount() > 0) {
      IOUtils.write(baos.toByteArray(), response.getOutputStream());
    }
  }

  /*
   * Determines if a byte array is compressed. The java.util.zip GZip implementation does not expose the GZip header so
   * it is difficult to determine if a string is compressed.
   * @param bytes an array of bytes
   * @return true if the array is compressed or false otherwise
   * @throws java.io.IOException if the byte array couldn't be read
   */
  public boolean isCompressed(final byte[] bytes)
      throws IOException {
    if ((bytes == null) || (bytes.length < 2)) {
      return false;
    } else {
      return ((bytes[0] == (byte) (GZIPInputStream.GZIP_MAGIC)) && (bytes[1] == (byte) (GZIPInputStream.GZIP_MAGIC >> 8)));
    }
   }

  /**
   * Checks if the request supports gzip and is not a include request (these cannot be gzipped)
   */
  private boolean isGzipAllowed(final HttpServletRequest request) {
    return !DispatcherStreamLocator.isIncludedRequest(request) && WroUtil.isGzipSupported(request);
  }

  /**
   * {@inheritDoc}
   */
  public void destroy() {
  }

}

@@ -127,46 +127,8 @@
 on;%0A
-import java.util.zip.GZIPInputStream;%0A
 impo
@@ -2375,24 +2375,33 @@
 utputStream(
+%0A        
 baos)));%0A   
@@ -2403,16 +2403,17 @@
 ;%0A    //
+ 
 final GZ
@@ -2495,16 +2495,17 @@
 ;%0A    //
+ 
 Perform 
@@ -2666,16 +2666,24 @@
 gStream,
+%0A       
  respons
@@ -2734,16 +2734,17 @@
 ;%0A    //
+ 
 close un
@@ -2851,16 +2851,17 @@
 ;%0A    //
+ 
 avoid NO
@@ -3045,663 +3045,8 @@
  %7D%0A%0A
-  /*%0A   * Determines if a byte array is compressed. The java.util.zip GZip implementation does not expose the GZip header so%0A   * it is difficult to determine if a string is compressed.%0A   * @param bytes an array of bytes%0A   * @return true if the array is compressed or false otherwise%0A   * @throws java.io.IOException if the byte array couldn't be read%0A   */%0A  public boolean isCompressed(final byte%5B%5D bytes)%0A      throws IOException %7B%0A    if ((bytes == null) %7C%7C (bytes.length %3C 2)) %7B%0A      return false;%0A    %7D else %7B%0A      return ((bytes%5B0%5D == (byte) (GZIPInputStream.GZIP_MAGIC)) && (bytes%5B1%5D == (byte) (GZIPInputStream.GZIP_MAGIC %3E%3E 8)));%0A    %7D%0A   %7D%0A%0A
   /*

remove unnecessary method