/*
 * @(#)DisplayChangeAnimation.java
 *
 * Last Modified: 9/01/02
 */

 import java.util.*;
 import java.awt.*;
 import java.awt.geom.*;

/** *
 	* The Animation object that defines the Rotation of a BSTTree. Two constructors exist,
 	* one setting the animator and animation color Schemes, one setting those to defaults. <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.
	*
	* @author  Corey Sanders
	* @version 1.4 9/01/02
 	*/

public class DisplayChangeAnimation extends AbstractAnimation {


	/**
     * The Default step size used in the animation (16).
	 */
	public final static int DEFAULT_STEP = 16;


	/**
	 * Constant that defines the starting location.
	 */
	private final int START = 0;

	/**
	 * Constant the defines the final moving location.
	 */
	private final int MOVE = 1;

	/**
	 * Private doubles used to hold the current and previous location steps.
	 */
	private double currentLocation = 0.0;

	/**
	 * The previous location of the animation.
	 */
	private double previousLocation;

	/**
	 * The moving nodes for the display change.
	 */
	private MovingBSTTreeAnimation movingTreeNodes;



	/**
	 * The int defining the change in display.
	 */
	 private int displayChange;

	/**
	 * The linked list containing all of the right null links nodes
	 */
	 private LinkedList rightNullNodes;

	/**
	 * The linked list containing all of the left null links nodes
	 */
	 private LinkedList leftNullNodes;

	/**
	 * Color Scheme used as the original.
	 */
	private NodeSettings nodeOriginalScheme;

	/**
	 * Color Scheme used for the original scheme of the key.
	 */
	 private KeySettings keyOriginalScheme;

	/**
	 * BSTTreeHead which is the head of the tree whose display is chaning.
	 */
	 private BSTTreeHead head;


	/**
	 * The constructor which initiates the status and sets the color Schemes to default. Also sets
	 * starting command to Animation.PLAY, and sets the step time to the default.
	 *
	 * @param head the BSTTreeHead head of the tree whose display is changing.
	 * @param displayChange the new kind of display, according to BSTTreeHead.SECT_DISPLAY or BSTTreeHead.BINARY_DISPLAY
	 */
	public DisplayChangeAnimation(BSTTreeHead head, int displayChange) {
		this(head, displayChange, null, null, Animation.PLAY, DEFAULT_STEP);
	}

	/**
	 * The constructor which initiates the status and prepares the color schemes.
	 *
	 * @param head the BSTTreeHead head of the tree whose display is changing.
	 * @param displayChange the new kind of display, according to BSTTreeHead.SECT_DISPLAY or BSTTreeHead.BINARY_DISPLAY
	 * @param NodeOriginalAnimationScheme original scheme for the root.
	 * @param KeyOriginalScheme original scheme for the key.
	 * @param startingCmd the Animation command that should start.
	 * @param stepTime the time for each step of the Animation. Sets the initial value.
	 */
	public DisplayChangeAnimation(BSTTreeHead head, int displayChange, NodeSettings NodeOriginalScheme, KeySettings KeyOriginalScheme, String startingCmd, int stepTime) {
		super();

		// Set defaults if no color schemes exist
		if (NodeOriginalScheme == null) {
			NodeOriginalScheme = new NodeSettings();
		}

		if (KeyOriginalScheme == null) {
			KeyOriginalScheme = new KeySettings();
		}

		setNodeOriginalScheme((NodeSettings)NodeOriginalScheme.clone());
		setKeyOriginalScheme((KeySettings)KeyOriginalScheme.clone());

		setLeftNullNodes(new LinkedList());
		setRightNullNodes(new LinkedList());

		// Sets the root and child nodes
		setHead(head);

		setDisplayChange(displayChange);

		setMovingTreeNodes(new MovingBSTTreeAnimation(getNodeOriginalScheme(), getKeyOriginalScheme()));

		setStartingCommand(startingCmd);
		setStepTime(stepTime);
	}

	/************************/
	/* Accessor methods     */
	/************************/

	/**
	 * Gets the displayChange for the display change animation.
	 *
	 * @return displayChange integer defined in BSTTreeHead.
	 */
	public int getDisplayChange() {
		return displayChange;
	}


	/**
	 * Gets the right null link nodes.
	 *
	 * @return LinkedList for the right null link nodes.
	 */
	private LinkedList getRightNullNodes() {
		return rightNullNodes;
	}

	/**
	 * Gets the left null link nodes.
	 *
	 * @return LinkedList for the left null link nodes.
	 */
	private LinkedList getLeftNullNodes() {
		return leftNullNodes;
	}

	/**
	 * Gets the head for the tree whose display is changing.
	 *
	 * @return BSTTreeHead for the entire tree display change.
	 */
	private BSTTreeHead getHead() {
		return head;
	}

	/**
	 * Gets the MovingBSTTreeAnimation for the tree whose display is changing.
	 *
	 * @return MovingBSTTreeAnimation for the entire tree display change.
	 */
	public MovingBSTTreeAnimation getMovingTreeNodes() {
		return movingTreeNodes;
	}

	/**
	 * Gets the NodeSettings for the original node scheme for the rotation.
	 *
	 * @return NodeSettings for the original node scheme.
	 */
	public NodeSettings getNodeOriginalScheme() {
		return nodeOriginalScheme;
	}

	/**
	 * Gets the KeySettings for the original scheme of the key.
	 *
	 * @return KeySettings for the original key scheme.
	 */
	public KeySettings getKeyOriginalScheme() {
		return keyOriginalScheme;
	}

	/************************/
	/* Mutator methods     */
	/************************/


	/**
	 * Sets the displayChange for the animation.
	 *
	 * @param displayChange the int defined within BSTTreeHead (SECT_DISPLAY or BINARY_DISPLAY).
	 */
	public void setDisplayChange(int displayChange) {
		this.displayChange = displayChange;
	}


	/**
	 * Sets the right null link nodes.
	 *
	 * @param LinkedList for the right null link nodes.
	 */
	private void setRightNullNodes(LinkedList rightNullNodes) {
		this.rightNullNodes = rightNullNodes;
	}

	/**
	 * Sets the left null link nodes.
	 *
	 * @param leftNullNodes LinkedList for the left null link nodes.
	 */
	private void setLeftNullNodes(LinkedList leftNullNodes) {
		this.leftNullNodes = leftNullNodes;
	}

	/**
	 * Sets the head for the tree whose display is changing.
	 *
	 * @param head BSTTreeHead for the display change.
	 */
	private void setHead(BSTTreeHead head) {
		this.head = head;
	}

	/**
	 * Sets the MovingBSTTreeAnimation for the tree whose display is changing.
	 *
	 * @param movingTreeNodes MovingBSTTreeAnimation for the entire tree display change.
	 */
	public void setMovingTreeNodes(MovingBSTTreeAnimation movingTreeNodes) {
		this.movingTreeNodes = movingTreeNodes;
	}


	/**
	 * Sets the NodeSettings for the original scheme for the rotation.
	 *
	 * @param scheme NodeSettings for the original scheme.
	 */
	public void setNodeOriginalScheme(NodeSettings scheme) {
		nodeOriginalScheme = scheme;
	}

	/**
	 * Sets the KeySettings for the original scheme of the key during rotation.
	 *
	 * @param scheme KeySettings for the original of the key.
	 */
	public void setKeyOriginalScheme(KeySettings scheme) {
		keyOriginalScheme = scheme;
	}

	/*****************************/
	/* Entire Animators Mutators */
	/*****************************/

	/**
	 * Creates the moving nodes corresponding to the entire tree.
	 */
	private void createFinalTreeMovingNodes() {
		// Intialize
		//finalTreeMovingNodes = new MovingBSTTreeAnimation();

		// Top node
		BSTTree topNode = (BSTTree)getHead().getChild();

		//MovingBSTTree topMovingNode = new MovingBSTTree(topNode);
		MovingBSTTree topMovingNode = new MovingBSTTree(topNode);


		if (topNode.isAnimateDrawing()) {

			// Add grandchild to descendant animation
			getMovingTreeNodes().add(topMovingNode, topNode);

			topMovingNode.setMovePosition(MovingBSTTree.FOLLOW_NODE);

			// Set listeners
			getMovingTreeNodes().addAnimationListener(topNode);
			(topNode).addAnimator(getMovingTreeNodes());

		}

		// Add all children
		MovingBSTTree left = addTreeNode((BSTTree)topNode.getLeftTree(), getMovingTreeNodes(), MovingBSTTree.FOLLOW_NODE, topMovingNode);
		MovingBSTTree right = addTreeNode((BSTTree)topNode.getRightTree(), getMovingTreeNodes(), MovingBSTTree.FOLLOW_NODE, topMovingNode);



		if (left != null) {
			topMovingNode.setLeftTree(left);
		}
		else {
			getLeftNullNodes().add(topMovingNode);
		}

		if (right != null) {
			topMovingNode.setRightTree(right);
		}
		else {
			getRightNullNodes().add(topMovingNode);
		}

	}

	/**
	 * Adds all children nodes to the animator list, setting the Moving node as its parent. The move position
	 * defines the moving of the new node.
	 *
	 * @param node the node which the MovingBSTTree made imitates.
	 * @param animator the MovingBSTTreeAnimation to which the new MovingBSTTree node is added.
	 * @param movePostion the moving position of the new MovingBSTTree.
	 * @return MovingBSTTree the new tree moving node.
	 */
	private MovingBSTTree addTreeNode(BSTTree node, MovingBSTTreeAnimation animator, int movePosition, MovingBSTTree parent) {
		if (node.isEmpty())
			return null;

		// Create new MovingBSTTree
		MovingBSTTree movingNode = new MovingBSTTree(node, parent);

		if (node.isAnimateDrawing()) {
			// Sets the move position
			movingNode.setMovePosition(movePosition);

			// Adds the animator to the MovingBSTTreeAnimation
			animator.add(movingNode, node);

			// Adds the listener to the animation and the animation to the node.
			animator.addAnimationListener(node);
			node.addAnimator(animator);

			MovingBSTTree left = addTreeNode((BSTTree)node.getLeftTree(), animator, MovingBSTTree.FOLLOW_NODE, movingNode);
			MovingBSTTree right = addTreeNode((BSTTree)node.getRightTree(), animator, MovingBSTTree.FOLLOW_NODE, movingNode);

			// Recursively goes through children
			if (left != null) {
				movingNode.setLeftTree(left);
			}
			else {
				getLeftNullNodes().add(movingNode);
			}

			if (right != null) {
				movingNode.setRightTree(right);
			}
			else {
				getRightNullNodes().add(movingNode);
			}
		}

		movingNode.setCurrentTransform(movingNode.getStartTransform());

		return movingNode;
	}


	/**
     * 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 <code>AnimationEvent</code> to all its listeners, indicating
	 * any information that the listerners may wish to use. <p>
	 * The starting status used for the animation is the one previously defined.
 	 *
 	 * @param g2 Graphics2D to which the graphics are drawn.
 	 */
	public void drawAnimation(Graphics2D g2) {
		drawAnimation(g2, getStartingCommand());
	}


	/**
	 * 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>rotateUpTreeType</code> - called when animation does completes </li>
	 * </ul>
	 * <b> Other Animation Objects used: </b>
	 * <ul>
	 * <li>MovingBSTTreeAnimation</li>
	 * </ul>
	 *
	 * @param g2 the graphics to which the animation step should be drawn.
	 * @param startingStatus sent to the animators
	 */
	public void drawAnimation(Graphics2D g2, String startingStatus) {

		setStartingCommand(startingStatus);

		// Sets the animation step size.
		getMovingTreeNodes().setStepSize(getStepSize());

		// Sets the animation step size.
		//getMovingTreeNodes().setStep(getStep());
		getMovingTreeNodes().setStep(false);

		// FINISH status (set from outside)
		if (getStatus().equals(Animation.FINISH)) {

			// Actual rotation
			getHead().setDisplay(getDisplayChange());
		}

		// BEGIN status
		if (getStatus().equals(Animation.BEGIN)) {

			currentLocation = 0.0;
			previousLocation = 0.0;

			// Create the final moving nodes
			createFinalTreeMovingNodes();
			//getMovingTreeNodes().drawAnimation(g2, startingStatus);
			//getMovingTreeNodes().setAnimationScheme(getNodeOriginalScheme(), getKeyOriginalScheme());

			// Actual display change
			getHead().setDisplay(getDisplayChange());

			// REDRAW message
			messageAction(Animation.REDRAW);

			if (displayChange == BSTTreeHead.BINARY_DISPLAY) {
				for(int i=0; i<getLeftNullNodes().size(); i++) {
					((BSTTree)getLeftNullNodes().get(i)).getSettings().setLeftLinkComposite((Composite)AlphaComposite.getInstance(NodeSettings.nodeRuleDefault,0));
				}
				for(int i=0; i<getRightNullNodes().size(); i++) {
					((BSTTree)getRightNullNodes().get(i)).getSettings().setRightLinkComposite((Composite)AlphaComposite.getInstance(NodeSettings.nodeRuleDefault,0));
				}
			}

			getMovingTreeNodes().drawAnimation(g2, startingStatus);

			animationAction();

			setStatus(startingStatus);

			return;
		}


		String action = Animation.PLAY;

		if (getStatus().equals(Animation.PLAY)) {
			action = Animation.PLAY;

			previousLocation = currentLocation;

			if(getStep()) { // Skip middle animation steps.
				currentLocation = Math.ceil(currentLocation) + getStepSize();
			}
			else { // Normal step
				currentLocation += getStepSize();
			}


		}


		// REWIND status
		if (getStatus().equals(Animation.REWIND)) {
			action = Animation.REWIND;

			previousLocation = currentLocation;

			if(getStep()) { // Skip middle animation steps.
				currentLocation = Math.floor(currentLocation)-getStepSize();
			}
			else { // Normal step
				currentLocation -= getStepSize();
			}
		}

		// Beginning of Animation
		if (currentLocation <= 0) {
			setStatus(Animation.PAUSE);
			currentLocation = 0;
		}

		// PAUSE status
		if (getStatus().equals(Animation.PAUSE)) {
			action = Animation.PAUSE;
		}

		// STOP status
		if (getStatus().equals(Animation.STOP)) {
			messageAction(Animation.STOP);
			// Nothing happens

			// Call listeners
			animationAction();
			return;
		}

		messageAction(action);

		float decreasingFromOne = .6F - ((float)currentLocation) ;
		float increasingToOne = ((float)currentLocation);

		if (increasingToOne > 1) {
			increasingToOne = 1;
		}
		if (increasingToOne < 0) {
			increasingToOne = 0;
		}
		if (decreasingFromOne < 0) {
			decreasingFromOne = 0;
		}

		//Moving
		if (currentLocation < MOVE) {

			getMovingTreeNodes().setStatus(action);

			getMovingTreeNodes().makeAnimation(g2, action);

			if (displayChange == BSTTreeHead.BINARY_DISPLAY) {
				for(int i=0; i<getLeftNullNodes().size(); i++) {
					((BSTTree)getLeftNullNodes().get(i)).getSettings().setLeftLinkComposite((Composite)AlphaComposite.getInstance(NodeSettings.nodeRuleDefault, increasingToOne));
				}
				for(int i=0; i<getRightNullNodes().size(); i++) {
					((BSTTree)getRightNullNodes().get(i)).getSettings().setRightLinkComposite((Composite)AlphaComposite.getInstance(NodeSettings.nodeRuleDefault, increasingToOne));
				}
			}
			else {
				for(int i=0; i<getLeftNullNodes().size(); i++) {
					((BSTTree)getLeftNullNodes().get(i)).getSettings().setLeftLinkComposite((Composite)AlphaComposite.getInstance(NodeSettings.nodeRuleDefault, decreasingFromOne));
					((MovingBSTTree)getLeftNullNodes().get(i)).drawFollowNode(g2, currentLocation, true);
				}

				for(int i=0; i<getRightNullNodes().size(); i++) {
					((BSTTree)getRightNullNodes().get(i)).getSettings().setRightLinkComposite((Composite)AlphaComposite.getInstance(NodeSettings.nodeRuleDefault, decreasingFromOne));
					((MovingBSTTree)getRightNullNodes().get(i)).drawFollowNode(g2, currentLocation, true);
				}
			}

			getMovingTreeNodes().drawAnimation(g2, action);
		}

		// Animation is completed
		if (currentLocation >= MOVE) {
			// Set step, to finish the animation
			getMovingTreeNodes().setStep(true);
			// Draw
			getMovingTreeNodes().makeAnimation(g2, Animation.PLAY);
			getMovingTreeNodes().drawAnimation(g2, Animation.PLAY);

			// Set own status to FINISH
			setStatus(Animation.FINISH);
		}

		// FINISH status
		if (getStatus().equals(Animation.FINISH)) {
			// Completion messages
			messageAction(Animation.FINISH);

			// Set all status to FINISH
			getMovingTreeNodes().setStatus(Animation.FINISH);
			getMovingTreeNodes().makeAnimation(g2, startingStatus);
			// Draw
			getMovingTreeNodes().drawAnimation(g2, startingStatus);
		}


		// Call listeners
		animationAction();
	}
}


