package net.sourceforge.jsurveyor;
import java.util.*;

/**
 * Class to handle the composite structure of users and usergroups
 * 
 * @version 1.0
 * @author Claus Methmann Christensen
 * @author Anders Spliid Hansen
 * @author Esben Iversen Hansen
 * @author Lars H Jensen
 * @author Marcus Overheu
 * @author Thomas Ramdal
 * @author Peter Thomsen
 */
public abstract class UserComponent {

	/**
	 * Field refChildren
	 */
	private ArrayList refChildren = new ArrayList();
	/**
	 * Field refParents
	 */
	private ArrayList refParents = new ArrayList();

	/**
	 * Constructor for UserComponent
	 */
	public UserComponent() {
	}

	/**
	 * Constructor for UserComponent
	 * 
	 * @param refChildren ArrayList
	 * @param refParents ArrayList
	 */
	public UserComponent(ArrayList refChildren, ArrayList refParents) {
		this.refChildren = refChildren;
		this.refParents = refParents;
	}

	/**
	 * Adds a parent to the userComponent in the composite structure.
	 * @pre the input must be a userGroup.
	 * @pre the userComponent must be added as a parent already.
	 * @pre if the method is run from a usergroup it must not have a parent when the method is called.
	 * @param userComponent UserComponent
	 */
	public void addParent(UserComponent userComponent)
		throws ArrayStoreException {

		assert userComponent
			instanceof UserGroup : "You can only have UserGroup's as parents";

		if (this instanceof User) {
			assert !(
				isUserComponentInParents(userComponent)) : "You cannot add a user to a group where it already exists";
			if (isUserComponentInParents(userComponent))
				throw new ArrayStoreException(
					"You cannot add a user to a group where it already exists refParents:"
						+ refParents
						+ " userComponent: "
						+ userComponent.toString());
			refParents.add(userComponent.toString());

			if (!userComponent.isUserComponentInChildren(this)) // rekursiv
				userComponent.addChild(this);
		} else {
			// instanceof UserGroup
			if (refParents.size() != 0)
				throw new ArrayStoreException("UserGroup's can only have one parent, please remove the old one first");
			assert refParents.size()
				== 0 : "UserGroup's can only have one parent, please remove the old one first";
			refParents.add(userComponent.toString());
			userComponent.addChild(this);

		} // end else
	} // end addParent

	/**
	 * Removes all parents for the userComponent.
	 *
	 */
	public void clearParents() {
		refParents.clear();
	} // end clearParent
	
	/**
	 * Removes a parent from the userComponent.
	 * @pre the input must be an instance of userGroup
	 * @pre the userGroup must be added as a parent.
	 * @param userComponent
	 *                    UserComponent
	 */
	public void removeParent(UserComponent userComponent) {
		assert isUserComponentInParents(userComponent) : "You cannot remove a userComponent from parents if its not there";

		refParents.remove(refParents.indexOf(userComponent.toString()));

		if (userComponent.isUserComponentInChildren(this)) {
			userComponent.removeChild(this);

		} // end if
	} // end removeParent
	
	/**
	 * Returns an ArrayList with toString representation of the added parents.
	 * @return ArrayList
	 */
	public ArrayList getParents() {
		return refParents;
	}

	/**
	 * Checks if a userComponent is added a parent.
	 * 
	 * @param userComponent UserComponent
	 * @return boolean
	 */
	private boolean isUserComponentInParents(UserComponent userComponent) {
		return refParents.indexOf(userComponent.toString()) != -1;
	}

	/**
	 * Adds a child.
	 * @pre the userComponent must not already be added.
	 * @pre the method must only be called on instances of userGroups.
	 * 
	 * @param userComponent UserComponent
	 */
	public void addChild(UserComponent userComponent) {
		assert this instanceof UserGroup : "You cannot add children to Users";
		assert !(
			isUserComponentInChildren(userComponent)) : "The UserComponent "
			+ userComponent
			+ " was already added as a child";
		refChildren.add(userComponent.toString());
		if (!userComponent.isUserComponentInParents(this))
			userComponent.addParent(this); // rekursiv
	}

	/**
	 * Removes the userComponent from child list.
	 * @pre the method must only be called on instances of userGroups.
	 * @pre the usercomponent must be added as a child.
	 * @param userComponent UserComponent
	 */
	public void removeChild(UserComponent userComponent) {

		assert this
			instanceof UserGroup : "You cannot add children to Users, so you can't remove either";
		assert isUserComponentInChildren(userComponent) : "The UserComponent is not added as a child, so you cannot remove it";

		refChildren.remove(refChildren.indexOf(userComponent.toString()));

		if (userComponent.isUserComponentInChildren(this)) {
			userComponent.removeParent(this); // rekursiv
		}

	} // end removeChild

	/**
	 * Returns an ArrayList with toString representations of the added  children.
	 * 
	 * @return ArrayList
	 */
	public ArrayList getRefChildren() {
		return refChildren;
	}

	/**
	 * Returns an ArrayList with toString representations of the added parents.
	 * @return ArrayList
	 */
	public ArrayList getRefParents() {
		return refParents;
	}
	
	/**
	 * Checks whether or not any parents is added.
	 * @return boolean
	 */
	public boolean isRefParentsEmpty() {
		return (refParents.size() == 0);
	}

	/**
	 * Checks if the input is added as child. 
	 * @param userComponent UserComponent
	 * @return boolean
	 */
	private boolean isUserComponentInChildren(UserComponent userComponent) {
		return (refChildren.indexOf(userComponent.toString()) != -1);
	}

	/**
	 * Equals method that overrides the default Object.equals(). Necessary for
	 * the reference implementation of the composite pattern.
	 * @param userComponent UserComponent
	 * @return boolean
	 */
	public boolean equals(UserComponent userComponent) {

		String a1 = this.refChildren.toString();
		String a2 = "" + userComponent.getRefChildren();
		String b1 = "" + this.refParents;
		String b2 = "" + userComponent.getRefParents();

		return a1.equals(a2) && b1.equals(b2);
	} // end equals
} // end class