/*
 * @(#)PartitionBSTAnimation.java
 *
 * Last Modified: 9/15/01
 */


 import java.util.*;
 import java.awt.*;


/** *
 	* The Animation object that defines the Paritioning of a node in a BSTTree. The animation builds RotationBSTAnimations
 	* as it goes, keeping only one currently animating and allowing rewinding only to the beginning of
 	* the currrent rotation. <p>
 	*
	* The object restores all values changed in the given nodes, however, if the object
	* is never allowed to finish, the restoring of values becomes impossible. On any exception occuring
	* elsewhere, the object may not restore the conditions correctly. <p>
	*
	* @author  Corey Sanders
	* @version 1.5 9/15/01
 	*/

public class PartitionBSTAnimation extends AbstractAnimation implements AnimationListener {


	/**
	 * Constant that defines the starting location.
	 */
	private final int START = 0;

	/**
	 * Constant that defines the selection of the kth element node.
	 */
	private int SELECTION_ANIMATION = 1;

	/**
	 * Defines the rotating upwards through the tree.
	 */
	private int ROTATE_UP_TREE = 2;

	/**
	 * Private doubles used to hold the current and previous location steps.
	 */
	private double currentLocation = 0.0;
	private double previousLocation = 0.0;



	/**
	 * Refers to the current RotationBSTAnimation being drawn.
	 */
	 RotationBSTAnimation currentRotation;


	/**
	 * Refers to the current RotationBSTAnimation being drawn.
	 */
	 SelectionBSTAnimation currentSelection;

	/**
	 * Holds the node that is replacing.
	 */
	private BSTTree node;

	/**
	 * Holds the node that is replacing.
	 */
	private BSTTree replacingNode;


	/**
	 * Level count, how far down the replacing node is from the erased node.
	 */
	 private int levelCount;

	/**
	 * keySelect count, the kth element to partition to the top.
	 */
	 private int keySelect;

	/**
	 * Counts the amount of comparisons made.
	 */
	private int rotationCount = 0;

	/**
	 * The constructor which initiates the status and prepares the color schemes. The node
	 * which is being deleted must be passed.
	 *
	 * @param node the BSTTree from which the partition takes place.
	 * @param keySelect integer finding the kth node
	 * @param startingCmd the Animation command that this should start.
	 * @param stepTime the time for each step of the Animation. Sets the initial value.
	 */
	public PartitionBSTAnimation(BSTTree node, int keySelect, String startingCmd, int stepTime) {
		super();

		setNode(node);
		setKeySelect(keySelect);

		setStartingCommand(startingCmd);
		setStepTime(stepTime);

	}



	/**
	 * The constructor which initiates the status and sets the color schemes to null. No colors
	 * will be changed using this animation. The node
	 * which is animating must be passed.
	 *
	 * @param node the BSTTree which is deleted during the deletion.
	 * @param keySelect integer finding the kth node
	 */
	public PartitionBSTAnimation(BSTTree node, int keySelect) {
		this(node, keySelect, Animation.PLAY, DEFAULT_STEP);
	}

	/************************/
	/* Accessor methods     */
	/************************/

	/**
	 * Gets the node from which the partitioning takes place.
	 *
	 * @return BSTTree of the node currently being partitioned at the KeySelect.
	 */
	public BSTTree getNode() {
		return node;
	}

	/**
	 * Gets the keySelect of the partition, the kth element.
	 *
	 * @return int keySelect of the partition.
	 */
	public int getKeySelect() {
		return keySelect;
	}

	/**
	 * Gets the node currently being rotated up to replace (not set until after selection occurs).
	 *
	 * @return BSTTree of the ndoe currently being replaced and animated.
	 */
	public BSTTree getReplacingNode() {
		return replacingNode;
	}

	/**
	 * Gets the count of levels for rotated the replacing node to its proper place.
	 *
	 * @return int level count of rotations for the replacing node to its new location.
	 */
	 public int getLevelCount() {
		 return levelCount;
	 }


	/************************/
	/* Mutator methods     */
	/************************/

	/**
	 * Sets the node from which the partitioning takes place.
	 *
	 * @param node BSTTree of the node currently being partitioned at the KeySelect.
	 */
	public void setNode(BSTTree node) {
		this.node = node;
	}

	/**
	 * Sets the keySelect of the partition, the kth element.
	 *
	 * @param keySelect kth element of the partition.
	 */
	public void setKeySelect(int keySelect) {
		this.keySelect = keySelect;
	}


	/**
	 * Sets the node currently being drawn during the Partition.
	 *
	 * @param BSTTree of the node currently being replaced and animated.
	 */
	public void setReplacingNode(BSTTree node) {
		replacingNode = node;
	}

	/**
	 * Sets the count of levels for rotated the replacing node to its proper place. The
	 * count must be the amount of RotateUp calls the node needs to become the highest node in
	 * its tree. The final move does not count as a rotate.
	 *
	 * @param level intt level count of rotations for the replacing node to its new location.
	 */
	 public void setLevelCount(int level) {
		 levelCount = level;
	 }




	/*********************/
	/* 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.
	 *
	 * <b> BSTTreeHead calls: </b>
	 * <ul>
	 * <li><code>partitionTreeType</code> - only called if the animation does not complete (otherwise, the animation rotates it to the top)</li>
	 * <li><code>selectTreeType</code> - called to find the replacing node </li>
	 * </ul>
	 * <b> Other Animation Objects used: </b>
	 * <ul>
	 * <li>SelectionBSTAnimation</li>
	 * <li>RotationBSTTreeAnimation</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 (currentLocation == ROTATE_UP_TREE) {
				// Set play status
				currentRotation.setStatus(Animation.FINISH);

				// Draw rotation animation
				currentRotation.drawAnimation(g2, Animation.FINISH);
			}
			if (currentLocation == SELECTION_ANIMATION) {
				// Set play status
				currentSelection.setStatus(Animation.FINISH);

				// Draw rotation animation
				currentSelection.drawAnimation(g2, Animation.FINISH);
			}


			// Partition has not occured
			if (getNode().getParentTree() != getReplacingNode()) {

				// Do actual partition in tree (Setting the correct final location).
				getNode().getHead().partitionTreeType(getNode(), getKeySelect());
			}
			// Partition has occured
			else {
				messageAction(Animation.FINISH);
			}


			animationAction();

			return;
		}


		// BEGIN status
		if (getStatus().equals(Animation.BEGIN)) {
			// Set starting location
			currentLocation = START;

			animationAction();
			// Original message
			messageAction(Animation.BEGIN + " Partition of "+getNode().getKey().toString()+"\nReplacing with the "+getKeySelect()+"th element.");

			// set starting status
			setStatus(getStartingCommand());

			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)) {

			messageAction(Animation.PLAY);

			// Start location
			if (currentLocation == START) {
				// Construct selectionAnimation
				currentSelection = (SelectionBSTAnimation)((BSTTreeHead)getNode().getHead()).makeSelectionAnimation(getNode(), getKeySelect());
				messageAction(Animation.REDRAW);
				currentSelection.addAnimationListener(this);
				currentSelection.drawAnimation(g2, Animation.PLAY);
				currentLocation = SELECTION_ANIMATION;
			}

			else if (currentLocation == SELECTION_ANIMATION) {

				// Set values for animation
				currentSelection.setStepTime(getStepTime());
				currentSelection.setStep(getStep());

				// Set play status
				currentSelection.setStatus(Animation.PLAY);

				// Draw rotation animation
				currentSelection.drawAnimation(g2, Animation.PLAY);

				if (currentSelection.getStatus().equals(Animation.FINISH)) {

					// Do actual selection in tree, setting the replacing node.
					setReplacingNode((BSTTree) ((BSTTreeHead)getNode().getHead()).selectTreeType(getNode(), getKeySelect()));

					// Set level count as the difference in levels of the nodes.
					setLevelCount(getReplacingNode().getLevel() - getNode().getLevel());

					// More rotations needed
					if (getLevelCount() > 0) {
						// Make rotation animation.
						currentRotation = (RotationBSTAnimation)getReplacingNode().getHead().makeRotationAnimation(getReplacingNode());
						// Pass listeners
						currentRotation.addAnimationListener(this);
						// Decrease level count
						setLevelCount(getLevelCount() - 1);
						// Draw rotation animation (Beginning)
						currentRotation.drawAnimation(g2, Animation.PLAY);

						currentLocation = ROTATE_UP_TREE;
						rotationCount++;

						setStatus(Animation.STEP);
					}
					// No more rotations
					else {
						setStatus(Animation.FINISH);
					}
				}
			}
			else if (currentLocation == ROTATE_UP_TREE) {
				// Set values for animation
				currentRotation.setStepTime(getStepTime());
				currentRotation.setStep(getStep());

				// Set play status
				currentRotation.setStatus(Animation.PLAY);

				// Draw rotation animation
				currentRotation.drawAnimation(g2, Animation.PLAY);

				if (currentRotation.getStatus().equals(Animation.FINISH)) {

					// Rotate up count (more rotations)
					if (getLevelCount() > 0) {

						// Make next rotation
						currentRotation = (RotationBSTAnimation)getReplacingNode().getHead().makeRotationAnimation(getReplacingNode());
						// Pass listeners
						currentRotation.addAnimationListener(this);
						// Decrease level count
						setLevelCount(getLevelCount()-1);
						// Draw rotation animation
						currentRotation.drawAnimation(g2, Animation.PLAY);
						// Keep track of rotation Count
						rotationCount++;

						setStatus(Animation.STEP);


					}
					// Final move, no more rotations
					else {
						setStatus(Animation.FINISH);

					}

				}
			}
		}


		// REWIND status
		if (getStatus().equals(Animation.REWIND)) {
			messageAction(Animation.REWIND);

			if (currentLocation == SELECTION_ANIMATION) {

				// Set values for animation
				currentSelection.setStepTime(getStepTime());
				currentSelection.setStep(getStep());

				// Set play status
				currentSelection.setStatus(Animation.REWIND);

				// Draw rotation animation
				currentSelection.drawAnimation(g2, Animation.REWIND);

				if (currentSelection.getStatus().equals(Animation.PAUSE)) {
					messageAction("Cannot Rewind : Beginning of Selection");
					// Pause status
					setStatus(Animation.PAUSE);
				}
			}
			if (currentLocation == ROTATE_UP_TREE) {
				// Set values for animation
				currentRotation.setStepTime(getStepTime());
				currentRotation.setStep(getStep());

				// Set play status
				currentRotation.setStatus(Animation.REWIND);

				// Draw rotation animation
				currentRotation.drawAnimation(g2, Animation.REWIND);

				if (currentRotation.getStatus().equals(Animation.PAUSE)) {
					messageAction("Cannot Rewind : Beginning of Rotation");
					// Pause status
					setStatus(Animation.PAUSE);

				}
			}

		}

		// PAUSE status
		if (getStatus().equals(Animation.PAUSE)) {

			if (currentLocation == SELECTION_ANIMATION) {

				// Set play status
				currentSelection.setStatus(Animation.PAUSE);

				// Draw rotation animation
				currentSelection.drawAnimation(g2, Animation.PAUSE);
			}

			if (currentLocation == ROTATE_UP_TREE) {

				currentRotation.setStatus(Animation.PAUSE);
				// Draw rotation animation
				currentRotation.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)) {
			// Finish message

			messageAction(Animation.FINISH);
			messageAction("*--------Partition of "+getNode().getKey().toString()+"--------*\nReplaced with "+getKeySelect()+"th element\nReplaced by: "+
			getReplacingNode().getKey().toString()+"\nRotations: "+rotationCount);



		}


		// 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.PARTITION_BST_ANIMATION, cmd, description, currentLocation / (double)ROTATE_UP_TREE);
	}


	/**
	 * 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) {

		if (e.getStatus().equals(Animation.ANIMATION_MESSAGE)) {
			messageAction(e.getAnimationDescription());
		}
		if (e.getStatus().equals(Animation.STEP)) {
			animationAction(Animation.STEP, null);
		}

	}

}
