/*
 * Copyright
 *   2007 axYus - www.axyus.com
 *   2008 C.Marchand - christophe.marchand@axyus.com
 *
 * This file is part of XEMELIOS.
 *
 * XEMELIOS 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 3 of the License, or
 * (at your option) any later version.
 *
 * XEMELIOS 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 XEMELIOS; if not, write to the Free Software
 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
 */

package fr.gouv.finances.dgfip.xemelios.web.utils.filters;

import com.lowagie.text.BadElementException;
import com.lowagie.text.Image;
import com.lowagie.text.Rectangle;
import com.lowagie.text.pdf.PdfReader;
import fr.gouv.finances.dgfip.xemelios.web.utils.CharArrayResponseWrapper;
import java.io.ByteArrayOutputStream;
import java.io.CharArrayReader;
import java.io.IOException;

import java.io.InputStream;
import java.net.URL;
import java.sql.SQLException;
import javax.servlet.Filter;
import javax.servlet.FilterChain;
import javax.servlet.FilterConfig;
import javax.servlet.ServletContext;
import javax.servlet.ServletException;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpServletRequest;

import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;

import org.w3c.dom.Document;
import org.xhtmlrenderer.pdf.ITextRenderer;
import org.xml.sax.InputSource;

import org.apache.log4j.Logger;
import org.xhtmlrenderer.context.StyleReference;
import org.xhtmlrenderer.pdf.ITextFSImage;
import org.xhtmlrenderer.pdf.ITextOutputDevice;
import org.xhtmlrenderer.pdf.ITextUserAgent;
import org.xhtmlrenderer.pdf.PDFAsImage;
import org.xhtmlrenderer.resource.CSSResource;
import org.xhtmlrenderer.resource.ImageResource;
import org.xhtmlrenderer.util.XRLog;

/**
 * @author CHM
 * @author CBO
 */
public class PdfFilter implements Filter {

  private static final Logger logger = Logger.getLogger(PdfFilter.class);
  private ServletContext servletContext;

  public void init(FilterConfig filterConfig) throws ServletException {
    this.servletContext = filterConfig.getServletContext();
  }

  public void doFilter(ServletRequest req, ServletResponse response, FilterChain chain) throws IOException, ServletException {
    if (req instanceof HttpServletRequest) {
      HttpServletRequest httpReq = (HttpServletRequest) req;
      HttpServletResponse httpResp = (HttpServletResponse) response;
      if ("true".equals(req.getParameter("pdfRendering"))) {
        //ByteArrayResponseWrapper wrapper = new ByteArrayResponseWrapper(httpResp);
        CharArrayResponseWrapper wrapper = new CharArrayResponseWrapper(httpResp);
        chain.doFilter(req, wrapper);
        ByteArrayOutputStream baos = new ByteArrayOutputStream();
        try {
          DocumentBuilder builder = DocumentBuilderFactory.newInstance().newDocumentBuilder();
          //Document doc = builder.parse(new InputSource(new StringReader(new String(wrapper.toByteArray()))));
          Document doc = builder.parse(new InputSource(new CharArrayReader(wrapper.toCharArray())));
          ITextRenderer renderer = null;
          renderer = new ITextRenderer();
          CustomUserAgentCallback callback = new CustomUserAgentCallback(renderer.getOutputDevice(), servletContext, httpReq.getContextPath());
          callback.setSharedContext(renderer.getSharedContext());
          renderer.getSharedContext().setUserAgentCallback(callback);
          StyleReference css = new StyleReference(callback);
          renderer.getSharedContext().setCss(css);
          renderer.setDocument(doc, httpReq.getRequestURL().toString());
          renderer.layout();
          renderer.createPDF(baos);
          renderer.finishPDF();
          baos.flush();
          baos.close();
          byte[] data = baos.toByteArray();
          httpResp.setContentType("application/pdf");
          httpResp.setHeader("Content-Disposition", "attachment");
          httpResp.setContentLength(data.length);
          httpResp.getOutputStream().write(data);
          httpResp.getOutputStream().flush();
          httpResp.getOutputStream().close();
        } catch (Exception ex) {
          throw new ServletException(ex);
        }
      } else {
        chain.doFilter(req, response);
      }
    } else {
      chain.doFilter(req, response);
    }
  }

  public void destroy() {
  }

  static private class CustomUserAgentCallback extends ITextUserAgent {

    private ServletContext servletContext;
    private String contextPath;
    private ITextOutputDevice outputDevice;

    public CustomUserAgentCallback(ITextOutputDevice outputDevice, ServletContext servletContext, String contextPath) {
      super(outputDevice);
      this.outputDevice = outputDevice;
      this.contextPath = contextPath;
      this.servletContext = servletContext;
    }

    private InputStream openStream(URL url) throws IOException, SQLException {
      return url.openStream();
    }

    @Override
    protected InputStream resolveAndOpenStream(String uri) {
      InputStream is = null;
      try {
        URL url = new URL(uri);
        is = openStream(url);
      } catch (Exception e) {
        logger.warn(e, e);
      }
      return is;
    }

    @Override
    public CSSResource getCSSResource(String uri) {
      return new CSSResource(resolveAndOpenStream(uri));
    }

    @Override
    public ImageResource getImageResource(String uri) {
      ImageResource resource = null;
      uri = resolveURI(uri);
      resource = (ImageResource) _imageCache.get(uri);
      if (resource == null) {
        InputStream is = resolveAndOpenStream(uri);
        if (is != null) {
          try {
            URL url = new URL(uri);
            if (url.getPath() != null && url.getPath().toLowerCase().endsWith(".pdf")) {
              PdfReader reader = outputDevice.getReader(url);
              PDFAsImage image = new PDFAsImage(url);
              Rectangle rect = reader.getPageSizeWithRotation(1);
              image.setInitialWidth(rect.getWidth() * outputDevice.getDotsPerPoint());
              image.setInitialHeight(rect.getHeight() * outputDevice.getDotsPerPoint());
              resource = new ImageResource(image);
            } else {
              Image image = Image.getInstance(inputStreamToBytes(openStream(url)));
              scaleToOutputResolution(image);
              resource = new ImageResource(new ITextFSImage(image));
            }
            _imageCache.put(uri, resource);
          } catch (IOException e) {
            XRLog.exception("Can't read image file; unexpected problem for URI '" + uri + "'", e);
          } catch (BadElementException e) {
            XRLog.exception("Can't read image file; unexpected problem for URI '" + uri + "'", e);
          } catch (SQLException e) {
            XRLog.exception(e.toString());
          }
        }
      }
      if (resource == null) {
        resource = new ImageResource(null);
      }
      return resource;
    }

    public static byte[] inputStreamToBytes(InputStream is) throws IOException {
      ByteArrayOutputStream baos = new ByteArrayOutputStream();
      byte[] buffer = new byte[4096];
      int n;
      while ((n = is.read(buffer)) != -1) {
        baos.write(buffer, 0, n);
      }
      return baos.toByteArray();
    }

    private void scaleToOutputResolution(Image image) {
      float factor = getSharedContext().getDotsPerPixel();
      image.scaleAbsolute(image.getPlainWidth() * factor, image.getPlainHeight() * factor);
    }
  }
}
