package com.mxgraph.util;

import java.awt.Color;
import java.awt.Point;
import java.awt.Rectangle;
import java.awt.image.BufferedImage;

import org.w3c.dom.Document;

import com.mxgraph.canvas.mxGraphics2DCanvas;
import com.mxgraph.canvas.mxHtmlCanvas;
import com.mxgraph.canvas.mxICanvas;
import com.mxgraph.canvas.mxImageCanvas;
import com.mxgraph.canvas.mxSvgCanvas;
import com.mxgraph.canvas.mxVmlCanvas;
import com.mxgraph.view.mxGraph;
import com.mxgraph.view.mxGraphView;
import com.mxgraph.view.mxTemporaryCellStates;

public class mxCellRenderer
{
	/**
	 * 
	 */
	private mxCellRenderer()
	{
		// static class
	}

	/**
	 * Draws the given cells using a Graphics2D canvas and returns the buffered image
	 * that represents the cells.
	 * 
	 * @param graph Graph to be painted onto the canvas.
	 * @return Returns the image that represents the canvas.
	 */
	public static mxICanvas drawCells(mxGraph graph, Object[] cells,
			double scale, mxRectangle clip, CanvasFactory factory)
	{
		mxICanvas canvas = null;

		if (cells == null)
		{
			cells = new Object[] { graph.getModel().getRoot() };
		}

		if (cells != null)
		{
			// Gets the current state of the view
			mxGraphView view = graph.getView();

			// Keeps the existing translation as the cells might
			// be aligned to the grid in a different way in a graph
			// that has a translation other than zero
			boolean eventsEnabled = view.isEventsEnabled();

			// Disables firing of scale events so that there is no
			// repaint or update of the original graph
			view.setEventsEnabled(false);

			// Uses the view to create temporary cell states for each cell
			mxTemporaryCellStates temp = new mxTemporaryCellStates(view, scale,
					cells);

			try
			{
				if (clip == null)
				{
					clip = graph.getPaintBounds(cells);
				}

				if (clip != null && clip.getWidth() > 0 && clip.getHeight() > 0)
				{
					Rectangle rect = clip.getRectangle();
					canvas = factory.createCanvas(rect.width + 1,
							rect.height + 1);

					if (canvas != null)
					{
						double previousScale = canvas.getScale();
						Point previousTranslate = canvas.getTranslate();

						try
						{
							canvas.setTranslate(-rect.x, -rect.y);
							canvas.setScale(view.getScale());

							for (int i = 0; i < cells.length; i++)
							{
								graph.drawCell(canvas, cells[i]);
							}
						}
						finally
						{
							canvas.setScale(previousScale);
							canvas.setTranslate(previousTranslate.x,
									previousTranslate.y);
						}
					}
				}
			}
			finally
			{
				temp.destroy();
				view.setEventsEnabled(eventsEnabled);
			}
		}

		return canvas;
	}

	/**
	 * 
	 */
	public static BufferedImage createBufferedImage(mxGraph graph,
			Object[] cells, double scale, Color background, boolean antiAlias,
			mxRectangle clip)
	{
		return createBufferedImage(graph, cells, scale, background, antiAlias,
				clip, new mxGraphics2DCanvas());
	}

	/**
	 * 
	 */
	public static BufferedImage createBufferedImage(mxGraph graph,
			Object[] cells, double scale, final Color background,
			final boolean antiAlias, mxRectangle clip,
			final mxGraphics2DCanvas graphicsCanvas)
	{
		mxImageCanvas canvas = (mxImageCanvas) drawCells(graph, cells, scale,
				clip, new CanvasFactory()
				{
					public mxICanvas createCanvas(int width, int height)
					{
						return new mxImageCanvas(graphicsCanvas, width, height,
								background, antiAlias);
					}

				});

		return (canvas != null) ? canvas.destroy() : null;
	}

	/**
	 * 
	 */
	public static Document createHtmlDocument(mxGraph graph, Object[] cells,
			double scale, Color background, mxRectangle clip)
	{
		mxHtmlCanvas canvas = (mxHtmlCanvas) drawCells(graph, cells, scale,
				clip, new CanvasFactory()
				{
					public mxICanvas createCanvas(int width, int height)
					{
						return new mxHtmlCanvas(mxUtils.createHtmlDocument());
					}

				});

		return canvas.getDocument();
	}

	/**
	 * 
	 */
	public static Document createSvgDocument(mxGraph graph, Object[] cells,
			double scale, Color background, mxRectangle clip)
	{
		mxSvgCanvas canvas = (mxSvgCanvas) drawCells(graph, cells, scale, clip,
				new CanvasFactory()
				{
					public mxICanvas createCanvas(int width, int height)
					{
						return new mxSvgCanvas(mxUtils.createSvgDocument(width,
								height));
					}

				});

		return canvas.getDocument();
	}

	/**
	 * 
	 */
	public static Document createVmlDocument(mxGraph graph, Object[] cells,
			double scale, Color background, mxRectangle clip)
	{
		mxVmlCanvas canvas = (mxVmlCanvas) drawCells(graph, cells, scale, clip,
				new CanvasFactory()
				{
					public mxICanvas createCanvas(int width, int height)
					{
						return new mxVmlCanvas(mxUtils.createVmlDocument());
					}

				});

		return canvas.getDocument();
	}

	/**
	 * 
	 */
	public static abstract class CanvasFactory
	{

		/**
		 * Separates the creation of the canvas from its initialization, when the
		 * size of the required graphics buffer / document / container is known.
		 */
		public abstract mxICanvas createCanvas(int width, int height);

	}

}
