/*
 * Created on 01.03.2004 16:49:57
 *
 * Multimediale Algorithmen und Datenstrukturen Assessments
 */
package mauda.treeoperationview;

import mauda.*;
import mauda.operation.*;
import mauda.images.ImageHandler;
import mauda.undoredo.UndoRedoElement;
import mauda.undoredo.UndoRedoInfo;
import mauda.undoredo.UndoRedoUnit;

import javax.swing.*;
import javax.swing.tree.*;
import java.awt.*;
import java.util.Vector;
import java.util.Enumeration;
import java.awt.event.*;
/**
 * Provides methods for displaying and manipulating a view of the
 * operations as a tree.
 * 
 * @author Markus Krebs
 */
public class TreeOperationView implements ExerciseUpdateListener, MouseListener, ActionListener {

	/**
	 * ID for displaying the operation-text without e.g. the word
	 * (template) that marks a operation as non-executed.
	 */
	public static final int NORMAL_DISPLAY_MODE = 0;
	/**
	 * ID for displaying the operation-text with e.g. the word
	 * (template). This is needed in Generation-Editor
	 */
	public static final int GENERATION_DISPLAY_MODE = 1; 

	/**
	 * Contains the actual display-mode
	 */
	// Achtung: muss initial auf NORMAL_DISPLAY_MODE gesetzt werden!
	public static int DISPLAY_MODE = NORMAL_DISPLAY_MODE;


	private Exercise exercise;

	// PopUp-Menu
	private JPopupMenu popupMenu;
	// Merker für actionPerformed
	private DefaultMutableTreeNode currentTreeNode;

	private DefaultTreeModel treeModel;
	private JTree tree;
	
	protected static final String rootNodeText = "The Exercise";
	private DefaultMutableTreeNode rootNode;
	
	private UndoRedoInfo undoRedoInfo;
	
	private Vector treeObjects;
	
	private static final String play = "Play";
	private static final String stop = "Stop";
	private static final String back = "Back";
	private static final String forward = "Forward";
	
	private JPanel panel;
	private boolean finishedLoading;
	// Gibt an ob finishedLoading gültig sein soll
	private boolean finishedLoadingMessage;
	
	private int position;
	
	private boolean jumping;
	
	private boolean stopPossible;	// Stop-Button	

	/**
	 * Creates a Tree-Operation-View
	 * @param exercise Exercise
	 */
	public TreeOperationView(Exercise exercise) {
		this.exercise = exercise; 
		
		finishedLoading = false;
		finishedLoadingMessage = true;

		panel = new JPanel();
		panel.setLayout(new BorderLayout());

		TreeObject to = new TreeObject(null);
		to.setText(rootNodeText);
		rootNode = new DefaultMutableTreeNode(to);
		treeModel = new DefaultTreeModel(rootNode);
		tree = new JTree(treeModel);
		tree.getSelectionModel().setSelectionMode
					(TreeSelectionModel.SINGLE_TREE_SELECTION);
		tree.setCellRenderer(new MyTreeCellRenderer());
		tree.addMouseListener(this);
		// Enable tool tips.
		//ToolTipManager.sharedInstance().registerComponent(tree);
				
		panel.add(new JScrollPane(tree), BorderLayout.CENTER);
		
		//JButton playButton = new JButton(new ImageIcon("images/Play16.gif"));
		JButton playButton = new JButton(ImageHandler.createImageIcon("Play16.gif"));
		playButton.addActionListener(this);
		playButton.setActionCommand(play);
		
		//JButton stopButton = new JButton(new ImageIcon("images/Stop16.gif"));
		JButton stopButton = new JButton(ImageHandler.createImageIcon("Stop16.gif"));
		stopButton.addActionListener(this);
		stopButton.setActionCommand(stop);

		//JButton backButton = new JButton(new ImageIcon("images/StepBack16.gif"));
		JButton backButton = new JButton(ImageHandler.createImageIcon("StepBack16.gif"));
		backButton.addActionListener(this);
		backButton.setActionCommand(back);
		
		//JButton forwardButton = new JButton(new ImageIcon("images/StepForward16.gif"));
		JButton forwardButton = new JButton(ImageHandler.createImageIcon("StepForward16.gif"));
		forwardButton.addActionListener(this);
		forwardButton.setActionCommand(forward);
		
		
		JPanel buttonPanel = new JPanel();
		buttonPanel.setLayout(new GridLayout(1,4));
		buttonPanel.add(playButton);
		buttonPanel.add(stopButton);
		buttonPanel.add(backButton);
		buttonPanel.add(forwardButton);
		
		panel.add(buttonPanel, BorderLayout.SOUTH);
		
		undoRedoInfo = new UndoRedoInfo();
		position = -1;
		treeObjects = new Vector();
		
		jumping = false;
		
		// Popup
		popupMenu = new NodePopup(this);
		currentTreeNode = null;
		
		stopPossible = false;		
		
	}
	/**
	 * Gets the drawing-panel of the tree
	 * @return panel
	 */
	public JPanel getPanel() { return panel; }
	/**
	 * Gets the tree-component of the tree
	 * @return tree
	 */
	public JTree getTree() { return tree; }
	/**
	 * Enables jumping by the user
	 */
	public void enableJumping() { jumping = true; }
	/**
	 * Disables jumping by the user
	 */
	public void disableJumping() { jumping = false; }
	
	/**
	 * Defines that the tree should immediatly recording operations,
	 * not until a FINISHED_LOADING-Message received. 
	 */
	// Beim Baum-Aufbau nicht auf finishedLoading-Message warten,
	// sondern sofort anfangen
	public void noFinishedLoadingMessage() {
		finishedLoadingMessage = false;
		finishedLoading = true;
	}
	
	/**
	 * Marks init- and todo-operations in the tree. Only used in
	 * Generation-Editor.
	 */
	public void markTodoPosition() {
		int todoPosition = exercise.getOperationRecorder().getTodoPosition();
		//System.out.println("TOView.markTodoPosition: initOperationQueue.length = "+todoPosition);
		for(int i=0; i<treeObjects.size(); i++) {
			TreeObject to = (TreeObject)treeObjects.elementAt(i);
			if(i<todoPosition && to.getState() != TreeObject.INIT) {
				to.setState(TreeObject.INIT);
				treeModel.reload(to.getNode());
			} else if(i>=todoPosition && to.getState() != TreeObject.TODO) {
				to.setState(TreeObject.TODO);
				treeModel.reload(to.getNode());
			}
		}
	}
		
	/**
	 * Gets the associated context-menu
	 * @return context-menu-component
	 */
	// Hinzufügen von PopupMenuItems
	public NodePopup getNodePopup() { return (NodePopup)popupMenu; }

	/* (non-Javadoc)
	 * @see mauda.ExerciseUpdateListener#exerciseUpdate(mauda.ExerciseUpdateEvent)
	 */
	public void exerciseUpdate(ExerciseUpdateEvent e) {
		if(e.getID() == ExerciseUpdateEvent.FINISHED_LOADING) {
			finishedLoading = true;
			return;
		}
		if(!finishedLoading) return;
		if(e.getID() == ExerciseUpdateEvent.CLEAR_FOLLOWING) {
			UndoRedoUnit uru = cutTree();
			undoRedoInfo.addUnit(uru);
			return;
		}
		
		// Im TOView Operation, die gerade ausgeführt wird, speziell hervorheben
		// Bei Verwendung des folgenden Ansatzen, unten bei OPERATION_EXECUTED
		// position++ auskommentieren. Siehe CommentView. ACHTUNG: Abhängigkeiten!!! (in public-Methoden und Variablen)
		if(e.getID() == ExerciseUpdateEvent.START_OF_EXECUTION) {
			//position++;
			int p = position+1;
			DefaultMutableTreeNode node = null;
			if(p==-1) node = rootNode;
			if(p<treeObjects.size()) {
				TreeObject to = (TreeObject)treeObjects.elementAt(p); 
				node = to.getNode();
			}
			if(node!=null) makeActualProcessing(node);	// anstatt makeActual, andere Methode
		}
		if(!e.dsChanged()) return;
		switch(e.getID()) {
			case ExerciseUpdateEvent.OPERATION_EXECUTED :
				interactiveEvent(e.getOperation());
				break;
			case ExerciseUpdateEvent.BACK :
				position--;
				makeActual(getActNode());
				break;
			case ExerciseUpdateEvent.FORWARD :
				position++;
				makeActual(getActNode());
				break;
			case ExerciseUpdateEvent.UNDO :
				undo();
				break;
			case ExerciseUpdateEvent.REDO :
				redo();
				break;
			case ExerciseUpdateEvent.RESET:
				// Tree leeren
				while(rootNode.getChildCount()>0)
					treeModel.removeNodeFromParent((DefaultMutableTreeNode)rootNode.getChildAt(0));
				treeModel.reload();
				finishedLoading = false;
				stopPossible = false;
				// Bei GenExercise Baum immer sofort füllen
				if(!finishedLoadingMessage) {
					finishedLoading = true;
					stopPossible = true;
				}
				position=-1;
				treeObjects.removeAllElements();
				undoRedoInfo.clear(new Vector());
				break;
			case ExerciseUpdateEvent.JUMP :
				position += e.getValue();
				makeActual(getActNode());
				break;
			default :
				break;
		}
	}
	private void undo() {
		UndoRedoUnit uru = undoRedoInfo.undo(treeObjects);
		performGraphicalChange(uru);
	}
	private void redo() {
		UndoRedoUnit uru = undoRedoInfo.redo(treeObjects);
		performGraphicalChange(uru);
	}
	private void performGraphicalChange(UndoRedoUnit uru) {
		//System.out.println("TreeOperationView.performGraphicalChange():\n"+uru);
		Enumeration en = uru.getElements().elements();
		while(en.hasMoreElements())	{
			UndoRedoElement ure = (UndoRedoElement)en.nextElement();
			//System.out.println("  Performing: "+ure);
			int p = ure.getPos();
			TreeObject to = (TreeObject)ure.getObj();
			int id = ure.getID();
			if(id == UndoRedoElement.DELETE) {
				treeModel.removeNodeFromParent(to.getNode());
			} else if(id == UndoRedoElement.APPEND) {
				DefaultMutableTreeNode newNode = to.getNode();
				DefaultMutableTreeNode node;
				int childindex = 0;
				if(to.getOperation() instanceof Operation) {
					node = rootNode;
					childindex = node.getChildCount();
				} else {
					node = (DefaultMutableTreeNode)rootNode.getChildAt(rootNode.getChildCount()-1);
					childindex = node.getChildCount();
				}
				//treeModel.insertNodeInto(newNode, node, node.getChildCount());
				//System.out.println("insertNodeInto: "+newNode+" | "+node+" | "+childindex);
				treeModel.insertNodeInto(newNode, node, childindex);
				if(!tree.isExpanded(0)) tree.expandRow(0);
			}
		}
	}
	
	private void interactiveEvent(SimpleOperation op) {
		// Testen ob gleiche Operation gemacht wird
		// siehe auch UndoRedo
		if(op!=null&&position<treeObjects.size()-1) {
			TreeObject to = (TreeObject)treeObjects.elementAt(position+1);
			if(to!=null) {
				SimpleOperation o = to.getOperation();
				if(o.equals(op)) {
					position++;
					makeActual(to.getNode());
					return;
				}
			} 
		}		
		// Undo-Information:
		UndoRedoUnit uru = cutTree();
		position++;

		// Jetzt dazunehmen

		TreeObject newTO = new TreeObject(op);
		
		DefaultMutableTreeNode newNode = new DefaultMutableTreeNode(newTO);
		newTO.setNode(newNode);

		DefaultMutableTreeNode node;
		if(op instanceof Operation) {
			node = rootNode;
		} else {
			TreeObject to = (TreeObject)treeObjects.lastElement();
			node = (DefaultMutableTreeNode)to.getNode();
			if(to.getOperation() instanceof SubOperation)
				node = (DefaultMutableTreeNode)node.getParent();
		}
		treeModel.insertNodeInto(newNode, node, node.getChildCount());
		makeActual(newNode);

		treeObjects.add(newTO);
		
		uru.addElement(new UndoRedoElement(position, newTO, UndoRedoElement.APPEND));
		undoRedoInfo.addUnit(uru);
		//System.out.println("TreeOperationView.interactiveEvent(..): Unit added!");
	}
	
	/**
	 * Removes all following (relativly to the actual tree-node)
	 * tree-nodes from the tree.
	 */
	// Baum ab Position abschneiden
	public UndoRedoUnit cutTree() {
		//System.out.println("cutting tree");
		UndoRedoUnit uru = new UndoRedoUnit();
		
		int cutPosition = position+1;
		for(int i=treeObjects.size()-1; i>=cutPosition; i--) {
			//while (treeObjects.size() > cutPosition) {
			TreeObject to = (TreeObject)treeObjects.elementAt(i);
			treeModel.removeNodeFromParent(to.getNode());			
			treeObjects.removeElementAt(i);
			UndoRedoElement ure = new UndoRedoElement(i, to, UndoRedoElement.DELETE);
			uru.addElement(ure);
		}	
		return uru;
	}

	// Aktuellen Knoten setzen (für Markierung im Tree)
	private DefaultMutableTreeNode oldActualNode = null;
	/**
	 * Makes a specific node as the actual-node
	 * @param node TreeNode
	 */
	public void makeActual(DefaultMutableTreeNode node) {
		if(oldActualNode!=null) {
			TreeObject to = (TreeObject)oldActualNode.getUserObject();
			to.setActual(false);
			to.setProcessing(false);
			updateNodeDisplay(oldActualNode);
		}
		if(node!=null) {
			TreeObject to = (TreeObject)node.getUserObject(); 
			to.setActual(true);
			to.setProcessing(false);
			updateNodeDisplay(node);
			tree.scrollPathToVisible(new TreePath(node.getPath()));
		}
		oldActualNode = node;
	}
	/**
	 * Makes a specific node as the actual-node that was in process
	 * @param node TreeNode
	 */
	public void makeActualProcessing(DefaultMutableTreeNode node) {
		if(oldActualNode!=null) {
			TreeObject to = (TreeObject)oldActualNode.getUserObject();
			to.setActual(false);
			to.setProcessing(false);
			updateNodeDisplay(oldActualNode);
		}
		if(node!=null) {
			TreeObject to = (TreeObject)node.getUserObject(); 
			to.setActual(false);
			to.setProcessing(true);
			updateNodeDisplay(node);
			tree.scrollPathToVisible(new TreePath(node.getPath()));
		}
		oldActualNode = node;		
	}
	
	private void updateNodeDisplay(DefaultMutableTreeNode node) {
		treeModel.nodeChanged(node);
	}
	
	private DefaultMutableTreeNode getActNode() {
		// Auf Wurzelknoten springen => rootNode zurückgeben
		if(position==-1) return rootNode;
		
		TreeObject to = (TreeObject)treeObjects.elementAt(position); 
		return to.getNode();
	}
	
	private Vector genJumpTo(DefaultMutableTreeNode n) {
		TreeObject to = (TreeObject)n.getUserObject();
		// Beim Klick auf Wurzelknoten auf erste Operation
		// springen
		int offset = treeObjects.indexOf(to);
		
		// wir befinden uns auf dem Wurzelknoten und wollen auch dahin => nichts tun!
		if(offset==-1 && position==-1) return new Vector();
		
		OperationRecorder or = exercise.getOperationRecorder();
		int delta = offset+or.getOffset(0,-1) - or.getCurrentOffset();
		//System.out.println("TreeOperationView.genJumpTo(..): position = "+position+" delta="+delta+" indexOf="+offset);

		// Message fürs springen
		Vector v = new Vector();
		if(delta!=0) {
			ExerciseUpdateEvent eue = new ExerciseUpdateEvent(exercise, ExerciseUpdateEvent.JUMP);
			eue.setValue(delta);
			v.add(eue);
			return v;
		}
		return v;			
	}
	/**
	 * Marks the correctness in the tree.
	 * @param limited true means: Marking only up to the current node, false means to mark all nodes
	 * @param folderMark true if the folders should marked (e.g. INSERT), false otherwise
	 */
	public void markCorrectness(boolean limited, boolean folderMark) {
		//System.out.println("TOV: markCorrectness-call =========================");
		OperationRecorder or = exercise.getOperationRecorder();
		int orOffset = or.getOffset(0,-1);
		int c = 0;
		int startOffset = 0;	// Beginn des Fehlers
		int endOffset = 0;		// Ende des Fehlers
		boolean stop = false;
		TreeObject to = null;
		int opOffset = -1;
		while(true) {
			Failure f = null;
			if(limited) f = or.getLimitedFailure(c+orOffset);
			else f = or.getUnlimitedFailure(c+orOffset);
			//System.out.println(f!=null?f.getDescriptionString():"f=null");
			if(f!=null) {
				startOffset = f.getOffset()-orOffset;
				endOffset = f.getMaxOffset()-orOffset;
				if(f.getDescription() == Failure.MISSING_SUBOP)
					endOffset--;
				// Offset der aktuelle Operation holen
				// --> als Incorrect markieren (siehe unten)
				opOffset = or.getOffset(f.getOpNr(),-1)-orOffset;
			}
			else {
				startOffset = treeObjects.size();
				endOffset = startOffset-1;
				stop = true;
			}
			if(startOffset>=treeObjects.size()) startOffset = treeObjects.size();
			if(endOffset>=treeObjects.size()) {
				endOffset = treeObjects.size()-1;
				stop = true;
			}
			//System.out.println("startOffset = "+startOffset); 
			//System.out.println("endOffset = "+endOffset); 
			// Alles bis startOffset auf CORRECT setzen
			for(int i=c; i<startOffset; i++) {
				to = (TreeObject)treeObjects.elementAt(i);
				if(i == opOffset) {
					if(folderMark) markCorrectness(to.getNode(), TreeObject.INCORRECT);
				} else {
					if(folderMark || to.getOperation() instanceof SubOperation)
						markCorrectness(to.getNode(), TreeObject.CORRECT);
				}
			}
			// Alles von startOffset bis endOffset (inklusive) auf INCORRECT setzen
			for(int i=startOffset; i<=endOffset; i++) {
				to = (TreeObject)treeObjects.elementAt(i);
				markCorrectness(to.getNode(), TreeObject.INCORRECT);
			}
			// Abbruchbedingung
			if(stop) return;
			c = endOffset+1;
		}		
	}
	
	/**
	 * Marks the actual node as correct
	 */
	public void markActAsCorrect() {
		markCorrectness(getActNode(), TreeObject.CORRECT);
	}
	/**
	 * Marks the actual node as incorrect
	 */
	public void markActAsIncorrect() {
		markCorrectness(getActNode(), TreeObject.INCORRECT);
	}
	/**
	 * Marks a specific node as correct
	 * @param index Index (counted by 0)
	 */
	public void markAsCorrect(int index) {
		TreeObject to = (TreeObject)treeObjects.elementAt(index);
		markCorrectness(to.getNode(), TreeObject.CORRECT);
	}
	/**
	 * Marks a specific node as incorrect
	 * @param index Index (counted by 0)
	 */
	public void markAsIncorrect(int index) {
		TreeObject to = (TreeObject)treeObjects.elementAt(index);
		markCorrectness(to.getNode(), TreeObject.INCORRECT);
	}
	private void markCorrectness(DefaultMutableTreeNode node, int c) {
		if(node == null) return;
		TreeObject to = (TreeObject)node.getUserObject();
		if(to.getCorrectness() != c) {
			to.setCorrectness(c);
			treeModel.reload(node);
		}
		
	}

	/* (non-Javadoc)
	 * @see java.awt.event.MouseListener#mouseClicked(java.awt.event.MouseEvent)
	 */
	public void mouseClicked(MouseEvent e) { }
	/* (non-Javadoc)
	 * @see java.awt.event.MouseListener#mousePressed(java.awt.event.MouseEvent)
	 */
	public void mousePressed(MouseEvent e) { }

	/* (non-Javadoc)
	 * @see java.awt.event.MouseListener#mouseReleased(java.awt.event.MouseEvent)
	 */
	public void mouseReleased(MouseEvent e) {
		if(!exercise.actionsAllowed()) return;
		int x = e.getX();
		int y = e.getY();
		TreePath path = tree.getPathForLocation(x,y);
		if(path == null) return;
		DefaultMutableTreeNode tn = (DefaultMutableTreeNode)path.getLastPathComponent();
		currentTreeNode = tn;
		if (e.isPopupTrigger()) {
			// Popup-Menu anzeigen
			popupMenu.show(tree, x, y);
		} else {
			// Ansonsten springen
			if(jumping) {
				Vector v = genJumpTo(currentTreeNode);
				if(v.size()>0) exercise.commit(v);
			}
		}
		
	}
	
	/* (non-Javadoc)
	 * @see java.awt.event.MouseListener#mouseEntered(java.awt.event.MouseEvent)
	 */
	public void mouseEntered(MouseEvent e) { }
	/* (non-Javadoc)
	 * @see java.awt.event.MouseListener#mouseExited(java.awt.event.MouseEvent)
	 */
	public void mouseExited(MouseEvent e) { }
	
	/**
	 * Enables the stop-button, so that the replay of an exercise
	 * can stopped.
	 */
	public void enableStop() {
		//System.out.println("TOV: stop enabled!"); 
		stopPossible = true;
	}
	
	/* (non-Javadoc)
	 * @see java.awt.event.ActionListener#actionPerformed(java.awt.event.ActionEvent)
	 */
	// Aktionen im Popup-Menu
	public void actionPerformed(ActionEvent e) {
		if(!finishedLoading) return;
		String command = e.getActionCommand();
		//System.out.println("ActionCommand: "+command);
		if(command == stop) {
			// Stop nur bei Replay!!!
			// wird teilweise durch finishedLoading garantiert
			// aber nicht bei FaultMode und beim Laden von
			// in Bearbeitung-stehenden Aufgaben
			// => enableStop/disableStop-Methoden
			if(stopPossible) exercise.stopPlaying();
			//else System.out.println("TOView: stop is disabled");
		}
		
		if(!exercise.actionsAllowed()) return;
		
		if(command == NodePopup.play) {
			if(jumping) {
				// JumpTo-Message generieren
				Vector exOps = genJumpTo(currentTreeNode);
				// Jetzt die auszuführenden Operationen holen
				//System.out.println("Operations to perform for replay:");
				// Beim Anwählen der Wurzel auf nächste Operation
				// springen
				TreeObject to = (TreeObject)currentTreeNode.getUserObject();
				int index = treeObjects.indexOf(to);
				for(int i=index+1; i<treeObjects.size(); i++) {
					to = (TreeObject)treeObjects.elementAt(i);
					SimpleOperation so = to.getOperation();
					exOps.add(so.clone());
					//System.out.println((i-index)+". "+so.out());
				}
				// Alles ausführen
				if(exOps.size()>0) {
					exercise.jedasPanel.nextNoProgressBar();
					exercise.commit(exOps);
				} 
			}
		} else if(command == NodePopup.jumpTo) {
			if(jumping) {
				//System.out.println("Jump to ...");
				Vector v = genJumpTo(currentTreeNode);
				if(v.size()>0) exercise.commit(v);
			}
		} else if(command == play) {
			if(jumping) {
				Vector exOps = new Vector();
				for(int i=position+1; i<treeObjects.size(); i++) {
					TreeObject to = (TreeObject)treeObjects.elementAt(i);
					SimpleOperation so = to.getOperation();
					exOps.add(so.clone());
					//System.out.println((i-position)+". "+so.out());
				}
				// Alles ausführen
				if(exOps.size()>0) {
					exercise.jedasPanel.nextNoProgressBar();
					exercise.commit(exOps);
				}
			}
		} else if(command == back) {
			if(position>=0 && jumping) exercise.back();
		} else if(command == forward) {
			if(position<treeObjects.size()-1 && jumping) exercise.forward();
		}
	}
	/**
	 * Returns the index of the node, that context-menu was lastly
	 * activated. So it is possible, to get information on which
	 * node the context-menu-action was performed.
	 * @return Index
	 */
	public int getActionIndex() {
		TreeObject to = (TreeObject)currentTreeNode.getUserObject();
		return treeObjects.indexOf(to);
	}
	/**
	 * Converts the important values into a vector-representation
	 * for saving-purposes.
	 * @return vector-representation
	 */
	// Abspeichern der Correctnesses, jedes Baum-Eintrags
	public Vector save() {
		Vector v = new Vector();
		Enumeration en = treeObjects.elements();
		while(en.hasMoreElements()) {
			TreeObject to = (TreeObject)en.nextElement();
			v.add(new Integer(to.getCorrectness()));
		}
		return v;
	}
	/**
	 * Loads the values of the current tree with the values of the
	 * delivered vector-representation
	 * @param v vector-representation
	 */
	// Laden der Correctnesses
	public void load(Vector v) {
		if(v==null) return;
		for(int i=0; i<v.size(); i++) {
			int c = ((Integer)v.elementAt(i)).intValue();
			TreeObject to = (TreeObject)treeObjects.elementAt(i);
			markCorrectness(to.getNode(), c);
		}
	}
}
