package junit.swingui;

import java.util.*;

import javax.swing.event.*;
import javax.swing.tree.*;
import junit.extensions.TestDecorator;
import junit.framework.*;

/**
 * A tree model for a Test.
 */
class TestTreeModel implements TreeModel {
	private Test fRoot;
	private Vector fModelListeners= new Vector();
	private Hashtable fFailures= new Hashtable();
	private Hashtable fErrors= new Hashtable();
	private Hashtable fRunTests= new Hashtable();
	
	/**
	 * Constructs a tree model with the given test as its root.
	 */
	public TestTreeModel(Test root) {
		super();
		fRoot= root;
	}
	
	/**
	 * adds a TreeModelListener
	 */
	public void addTreeModelListener(TreeModelListener l) {
		if (!fModelListeners.contains(l))
			fModelListeners.addElement(l);
	}
	/**
	 * Removes a TestModelListener
	 */
	public void removeTreeModelListener(TreeModelListener l) {
		fModelListeners.removeElement(l);
	}
	/**
	 * Finds the path to a test. Returns the index of the test in its
	 * parent test suite.
	 */
	public int findTest(Test target, Test node, Vector path) {
		if (target.equals(node)) 
			return 0;
			
		TestSuite suite= isTestSuite(node);
		for (int i= 0; i < getChildCount(node); i++) {
			Test t= suite.testAt(i); 
			int index= findTest(target, t, path);
			if (index >= 0) {
				path.insertElementAt(node, 0);
				if (path.size() == 1)
					return i;
				return index;
			}
		}
		return -1;
	}
	/**
	 * Fires a node changed event
	 */
	public void fireNodeChanged(TreePath path, int index) {
		int[] indices= {index};
		Object[] changedChildren= {getChild(path.getLastPathComponent(), index)};
		TreeModelEvent event= new TreeModelEvent(this, path, indices, changedChildren);
		
		Enumeration e= fModelListeners.elements();
		while (e.hasMoreElements()) { 
			TreeModelListener l= (TreeModelListener) e.nextElement();
			l.treeNodesChanged(event);
		}	
	}
	/**
	 * Gets the test at the given index
	 */
	public Object getChild(Object parent, int index) {
		TestSuite suite= isTestSuite(parent);
		if (suite != null) 
			return suite.testAt(index);
		return null; 
	}
	/**
	 * Gets the number of tests.
	 */
	public int getChildCount(Object parent) {
		TestSuite suite= isTestSuite(parent);
		if (suite != null) 
			return suite.testCount();
		return 0;
	}
	/**
	 * Gets the index of a test in a test suite
	 */
	public int getIndexOfChild(Object parent, Object child) {
		TestSuite suite= isTestSuite(parent);
		if (suite != null) {
			int i= 0;
			for (Enumeration e= suite.tests(); e.hasMoreElements(); i++) {
				if (child.equals((Test)e.nextElement()))
					return i;
			}
		}
		return -1; 
	}
	/**
	 * Returns the root of the tree
	 */
	public Object getRoot() {
		return fRoot;
	}
	/**
	 * Tests if the test is a leaf.
	 */
	public boolean isLeaf(Object node) {
		return isTestSuite(node) == null;
	}
	/**
	 * Tests if the node is a TestSuite.
	 */
	TestSuite isTestSuite(Object node) {
		if (node instanceof TestSuite) 
			return (TestSuite)node;
		if (node instanceof TestDecorator) { 
			Test baseTest= ((TestDecorator)node).getTest(); 
			return isTestSuite(baseTest); 
		} 
		return null;
	}
	
	/**
	 * Called when the value of the model object was changed in the view
	 */
	public void valueForPathChanged(TreePath path, Object newValue) {
		// we don't support direct editing of the model
		System.out.println("TreeModel.valueForPathChanged: not implemented");
	}
	/**
	 * Remembers a test failure
	 */
	void addFailure(Test t) {
		fFailures.put(t, t);
	}
	/**
	 * Remembers a test error
	 */
	void addError(Test t) {
		fErrors.put(t, t);
	}
	/**
	 * Remembers that a test was run
	 */
	void addRunTest(Test t) {
		fRunTests.put(t, t);
	}
	/**
	 * Returns whether a test was run
	 */
	boolean wasRun(Test t) {
		return fRunTests.get(t) != null;
	}
	/**
	 * Tests whether a test was an error
	 */
	boolean isError(Test t) {
		return (fErrors != null) && fErrors.get(t) != null;
	}
	/**
	 * Tests whether a test was a failure
	 */
	boolean isFailure(Test t) {
		return (fFailures != null) && fFailures.get(t) != null;
	}
	/**
	 * Resets the test results
	 */
	void resetResults() {
		fFailures= new Hashtable();
		fRunTests= new Hashtable();
		fErrors= new Hashtable();
	}
}