/*
 * 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 test.dvi.v2.csv;

import java.io.File;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.util.Map;
import java.util.concurrent.TimeUnit;
import java.util.logging.Level;
import java.util.logging.Logger;

import dvi.DviException;
import dvi.DviResolution;
import dvi.DviSize;
import dvi.api.DviContext;
import dvi.ctx.DefaultDviContext;
import dvi.gs.GhostscriptCommandBuilder;
import dvi.image.pnm.PbmSplitter;
import dvi.image.split.DefaultSplitImageWriter;
import dvi.image.split.ImageFileConfig;
import dvi.image.split.SplitImage;
import dvi.image.split.SplitPiece;
import dvi.image.split.URLImagePiece;
import dvi.util.Benchmark;
import dvi.util.CommandShell;
import dvi.util.CommandShellHandler;
import dvi.util.DviUtils;
import dvi.util.csv.CsvData;
import dvi.util.csv.CsvException;
import dvi.util.csv.StringCsvData;
public class GhostscriptBenchmark {
  private static final Logger LOGGER = Logger
      .getLogger(GhostscriptBenchmark.class.getName());
  public static void main(final String[] args)
  throws Exception
  {
    if (args.length < 2) {
      showUsage();
      System.exit(1);
    } else {
      doMain(args);
    }
  }
  
  public static void doMain(String [] args) throws FileNotFoundException, IOException, CsvException, DviException
  {
    final File experimentsDir = new File("experiments");
    final File outputDir = new File(experimentsDir, "output");
    DviContext ctx = new DefaultDviContext();
    String timeid = DviUtils.getTimeId();
    String uuid = DviUtils.getApplicationInstanceUUID().toString();
    File resultsCsv = new File(outputDir, "result-" + timeid + "--" + uuid + ".csv");
    
    StringCsvData docsCsv = new StringCsvData();
    docsCsv.readFromFile(new File(args[0]));
    StringCsvData confCsv = new StringCsvData();
    confCsv.readFromFile(new File(args[1]));
    
    StringCsvData result = new StringCsvData();
    for (int i=0; i<confCsv.getRowCount(); i++) {
      Map<String, String> confData = confCsv.getRow(i);
      LOGGER.info("conf=" + confData);
      for (int j=0; j<docsCsv.getRowCount(); j++) {
        Map<String, String> docData = docsCsv.getRow(j);
        LOGGER.info("doc=" + docData);
        try {
          doConvert(outputDir, ctx, result, confData, docData);
        } catch (Exception e) {
          DviUtils.logStackTrace(LOGGER, Level.WARNING, e);
        }
      }
    }
    LOGGER.info("Writing output to file " + resultsCsv);
    result.writeToFile(resultsCsv);
    LOGGER.info("Finished.");
  }
  
  private static void doConvert(final File outputDir, final DviContext ctx, final CsvData<String, String> result, Map<String, String> confData, Map<String, String> docData) throws DviException, IOException, InterruptedException {
    final File tmpOutputDir = new File(outputDir, "tmp");
    if (!tmpOutputDir.exists()) {
      tmpOutputDir.mkdirs();
    }
    final int dpi = Integer.parseInt(confData.get("dpi"));
    final int sf = Integer.parseInt(DviUtils.wrapNull(confData.get("sf"), "1"));
    final DviResolution res = new DviResolution(dpi, sf);
    final int uw = Integer.parseInt(confData.get("unit_width"));
    final int uh = Integer.parseInt(confData.get("unit_height"));
    final DviSize unit = new DviSize(uw, uh);
    final ImageFileConfig imgConf = new ImageFileConfig(confData.get("image_type"), confData.get("image_ext"));
    final int totalPages = Integer.parseInt(docData.get("doc_total_pages"));
    final String docId = docData.get("doc_id");
    
    for (int i = 0; i < totalPages; i++) {
      int page = i + 1;

      Benchmark bm = new Benchmark();
      bm.begin("doc_id=" + docId + " page=" + page);
      result.beginLine();
      try {
        result.putAll(confData);
        result.putAll(docData);

        boolean success = false;
        final File output = new File(tmpOutputDir, "gs-output-" + docId + "-"
            + totalPages + "-" + page);
        final File input = new File(result.get("doc_path"));
        LOGGER.info("Rendering page " + page + " of document " + docId);
        result.put("doc_size", String.valueOf(input.length()));

        final GhostscriptCommandBuilder gs = new GhostscriptCommandBuilder(ctx);
        gs.setInputFile(input);
        gs.setOutputFile(null);
        gs.setResolution(dpi);
        gs.setDevice(result.get("gs_device"));
        gs.setFirstPage(page);
        gs.setLastPage(page);
        bm.addSample();
        final CommandShell shell = gs.createCommandShell();
        final DefaultSplitImageWriter imageWriter = new DefaultSplitImageWriter(output, imgConf, res);
        final PbmSplitter splitter = new PbmSplitter(unit, imageWriter);

        shell.setTimeout(100000, TimeUnit.MILLISECONDS);
        CommandShellHandler handler = new CommandShellHandler() {
          public void handleStderr(InputStream in) throws IOException {
            try {
              DviUtils.dumpStreamAsync("gs stderr", in, System.err).join();
            } catch (InterruptedException e) {
              DviUtils.logStackTrace(LOGGER, Level.WARNING, e);
            }
          }

          public void handleStdin(OutputStream out) throws IOException {
            out.close();
          }

          public void handleStdout(InputStream in) throws IOException, DviException {
            splitter.splitImageFromStream(in);
          }
        };
        shell.setHandler(handler);
        int ret = shell.execute();
        if (ret != 0) {
          throw new DviException("Ghostscript failed with exit code " + ret);
        } else {
          success = true;
        }

        SplitImage splitImage = imageWriter.getSplitImage();
        long totalSize = 0;
        {
          boolean first = true;
          for (SplitPiece piece : splitImage) {
            if (piece instanceof URLImagePiece) {
              URLImagePiece f = (URLImagePiece) piece;
              File file = DviUtils.toFile(f.getURL());
              if (first) {
                first = false;
                result.put("first_file", f.getURL().toExternalForm());
              }
              if (file != null) {
                totalSize += file.length();
              }
            }
          }
          result.put("num_files", String.valueOf(splitImage.getPieces().size()));
          result.put("totalSize", String.valueOf(totalSize));
        }
        
        bm.end();
        result.put("doc_page", String.valueOf(page));
        result.put("start_time", String.valueOf(bm.getStartTime()));
        result.put("end_time", String.valueOf(bm.getEndTime()));
        result.put("lapsed_time", String.valueOf(bm.getLapsedTime()));
        result.put("success", String.valueOf(success));
      } finally {
        result.endLine();
      }
      System.out.println(bm.format());
    }
  }

  private static void showUsage() {
    System.out.println(GhostscriptBenchmark.class.getName() + ": Benchmark the Ghostscript wrapper.");
    System.out.println("arguments: <document-list-in-csv> <config-list-in-csv>");
  }
}
