 /*
  * @(#)DrawingKey.java
  *
  * Last Modified: 9/15/01
  */

 import java.util.*;
 import java.lang.*;
 import java.awt.*;
 import java.awt.font.*;
 import java.awt.geom.*;

/**
 * <code>DrawingKey</code> implements <code>DrawableKey</code> for it is a specific type that
 * draws Object keys. Its constructor needs the Object key it will draw.
 *
 * @see DrawableKey
 *
 * @author Corey Sanders
 * @version 2.1 9/15/01
 */
 public class DrawingKey extends Object implements DrawableKey {


	/**
	 * Previous Drawn Shape
	 */
	private Shape currentShape = null;

	/**
	 * Previous Drawn Shape
	 */
	private Shape originalShape = null;

	/**
	 * Current Transform
	 */
	private AffineTransform currentTransform = null;

	/**
	 * The current drawing settings of the node.
	 */
	private KeySettings currentSettings = null;

	/**
	 * The previous drawing settings of the node.
	 */
	private KeySettings previousSettings = null;

	/**
	 * The count for how many total saves the current Node has been called
	 * (to save the NodeSettings).
	 */
	private int countSaves = 0;

	/**
	 * The Object key used to draw this key, using the method <code>toString</code>.
	 */
	private Object key;

	/**
	 * Constructor that instantiates the <code>DrawingIntegerKey</code> using the given Object k.
	 * @param k the <code>Object</code> drawn to the screen.
	 */
 	public DrawingKey(Object k) {
		setKey(k);
	}

 	/**
     * Returns a copy of this <code>AffineTransform</code> object.
     * @return an <code>Object</code> that is a copy of this
     * <code>AffineTransform</code> object.
     */
    public Object clone() {

		try {
		    return super.clone();
		}
		catch (CloneNotSupportedException e) {
		    // this shouldn't happen, since we are Cloneable
		    throw new InternalError();
		}
    }



	/**
	 * Sets the <code>Object</code> key of the <code>DrawableKey</code>.
	 *
	 * @param k <code>Object</code> key for use in drawing the key.
	 */
	 public void setKey(Object k) {
		 key = k;
	 }

	/**
	 * Gets the <code>Object</code> key of the <code>DrawableKey</code>.
	 *
	 * @return <code>Object</code> key used for drawing the key.
	 */
	 public Object getKey() {
		 return key;
	 }

	/**
	 * Sets the <code>KeySettings</code> of the <code>DrawableKey</code>.
	 * These settings are used for drawing the key in a given Graphics2D.
	 *
	 * @param s <code>KeySettings</code> for use in drawing the key.
	 */
	public void setSettings(KeySettings s) {

		KeySettings newSettings = (KeySettings)s.clone();

		// No previous saved settings
		if (!isSettingsSaved()) {
			// null previous settings
			previousSettings = null;
			currentSettings = newSettings;

			// Reset saves
			countSaves = 0;
		}
		else {
			// Set previous settings as s, because currently the settings are saved.
			previousSettings = newSettings;
		}
	}

	/**
	 * Sets the <code>KeySettings</code> of the <code>DrawableKey</code>.
	 * These settings are used for drawing the key in a given Graphics2D.
	 *
	 * @param s <code>KeySettings</code> for use in drawing the key.
	 */
	public void setKeySettings(KeySettings s) {
		currentSettings = (KeySettings)s.clone();
	}

	/**
	 * Gets the <code>KeySettings</code> of the key.
	 *
	 * @return <code>KeySettings</code> for use in drawing the key.
	 */
	public KeySettings getSettings() {
		return currentSettings;
	}

	/**
	 * Returns true if the <code>KeySettings are currently saved for the DrawingTree.
	 *
	 * @return true if the settings are saved.
	 */
	public boolean isSettingsSaved() {
		// Saved if previousSettings is not null.
		return (previousSettings != null);
	}

	/**
	 * Saves the settings for the tree, incrementing the count of saves by one and setting the
	 * previous settings accordingly.
	 */
	public void saveSettings() {
		countSaves++;
		if (previousSettings == null) {
			previousSettings = (KeySettings)currentSettings.clone();
		}
	}


	/**
	 * Restores the settings for the tree, decrementing the count of saves by one.
	 * If the settings have been restored completely, than the KeySettings for the tree
	 * are set accordingly.
	 */
	public void restoreSettings() {
		countSaves--;
		if (countSaves <= 0) { // Completely Restored
			currentSettings = previousSettings;
			previousSettings = null;
			countSaves = 0;
		}

	}

	/**
	 * Draws the key filling the given Graphics2D. The method completely fills the Graphics2D passed
	 * to it, using no AffineTransforms.
	 *
	 * @param g2 Graphics2D that fills with the key.
	 */
	public void fillKey(Graphics2D g2) {

		// Get bounds of the Graphics2D
		Rectangle bounds = g2.getClipBounds();

		// Find the drawingX, Y, Width, and Height, to fill the entire Graphics.
		double drawingX = Math.ceil(bounds.getX() + getSettings().getKeyOutlineStrokeWidth());
		double drawingY = Math.ceil(bounds.getY() + getSettings().getKeyOutlineStrokeWidth());
		double drawingWidth = Math.floor(bounds.getWidth() - drawingX - getSettings().getKeyOutlineStrokeWidth());
		double drawingHeight = Math.floor(bounds.getHeight() - drawingY - getSettings().getKeyOutlineStrokeWidth());

		AffineTransform scaleTransform = AffineTransform.getScaleInstance(drawingWidth, drawingHeight);
		AffineTransform translateTransform = AffineTransform.getTranslateInstance(drawingX, drawingY);

		translateTransform.concatenate(scaleTransform);

		drawKey(g2, translateTransform);

	}

	/**
	 * Draws the key in the given Graphics2D, using the AffineTransform passed to it.
	 * The method uses the AffineTransform using no previous transforms.
	 *
	 * @param g2 Graphics2D that this fills with the key.
	 * @param a <code>AffineTransform</code> that transforms the key, assuming no previous transforms occur.
	 */
	public void drawKey(Graphics2D g2, AffineTransform a) {

		if (originalShape == null)
			setOriginalShape(g2);


		if (getKey() instanceof Character) {
			if ( (((Character)getKey()).charValue() == 'i') || (((Character)getKey()).charValue() == 'I') ) {
				double oldScale = a.getScaleX();
				// Find the drawingX, Y, Width, and Height, to fill the entire Graphics.
				a.scale(1.0/5.0, 1);
				a.translate(( (2*oldScale)/5.0) / a.getScaleX(), 0);
			}
		}

		currentShape = a.createTransformedShape(originalShape);

		drawKey(g2);
	}



	/**
	 * Draws the key, first setting all values for the Graphics2D g2.
	 * The drawing is done using the current Shape, defined generally in <code>drawKey</code> or
	 * <code>fillKey</code>.
	 *
	 * @param g2 Graphics2D that the key is painted to.
	 */
	public void drawKey(Graphics2D g2) {


		// Set graphics information
		g2.setComposite(getSettings().getKeyComposite());

		g2.setPaint(getSettings().getKeyFillPaint());
		g2.fill(currentShape);

		g2.setStroke(getSettings().getKeyOutlineStroke());
		g2.setPaint(getSettings().getKeyOutlinePaint());

		g2.draw(currentShape);
	}

	/**
	 * sets the Original Shape using the g2 simply for the <code>FontRenderContext</code>.
	 * @param g2 Graphics2D that FontRenderContext is aquired from.
	 */
	private void setOriginalShape(Graphics2D g2) {
		String key = getKey().toString();

		// Makes a textLayout using the key.
		TextLayout text = new TextLayout(key, getSettings().getFont(), g2.getFontRenderContext());

		// Makes a Shape
		Shape keyShape = text.getOutline(null);
		// Get Bounds of the Shape
		Rectangle2D keyShapeBounds = keyShape.getBounds2D();


		// Scales to size of 1 and transforms to (0,0)
		AffineTransform scaleTransform = AffineTransform.getScaleInstance(1 / keyShapeBounds.getWidth() , 1 / keyShapeBounds.getHeight());
		AffineTransform translateTransform = AffineTransform.getTranslateInstance((-keyShapeBounds.getX()), (-keyShapeBounds.getY()));


		scaleTransform.concatenate(translateTransform);

		originalShape = scaleTransform.createTransformedShape(keyShape);

	}

}
