package de.ivu.elect.jee.performance.script;

import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.PrintWriter;
import java.util.ArrayList;
import java.util.List;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

import javax.annotation.Nullable;
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.HttpServletResponse;
import javax.servlet.http.HttpServletResponseWrapper;

/**
 *
 * Verschiebt alle <script>-Tags vor das </body> Tag, um html-Seite möglichst schnell im Browser rendern zu können
 * "deferred-loading"
 *
 * 'Nützlich' für das erste Laden der Startseite einer Anwendung per Smartphone oder generell schlechter Verbindung
 *
 * HANDLE WITH CARE (Seiteneffekte nicht ausgeschlossen ;) )
 *
 *  on 22.10.2015.
 */
public class ScriptMoveFilter implements Filter {

  @Override
  public void init(FilterConfig filterConfig) throws ServletException {
// nichts zu tun
  }

  @Override
  public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {
    HttpServletResponse response = (HttpServletResponse) servletResponse;
    ContentCaptureServletResponse capContent = new ContentCaptureServletResponse(response);
    filterChain.doFilter(servletRequest, capContent);
    String content = capContent.getContent();
    if (content != null) {
      List<String> scripts = new ArrayList<>();
      Matcher matcher = Pattern.compile("<script(.*?)</script>").matcher(content);
      while (matcher.find()) {
        scripts.add(matcher.group());
      }

      if (!scripts.isEmpty()) {

        for (String script : scripts) {
          content = content.replace(script, "");
        }

        String allScripts = String.join("", scripts);
        content = content.replace("</body>", allScripts + "</body>");
      }

      response.getWriter().write(content);
    }
  }

  @Override
  public void destroy() {
// nichts zu tun
  }
}

class ContentCaptureServletResponse extends HttpServletResponseWrapper {
  private ByteArrayOutputStream contentBuffer;

  private PrintWriter writer;

  public ContentCaptureServletResponse(HttpServletResponse resp) {
    super(resp);
  }

  @Override
  public PrintWriter getWriter() throws IOException {
    if (writer == null) {
      contentBuffer = new ByteArrayOutputStream();
      writer = new PrintWriter(contentBuffer);
    }
    return writer;
  }

  @Nullable
  public String getContent() {
    if (writer == null) {
      return null;
    }
    writer.flush();

    return new String(contentBuffer.toByteArray());
  }
}