/*
 * @(#)BalanceBSTAnimation.java
 *
 * Last Modified: 9/15/01
 */

 import java.util.*;
 import java.awt.*;

/** *
 	* The Animation object that defines the Balancing of a node in a BSTTree. Two constructors exist,
 	* one setting the starting string command and step time (preferred use). The other uses defaults.
 	* The animation builds <code>PartitionBSTAnimation</code>s from the head node as it goes, keeping
 	* only one currently animating and allowing rewinding only to the previous
 	* rotation. The color schemes are set by the head call. The call is
 	* <code>makePartitionAnimation</code>. <p>
	*
	* @author  Corey Sanders
	* @version 1.4 9/15/01
 	*/

public class BalanceBSTAnimation extends AbstractAnimation implements AnimationListener {


	/**
	 * Refers to the current PartitionBSTAnimation being drawn.
	 */
	 PartitionBSTAnimation currentPartition;

	/**
	 * Holds the node that is replacing.
	 */
	private BSTTree node;

	/**
	 * Holds the node that is replacing.
	 */
	private BSTTree replacingNode;


	/**
	 * Private doubles used to hold the current location steps.
	 */
	private double currentLocation = 0.0;

	/**
	 * The constructor which initiates the status. The node
	 * which is being balanced must be passed.
	 *
	 * @param node the BSTTree from which the balancing takes place.
	 * @param startingCmd the Animation command that this should start.
	 * @param stepTime the time for each step of the Animation. Sets the initial value.
	 */
	public BalanceBSTAnimation(BSTTree node, String startingCmd, int stepTime) {
		super();

		setNode(node);

		setStartingCommand(startingCmd);
		setStepTime(stepTime);

	}


	/**
	 * The constructor which initiates the status and sets the starting command and step time
	 *
	 * @param node the BSTTree which is balanced.
	 */
	public BalanceBSTAnimation(BSTTree node) {
		this(node, Animation.PLAY, DEFAULT_STEP);
	}

	/************************/
	/* Accessor methods     */
	/************************/

	/**
	 * Gets the node from which the balancing takes place.
	 *
	 * @return BSTTree of the node currently being balanced.
	 */
	public BSTTree getNode() {
		return node;
	}

	/**
	 * Gets the node currently being replaced by the node being balanced (not set until after partition occurs).
	 *
	 * @return BSTTree of the node currently being replaced and animated.
	 */
	public BSTTree getReplacingNode() {
		return replacingNode;
	}


	/************************/
	/* Mutator methods     */
	/************************/

	/**
	 * Sets the node from which the balancing takes place.
	 *
	 * @param node BSTTree of the node currently being balanced.
	 */
	public void setNode(BSTTree node) {
		this.node = node;
	}

	/**
	 * Sets the node that will replace the balanced node.
	 *
	 * @param BSTTree of the node replacing the balancing node.
	 */
	public void setReplacingNode(BSTTree node) {
		replacingNode = node;
	}

	/*********************/
	/* Animation methods */
	/*********************/

	/**
	 * Draws the animation of the next step, using the status of the animation (Animation.PLAY, Animation.PAUSE and so forth).
	 * After completing the drawing, the Animation sends an AnimationEvent to all its listeners, indicating
	 * any information that the listerners may wish to use. <p>
	 * <b> BSTTreeHead calls: </b>
	 * <ul>
	 * <li><code>selectTreeType</code> - to locate the replacing node without moving it</li>
	 * <li><code>balance</code> - to make recursive calls once the animation has completed</li>
	 * </ul>
	 * <b> Other Animation Objects used: </b>
	 * <ul>
	 * <li>PartitionBSTAnimation</li>
	 * </ul>
	 *
	 * @param g2 the graphics to which the animation step should be drawn.
	 * @param startingStatus the status used as the starting command of animation, if needed.
	 */
	public void drawAnimation(Graphics2D g2, String startingStatus) {
		setStartingCommand(startingStatus);

		// Starting Finish status (set prior to call)
		if (getStatus().equals(Animation.FINISH)) {
			if (currentPartition != null) {
				currentPartition.setStatus(Animation.FINISH);
				currentPartition.drawAnimation(g2, Animation.FINISH);
			}
			else {
				// Set replacing node (Using selectTree)
				setReplacingNode((BSTTree)((BSTTreeHead)getNode().getHead()).selectTreeType(getNode(), getNode().size() / 2));
			}

			if (getReplacingNode() == null) {
				System.out.println("NULL");
				return;
			}

			// Do actual balancing in tree (Setting the correct final location).
			getNode().getHead().balanceTreeType(getReplacingNode());
		}


		// BEGIN status
		if (getStatus().equals(Animation.BEGIN)) {
			animationAction();
			// Beginning message
			messageAction(Animation.BEGIN + " Balance of "+getNode().getKey().toString());

			// set starting status
			setStatus(getStartingCommand());

			// Set partition
			currentPartition =(PartitionBSTAnimation)((BSTTreeHead)getNode().getHead()).makePartitionAnimation(getNode(), getNode().size() / 2);
			// Set replacing node (Using selectTree)
			setReplacingNode((BSTTree)((BSTTreeHead)getNode().getHead()).selectTreeType(getNode(), getNode().size() / 2));

			currentPartition.addAnimationListener(this);

			currentPartition.drawAnimation(g2, getStatus());

			return;

		}


		// Currently on a step and no changes have occured. Return to startingStatus
		if (getStatus().equals(Animation.STEP)) {
			setStatus(getStartingCommand());
		}


		// PLAY status
		if (getStatus().equals(Animation.PLAY)) {
			// Set values for animation
			currentPartition.setStepTime(getStepTime());
			currentPartition.setStep(getStep());

			// Set play status
			currentPartition.setStatus(Animation.PLAY);

			// Draw rotation animation
			currentPartition.drawAnimation(g2, Animation.PLAY);

			messageAction(Animation.PLAY);

			// Finished with current partition
			if (currentPartition.getStatus().equals(Animation.FINISH)) {
				setStatus(Animation.FINISH);
			}

		}


		// REWIND status
		if (getStatus().equals(Animation.REWIND)) {
			// Set values for animation
			currentPartition.setStepTime(getStepTime());
			currentPartition.setStep(getStep());

			// Set play status
			currentPartition.setStatus(Animation.REWIND);

			// Draw rotation animation
			currentPartition.drawAnimation(g2, Animation.REWIND);

			messageAction(Animation.REWIND);
		}

		// PAUSE status
		if (getStatus().equals(Animation.PAUSE)) {
			// Set play status
			currentPartition.setStatus(Animation.PAUSE);

			// Draw rotation animation
			currentPartition.drawAnimation(g2, Animation.PAUSE);


			messageAction(Animation.PAUSE);
		}

		// STOP status
		if (getStatus().equals(Animation.STOP)) {
			messageAction(Animation.STOP);
			return;
		}


		// FINISH status
		if (getStatus().equals(Animation.FINISH)) {
			messageAction(Animation.FINISH);
			messageAction("*--------Balance of "+getNode().getKey().toString()+"--------*\nReplaced by "+getReplacingNode().getKey().toString());

			animationAction();

			messageAction(Animation.REDRAW);


			return;

		}


		// Call listeners
		animationAction();

	}


	/**
	 * Calls all of the listeners of the current Animation and passed information regarding the
	 * progress and status of the current Animation. Additionally, the id of the type of animation is
	 * passed. Within, the <code>animationEventPerformed</code> method is called.
	 *
	 * @param cmd String Animation command passed instead of the current Status.
	 * @param description String description for messages.
	 */
	protected void animationAction(String cmd, String description) {
		super.animationAction(AnimationEvent.BALANCE_BST_ANIMATION, cmd, description, currentLocation);
	}


	/**
	 * Implements <code>AnimationListener</code> which requires the following method.
	 * The only status of animation it listens for is <code>Animation.ANIMATION_MESSAGE</code>, to pass
	 * the message on.
	 *
	 * @param e AnimationEvent that represents the information of the Animation.
	 */
	public void animationEventPerformed(AnimationEvent e) {

		// set location each progress call.
		currentLocation = e.getProgress();

		if (e.getStatus().equals(Animation.ANIMATION_MESSAGE)) {
			messageAction(e.getAnimationDescription());
		}
		if (e.getStatus().equals(Animation.STEP)) {
			animationAction(Animation.STEP, null);
		}

	}

}
