package plab.prefuseTests;

/*
 * @(#)ImageExporter.java   01.23.2009
 *
 */

import prefuse.Display;
import prefuse.Visualization;

import prefuse.util.GraphicsLib;
import prefuse.util.display.ScaleSelector;
import prefuse.util.io.IOLib;
import prefuse.util.io.SimpleFileFilter;

//~--- JDK imports ------------------------------------------------------------

import java.awt.Dimension;
import java.awt.Graphics2D;
import java.awt.GraphicsEnvironment;
import java.awt.event.ActionEvent;
import java.awt.geom.Point2D;
import java.awt.geom.Rectangle2D;
import java.awt.image.BufferedImage;

import java.io.BufferedOutputStream;
import java.io.File;
import java.io.FileOutputStream;
import java.io.OutputStream;

import java.util.HashSet;

import javax.imageio.ImageIO;

import javax.swing.AbstractAction;
import javax.swing.JFileChooser;
import javax.swing.JOptionPane;

//~--- Classes ----------------------------------------------------------------

/**
 * This class exports a prefuse.Display to a graphics file. The scalefactor will
 * be modifiable via a Scale Selector.
 * The class is built as a Swing ActionListener that reveals a dialog box that allows users to
 * export the current Display view to an image file. Unlike the prefuse ExportDisplayAction
 * class, this class exports both the on and off screen components
 *
 *
 * @version 1.0.1, 01.23.2009
 * @author V.Balamuru (based on the DisplayToImageExporter() class of Marcus St&auml;nder)
 */
public class EnhancedExportDisplayAction extends AbstractAction {

	private Display display;
	private JFileChooser chooser;
	private ScaleSelector scaler;

	// ~--- Constructors -------------------------------------------------------

	public EnhancedExportDisplayAction(Display display) {
		this.display = display;
	}

	// ~--- Methods ------------------------------------------------------------

	/**
	 * This method initiates the chooser components, detecting available image
	 * formats
	 *
	 */
	private void init() {

		scaler = new ScaleSelector();
		// Initialize the chooser
		chooser = new JFileChooser();
		chooser.setDialogType(JFileChooser.SAVE_DIALOG);
		chooser.setDialogTitle("Export graph as image");
		chooser.setAcceptAllFileFilterUsed(false);

		HashSet<String> availableFormats = new HashSet<String>();
		String[] fmts = ImageIO.getWriterFormatNames();
		for (int i = 0; i < fmts.length; i++) {
			String s = fmts[i].toLowerCase();
			if ((s.length() == 3) && !availableFormats.contains(s)) {
				availableFormats.add(s);
				chooser.setFileFilter(new SimpleFileFilter(s, s.toUpperCase()
						+ " Image (*." + s + ")"));
			}
		}

		availableFormats.clear();
		availableFormats = null;
		chooser.setAccessory(scaler);
	}

	@Override
	public void actionPerformed(ActionEvent evt) {
		// TODO Auto-generated method stub
		System.err.println("Unimplemented");
		export(display);

	}
	/**
	 * This method lets the user select the target file and exports the
	 * <code>Display</code>
	 *
	 * @paran display the <code>Display</code> to export
	 *
	 */
	public void export(Display display) {

		// Initialize if needed
		if (chooser == null) {
			init();
		}

		// open image save dialog
		File f = null;
		scaler.setImage(display.getOffscreenBuffer());

		int returnVal = chooser.showSaveDialog(display);

		if (returnVal == JFileChooser.APPROVE_OPTION) {
			f = chooser.getSelectedFile();
		} else {
			return;
		}

		String format = ((SimpleFileFilter) chooser.getFileFilter())
				.getExtension();
		String ext = IOLib.getExtension(f);

		if (!format.equals(ext)) {
			f = new File(f.toString() + "." + format);
		}

		// Now save the image
		boolean success = false;

		try {
			OutputStream out = new BufferedOutputStream(new FileOutputStream(f));

			System.out.print("INFO Saving image " + f.getName() + ", " + format
					+ " format...");
			success = exportImage(display, out, format);
			out.flush();
			out.close();
			System.out.println("\tDONE");
		} catch (Exception e) {
			success = false;
		}

		// show result dialog on failure
		if (!success) {
			JOptionPane.showMessageDialog(display, "Error Saving Image!",
					"Image Save Error", JOptionPane.ERROR_MESSAGE);
		}
	}

	private boolean exportImage(Display display, OutputStream output,
			String format) {

		String m_group = Visualization.ALL_ITEMS;

		try {

			// Now comes the nice part

			// Get the bounding box
			Rectangle2D bounds = display.getVisualization().getBounds(m_group);

			// Some little extra spacing
			GraphicsLib.expand(bounds, 10 + (int) (1 / display.getScale()));

			// Get a buffered image to draw into
			BufferedImage img = getNewOffscreenBuffer(display, (int) bounds
					.getWidth(), (int) bounds.getHeight());
			Graphics2D g = (Graphics2D) img.getGraphics();

			/*
			 * Set up the display, render, then revert to normal settings
			 */

			// The zoom point, zooming should not change anything else than the
			// scale
			Point2D zoomPoint = new Point2D.Double(0, 0);

			// Get and remember the current scaling
			Double scale = display.getScale();

			// Change scale to normal (1)
			display.zoom(zoomPoint, 1 / scale);

			boolean isHighQuality = display.isHighQuality();
			display.setHighQuality(true);

			// Remember the current point
			Point2D currentPoint = new Point2D.Double(display.getDisplayX(),
					display.getDisplayY());

			// Now pan so the most left element is at the left side of the
			// display and
			// the highest element is at the top.
			display.panToAbs(new Point2D.Double(bounds.getMinX()
					+ display.getWidth() / 2, bounds.getMinY()
					+ display.getHeight() / 2));

			// Now lets prefuse to the actual painting
			display.paintDisplay(g, new Dimension((int) bounds.getWidth(),
					(int) bounds.getHeight()));

			// Undo the panning, zooming and reset the quality mode
			display.panToAbs(new Point2D.Double(currentPoint.getX()
					+ display.getWidth() / 2, currentPoint.getY()
					+ display.getHeight() / 2));
			display.setHighQuality(isHighQuality);
			display.zoom(zoomPoint, scale); // also takes care of damage report

			// Save the image and return
			ImageIO.write(img, format, output);

			return true;
		} catch (Exception e) {
			return false;
		}
	}

	// ~--- Get methods --------------------------------------------------------

	// From Display
	private BufferedImage getNewOffscreenBuffer(Display display, int width,
			int height) {
		BufferedImage img = null;

		if (!GraphicsEnvironment.isHeadless()) {
			try {
				img = (BufferedImage) display.createImage(width, height);
			} catch (Exception e) {
				img = null;
			}
		}

		if (img == null) {
			return new BufferedImage(width, height, BufferedImage.TYPE_INT_RGB);
		}

		return img;
	}


}
