/*
 * Copyright (c) 2009, Takeyuki Nagao
 * All rights reserved.
 * 
 * Redistribution and use in source and binary forms, with or
 * without modification, are permitted provided that the
 * following conditions are met:
 * 
 *  * Redistributions of source code must retain the above
 *    copyright notice, this list of conditions and the
 *    following disclaimer.
 *  * Redistributions in binary form must reproduce the above
 *    copyright notice, this list of conditions and the
 *    following disclaimer in the documentation and/or other
 *    materials provided with the distribution.
 *    
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND
 * CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES,
 * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
 * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
 * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR
 * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
 * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
 * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
 * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
 * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE
 * USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY
 * OF SUCH DAMAGE.
 */

package dvi.v2.xdoc.ps;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.net.MalformedURLException;
import java.util.logging.Level;
import java.util.logging.Logger;

import dvi.DviException;
import dvi.DviObject;
import dvi.DviPaperSize;
import dvi.DviRect;
import dvi.DviResolution;
import dvi.DviSize;
import dvi.gs.GhostscriptUtils;
import dvi.image.split.ImageFileConfig;
import dvi.image.split.SplitImage;
import dvi.image.split.ZipSplitImageReader;
import dvi.image.split.ZipSplitImageWriter;
import dvi.util.DviUtils;
import dvi.util.ZipBuilder;
import dvi.v2.xdoc.XDocument;
import dvi.v2.xdoc.XPage;
public class GhostscriptXPage
extends DviObject
implements XPage
{
  private static final Logger LOGGER = Logger.getLogger(GhostscriptXPage.class
      .getName());
  
  protected final GhostscriptXDocument xdoc;
  private int pageNum;
  
  GhostscriptXPage(GhostscriptXDocument xdoc, int pageNum)
  {
    super(xdoc);
    this.xdoc = xdoc;
    this.pageNum = pageNum;
  }

  public DviRect getBoundingBox() throws DviException {
    return xdoc.bboxes[pageNum];
  }

  public DviPaperSize getPaperSize() throws DviException {
    return xdoc.getPaperSize();
  }

  public int getPageNumber() throws DviException {
    return pageNum;
  }

  public XDocument getDocument() {
    return xdoc;
  }
  
  protected File getSplitImageFile(DviResolution res) throws DviException {
    try {
      File dir = new File(getDviContext().getCacheDirectory(), "resource");
      String md5 = DviUtils.md5Hex(xdoc.getFile().toURL().toExternalForm());
      File itemDir = new File(dir, md5.substring(0, 2));
      if (!itemDir.exists()) {
        itemDir.mkdirs();
      }
      // TODO: think about the naming rule to include the format and unit size.
      return new File(itemDir, "gs-output--" + md5 + "--" + res.dpi() + "_" + res.shrinkFactor() + "--" + getPageNumber() + ".zip");
    } catch (MalformedURLException e) {
      throw new DviException(e);
    }
  }

  private SplitImage splitImage = null;

  public synchronized SplitImage getSplitImage(DviResolution res, DviRect target,
      DviSize unit) throws DviException {
    if (splitImage != null) return splitImage;
    File file = getSplitImageFile(res);
    if (!file.exists()) {
      try {
        File tmpFile = File.createTempFile("gs-temp", null);
        tmpFile.deleteOnExit();
        try {
          LOGGER.fine("Generating page cache: " + file.getAbsolutePath());
          final FileOutputStream fos = new FileOutputStream(tmpFile);
          final ZipBuilder zb = new ZipBuilder(fos);
          final InputStream is = new FileInputStream(xdoc.getFile());
          try {
            final ZipSplitImageWriter imageWriter = new ZipSplitImageWriter
              (file.getName(), ImageFileConfig.PNG, res, zb);
            // Remark that Ghostscript counts pages from 1 not 0.
            GhostscriptUtils.renderAndSplit(getDviContext(), is, unit, imageWriter, res, "pnmraw", pageNum + 1, 0, null);
          } finally {
            DviUtils.silentClose(fos);
            DviUtils.silentClose(is);
          }
          if (!tmpFile.renameTo(file)) {
            throw new DviException("Failed to create cache file: " + file);
          }
        } finally {
          tmpFile.delete();
        }
      } catch (FileNotFoundException e) {
        DviUtils.logStackTrace(LOGGER, Level.WARNING, e);
        throw new DviException(e);
      } catch (IOException e) {
        DviUtils.logStackTrace(LOGGER, Level.WARNING, e);
        throw new DviException(e);
      }
    }

    ZipSplitImageReader imageReader = new ZipSplitImageReader(file);
    splitImage = imageReader.getSplitImage();
    return splitImage;
  }
}
