/*
 * Copyright (c) 2006-2008 Maskat Project.
 *
 * All rights reserved. This program and the accompanying materials
 * are made available under the terms of the Eclipse Public License v1.0
 * which accompanies this distribution, and is available at
 * http://www.eclipse.org/legal/epl-v10.html
 *
 * Contributors:
 *     Maskat Project - initial API and implementation
 */
package org.maskat.core;

import java.util.ArrayList;
import java.util.Collections;
import java.util.Iterator;
import java.util.List;

import org.apache.commons.collections.IteratorUtils;
import org.apache.commons.collections.PredicateUtils;

import org.maskat.core.layout.custom.DynaComponent;

/**
 * 基本レイアウト定義、レイアウト定義の下に子要素が持つことができる
 */
public abstract class AbstractMaskatElement implements MaskatElement {

	/** The parent of this element */
	private MaskatElement parent;

	/** list of child elements */
	private List children;

	public boolean contentEquals(Object obj) {
		if (obj == this) {
			return true;
		}
		if (!(obj instanceof AbstractMaskatElement)) {
			return false;
		}

		AbstractMaskatElement o = (AbstractMaskatElement) obj;
		List oChildren = o.getUnmodifiableChildren();
		List thisChildren = this.getUnmodifiableChildren();
		if (oChildren == null && thisChildren != null) {
			return false;
		}
		if (thisChildren == null && oChildren != null) {
			return false;
		}
		if (thisChildren != null) {
			return thisChildren.equals(oChildren);
		}

		return true;
	}

	public MaskatElement getParent() {
		return parent;
	}

	public void setParent(MaskatElement parent) {
		this.parent = parent;
	}
	
	/*
	 * (non-Javadoc)
	 * 
	 * @see org.maskat.framework.IBasicDef#getTypedChildren(java.lang.Class)
	 */
	public Iterator getTypedChildren(Class clazz) {
		if (children == null) {
			return null;
		}
		
		Iterator iterator = IteratorUtils.filteredIterator(children.iterator(), PredicateUtils
				.instanceofPredicate(clazz));
		
		if (iterator != null && iterator.hasNext())	{
			return iterator;
		}
		else {
			return null;
		}
		
		/*return IteratorUtils.filteredIterator(children.iterator(), PredicateUtils
				.instanceofPredicate(clazz));*/
	}

	/*
	 * (non-Javadoc)
	 * 
	 * @see org.maskat.framework.IBasicDef#getAllChildren()
	 */
	public Iterator getChildren() {
		if (children == null) {
			return null;
		}
		return IteratorUtils.unmodifiableIterator(children.iterator());
	}
	
	/*
	 * (non-Javadoc)
	 * 
	 * @see org.maskat.framework.IBasicDef#getUnmodifiableChildren()
	 */
	public List getUnmodifiableChildren() {
		return (children == null) ? null : Collections.unmodifiableList(children);
	}

	/*
	 * (non-Javadoc)
	 * 
	 * @see org.maskat.framework.IBasicDef#getChildByTypeIdx(java.lang.Class,
	 *      int)
	 */
	public Object getChildByTypeIdx(Class clazz, int idx) {
		if (idx < 0) {
			throw new IllegalArgumentException(
					"parameter [idx] should be >= 0.(" + idx + ")");
		}
		if (children == null || children.size() <= idx) {
			return null;
		}
		for (Iterator it = children.iterator(); it != null && it.hasNext();) {
			Object obj = it.next();
			if (clazz.isInstance(obj)) {
				if (idx == 0) {
					return obj;
				}
				idx--;
			}
		}
		return null;
	}

	/*
	 * (non-Javadoc)
	 * 
	 * @see org.maskat.framework.IBasicDef#addChild(java.lang.Object)
	 */
	public void addChild(Object child) {
		if (child == null) {
			throw new IllegalArgumentException(
					"BasicDef should not have a null child element");
		}
		if (children == null) {
			children = new ArrayList();
		}
		if (child == this) {
			throw new IllegalArgumentException("Could not add an object as his own child");
		}
		if (children.contains(child)) {
//			throw new IllegalStateException("object already exists");
			return;
		}

		if (child instanceof MaskatElement) {
			((MaskatElement) child).setParent(this);
		}
		children.add(child);
	}

	/*
	 * (non-Javadoc)
	 * 
	 * @see org.maskat.framework.IBasicDef#getChildIdx(java.lang.Object)
	 */
	public int getChildIdx(Object child) {
		return (children == null) ? -1 : children.indexOf(child);
	}

	/*
	 * (non-Javadoc)
	 * 
	 * @see org.maskat.framework.IBasicDef#addChildToIdx(java.lang.Object, int)
	 */
	public void addChildToIdx(Object child, int idx) {
		if (children == null) {
			children = new ArrayList();
		}
		if (child == this) {
			throw new IllegalArgumentException("Could not add an object as his own child");
		}
		if (children.contains(child)) {
			throw new IllegalStateException("object already exists");
		}

		if (child instanceof AbstractMaskatElement) {
			((AbstractMaskatElement) child).parent = this;
		}
		children.add(idx, child);
	}

	/*
	 * (non-Javadoc)
	 * 
	 * @see org.maskat.framework.IBasicDef#removeChild(java.lang.Object)
	 */
	public void removeChild(Object obj) {
		if (children == null) {
			return;
		}
		children.remove(obj);
		if (children.size() == 0)
		{
			children = null;
		}
	}

	/*
	 * (non-Javadoc)
	 * 
	 * @see org.maskat.framework.IBasicDef#removeAllChildren()
	 */
	public void removeAllChildren() {
		if (children == null) {
			return;
		}
		children.clear();
	}

	/**
	 * Delete all the child elements which are instances of clazz
	 * 
	 * @param clazz
	 */
	public void removeAllByType(Class clazz) {
		if (children == null) {
			return;
		}
		for (Iterator it = children.iterator(); it != null && it.hasNext();) {
			Object obj = it.next();
			if (clazz.isInstance(obj)) {
				it.remove();
			}
		}
	}

	public void accept(MaskatElementVisitor visitor) {
		if (children == null) {
			return;
		}

		for (Iterator it = children.iterator(); it.hasNext();) {
			Object next = it.next();
			if (next == null) {
				continue;
			}

			visitor.visit(next);
			if (next instanceof MaskatElement) {
				MaskatElement basicDef = (MaskatElement) next;
				basicDef.accept(visitor);
			}
		}
	}

	/**
	 * Note: the BasicDef returned by clone is breaked from it's original
	 * parent. So the parentDef is <em>null</em>.
	 */
	public Object clone() throws CloneNotSupportedException {
		AbstractMaskatElement obj = (AbstractMaskatElement) super.clone();
		obj.children = new ArrayList();
		obj.parent = null;
		for (Iterator it = this.getChildren(); it != null && it.hasNext();) {
			Object def = it.next();
			if (def instanceof AbstractMaskatElement) {
				obj.addChild(((AbstractMaskatElement) def).clone());
			} else if (def instanceof DynaComponent) {
				obj.addChild(((DynaComponent) def).clone());
			} else {
				obj.addChild(def);
			}
		}
		return obj;
	}
	
	public void getAllDescendants(MaskatElement parent, Class descendantClass, ArrayList result) {
		if (parent == null || result == null) {
			return;
		}

		Iterator it = parent.getChildren();
		while (it != null && it.hasNext()) {
			Object child = it.next();
			if (!(child instanceof MaskatElement)) {
				continue;
			}
			if (descendantClass.equals(child.getClass())) {
				result.add(child);
			}
			getAllDescendants((MaskatElement)child, descendantClass, result);
		}
	}

}
