package de.ugoe.cs.eventbench.windows.data;

import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.SortedSet;
import java.util.TreeSet;

/**
 * <p>
 * This class provides an the interfaces for window trees.
 * </p>
 * <p>
 * The window tree represents the hierarchical structure of the windows
 * "as it is" currently during a session. It may change during the session due
 * to creation and destruction of windows.
 * </p>
 * <p>
 * The class is implemented as a singleton. The rational behind implementing
 * this class as a singleton is to ease the access of all class that may request
 * information about the windows during the parsing of a session. As the tree
 * may change during the session, it does not make sense to preserve it after a
 * session. Thus, it can just be deleted. Therefore, as long as only one session
 * is parsed at a time, a single instance is sufficient.
 * </p>
 * 
 * @author Steffen Herbold
 * @version 1.0
 */
public class WindowTree {

	/**
	 * <p>
	 * Handle to the window instance.
	 * </p>
	 */
	private static WindowTree theInstance = null;

	/**
	 * <p>
	 * Maintains a set of all the target strings of all widgets that were at
	 * some point part of the window tree.
	 * </p>
	 */
	private SortedSet<String> targets;

	/**
	 * <p>
	 * Obtain a handle to the window instance.
	 * </p>
	 * 
	 * @return instance of the window tree
	 */
	public static WindowTree getInstance() {
		if (theInstance == null) {
			theInstance = new WindowTree();
		}
		return theInstance;
	}

	/**
	 * <p>
	 * Resets the tree. Should be used between sessions.
	 * </p>
	 */
	public static void resetTree() {
		theInstance = null;
	}

	/**
	 * <p>
	 * Map of all windows that are part of the tree for efficient searching. The
	 * keys of the map are the hwnd's of the windows.
	 * </p>
	 */
	private Map<Integer, WindowTreeNode> nodes;

	/**
	 * <p>
	 * Creates a new WindowTree.
	 * </p>
	 * <p>
	 * Private, as the class is a singleton.
	 * </p>
	 */
	private WindowTree() {
		nodes = new HashMap<Integer, WindowTreeNode>();
		targets = new TreeSet<String>();
	}

	/**
	 * <p>
	 * Adds a new window to the tree.
	 * </p>
	 * 
	 * @param parentHwnd
	 *            hwnd of the parent window
	 * @param childHwnd
	 *            hwnd of the window to be created
	 * @param childWindowName
	 *            resource id of the window to be created
	 * @param resourceId
	 *            resource id of the window to be created
	 * @param className
	 *            class name of the window to be created
	 */
	public void add(int parentHwnd, int childHwnd, String childWindowName,
			int resourceId, String className, boolean isModal) {
		WindowTreeNode parent = nodes.get(parentHwnd);
		WindowTreeNode child = nodes.get(childHwnd);
		if (child == null) {
			if (parent != null) {
				child = parent.addChild(childHwnd, childWindowName, resourceId,
						className, isModal);
			} else {
				child = new WindowTreeNode(childHwnd, null, childWindowName,
						resourceId, className, isModal);
			}
			nodes.put(childHwnd, child);
			targets.add(child.xmlRepresentation());
		}
	}

	/**
	 * <p>
	 * Removes a window (defined by its hwnd) from the tree. All children of the
	 * window will be removed recursively.
	 * </p>
	 * 
	 * @param hwnd
	 *            hwnd of the window to be removed
	 * @return number of windows that were removed
	 */
	public int remove(int hwnd) {
		int removedCounter = 0;
		WindowTreeNode node = nodes.get(hwnd);
		if (node != null) {
			List<WindowTreeNode> nodesToBeRemoved = node.remove();
			for (int i = 0; i < nodesToBeRemoved.size(); i++) {
				WindowTreeNode nodeToBeRemoved = nodesToBeRemoved.get(i);
				nodesToBeRemoved.addAll(nodeToBeRemoved.getChildren());
				nodes.remove(nodeToBeRemoved.getHwnd());
				removedCounter++;
			}
			nodes.remove(hwnd);
			removedCounter++;
		}
		return removedCounter;
	}

	/**
	 * <p>
	 * Searches the tree for a window with the specified hwnd and returns its
	 * {@link WindowTreeNode}.
	 * </p>
	 * 
	 * @param hwnd
	 *            hwnd that is looked for
	 * @return {@link WindowTreeNode} of the window with the given hwnd if
	 *         found, null otherwise
	 */
	public WindowTreeNode find(int hwnd) {
		return nodes.get(hwnd);
	}

	/**
	 * <p>
	 * Returns the number of nodes contained in the WindowTree.
	 * </p>
	 * 
	 * @return number of nodes
	 */
	public int size() {
		return nodes.size();
	}

	/**
	 * <p>
	 * Returns a sorted set of all targets that existed any time in the window
	 * tree.
	 * </p>
	 * 
	 * @return set of targets
	 */
	public SortedSet<String> getTargets() {
		return targets;
	}
}
