package ro.isdc.wro.http.support;

import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.OutputStreamWriter;
import java.io.Reader;
import java.io.StringReader;
import java.io.StringWriter;
import java.io.Writer;
import java.util.List;

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.slf4j.Logger;
import org.slf4j.LoggerFactory;

import ro.isdc.wro.WroRuntimeException;
import ro.isdc.wro.config.Context;
import ro.isdc.wro.http.WroFilter;
import ro.isdc.wro.model.group.processor.Injector;
import ro.isdc.wro.model.group.processor.InjectorBuilder;
import ro.isdc.wro.model.resource.processor.ResourcePreProcessor;
import ro.isdc.wro.util.StopWatch;

/**
 * Allows configuration of a list of processors to be applied on the    
 * 
 * @author Alex Objelean
 * @since 1.4.5
 * @created 17 Mar 2012
 */
public abstract class AbstractProcessorFilter
    implements Filter {
  private static final Logger LOG = LoggerFactory.getLogger(AbstractProcessorFilter.class);
  private FilterConfig filterConfig;
  /**
   * {@inheritDoc}
   */
  public final void init(final FilterConfig config)
    throws ServletException {
    this.filterConfig = config;
    doInit(config);
  }
  
  /**
   * Allows custom initialization.
   */
  protected void doInit(final FilterConfig config) {
  }

  /**
   * {@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;
    try {
      // add request, response & servletContext to thread local
      Context.set(Context.webContext(request, response, filterConfig));
      IOUtils.write("\nBefore chain!\n", response.getOutputStream());
      final ByteArrayOutputStream os = new ByteArrayOutputStream();
      System.out.println(response.getOutputStream());
      final HttpServletResponse wrappedResponse = new RedirectedStreamServletResponseWrapper(os, response);
      chain.doFilter(request, wrappedResponse); 
      final Reader reader = new StringReader(new String(os.toByteArray(), Context.get().getConfig().getEncoding()));
      
      doProcess(reader, new OutputStreamWriter(os));
      IOUtils.write(os.toByteArray(), response.getOutputStream());
      response.flushBuffer();
      response.getOutputStream().close();
    } catch (final RuntimeException e) {
      onRuntimeException(e, response, chain);
    } finally { 
      Context.unset();
    }
  }

  /**
   * Applies configured processor on the intercepted stream.
   */
  private void doProcess(final Reader reader, final Writer writer)
      throws IOException {
    Reader input = reader;
    Writer output = null;
    try {
      final StopWatch stopWatch = new StopWatch();
      Injector injector = new InjectorBuilder().build();
      for (final ResourcePreProcessor processor : processorsList()) {
        stopWatch.start("Using " + processor.getClass().getSimpleName());
        // inject all required properites
        injector.inject(processor);
        
        output = new StringWriter();
        processor.process(null, input, output);
        
        input = new StringReader(output.toString());
        stopWatch.stop();
      }
      LOG.debug(stopWatch.prettyPrint());
      writer.write(output.toString());
    } finally {
      reader.close();
      writer.close();
    }
  }

  /**
   * Invoked when a {@link RuntimeException} is thrown. Allows custom exception handling. The default implementation
   * redirects to 404 for a specific {@link WroRuntimeException} exception when in DEPLOYMENT mode.
   *
   * @param e {@link RuntimeException} thrown during request processing.
   */
  protected void onRuntimeException(final RuntimeException e, final HttpServletResponse response,
    final FilterChain chain) {
    LOG.debug("RuntimeException occured", e);
    try {
      LOG.debug("Cannot process. Proceeding with chain execution.");
      chain.doFilter(Context.get().getRequest(), response);
    } catch (final Exception ex) {
      // should never happen
      LOG.error("Error while chaining the request: " + HttpServletResponse.SC_NOT_FOUND);
    }
  }

  /**
   * @return a list of processors to apply for this filter.
   */
  protected abstract List<ResourcePreProcessor> processorsList();

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

@@ -98,43 +98,8 @@
 on;%0A
-import java.io.OutputStreamWriter;%0A
 impo
@@ -109,32 +109,32 @@
 java.io.Reader;%0A
+
 import java.io.S
@@ -220,16 +220,39 @@
 il.List;
+%0Aimport java.util.UUID;
 %0A%0Aimport
@@ -553,62 +553,8 @@
 nse;
-%0Aimport javax.servlet.http.HttpServletResponseWrapper;
 %0A%0Aim
@@ -726,43 +726,8 @@
 xt;%0A
-import ro.isdc.wro.http.WroFilter;%0A
 impo
@@ -2013,32 +2013,216 @@
 Config));%0A      
+//response.setHeader(%22ETag%22, %22%22 + UUID.randomUUID());%0A      response.setContentType(%22text/css%22 + %22; charset=UTF-8%22);%0A      //write one character to avoid closed connection issue%0A      
 IOUtils.write(%22%5C
@@ -2224,23 +2224,8 @@
 te(%22
-%5CnBefore chain!
 %5Cn%22,
@@ -2326,62 +2326,8 @@
 ();%0A
-      System.out.println(response.getOutputStream());%0A
     
@@ -2598,24 +2598,65 @@
 ()));%0A      
+StringWriter writer = new StringWriter();
 %0A      doPro
@@ -2672,34 +2672,14 @@
 er, 
-new OutputStreamW
+w
 riter
-(os)
 );%0A 
@@ -2697,30 +2697,31 @@
 s.write(
-os.toByteArray
+writer.toString
 (), resp

add one character befo