/*
 * Copyright 2006 Mask@ Project.
 * 
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 * 
 *      http://www.apache.org/licenses/LICENSE-2.0
 * 
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package org.maskat.framework;

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

import org.maskat.framework.screendef.DescDef;

/**
 * 基本レイアウト定義、レイアウト定義の下に子要素が持つことができる
 * 
 * @author shengshen
 */
public abstract class BasicDef implements IBasicDef {
	/**
	 * 子要素<br>
	 * !!!childrenには同じオブジェクトを多重持つことは禁止!!!<br>
	 * !!!removeChildはObject.equal()に基づくだから!!!
	 */
	private DefChildList children;

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

		BasicDef o = (BasicDef) 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;
	}

	/*
	 * (non-Javadoc)
	 * 
	 * @see org.maskat.framework.IBasicDef#getTypedChildren(java.lang.Class)
	 */
	public Iterator getTypedChildren(Class clazz) {
		if (children == null)
			return null;
		return children.childItr(clazz);
	}

	/*
	 * (non-Javadoc)
	 * 
	 * @see org.maskat.framework.IBasicDef#getAllChildren()
	 */
	public Iterator getAllChildren() {
		if (children == null)
			return null;
		return new NoRemoveIterator(children.iterator());
	}

	/*
	 * (non-Javadoc)
	 * 
	 * @see org.maskat.framework.IBasicDef#getUnmodifiableChildren()
	 */
	public List getUnmodifiableChildren() {
		if (children == null)
			return null;
		return Collections.unmodifiableList(children);
	}

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

	/*
	 * (non-Javadoc)
	 * 
	 * @see org.maskat.framework.IBasicDef#getChildIdx(java.lang.Object)
	 */
	public int getChildIdx(Object child) {
		if (children == null)
			return -1;
		return 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 DefChildList();
		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");
		}
		children.add(idx, child);
	}

	/*
	 * (non-Javadoc)
	 * 
	 * @see org.maskat.framework.IBasicDef#addDesc(java.lang.String)
	 */
	public void addDesc(String desc) {
		DescDef descDef = new DescDef();
		descDef.setComment(desc);
		addChild(descDef);
	}

	// public List getSortedChildren() {
	// // TODO 子要素のソート、XMLファイルに出力を綺麗にするために、
	// // たとえば、grid要素のheader要素を先に出力、次はline要素
	// throw new UnsupportedOperationException("");
	// }

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

	/*
	 * (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#removeChildByTypeIdx(java.lang.Class,
	 *      int)
	 */
	public boolean removeChildByTypeIdx(Class clazz, int idx) {
		if (idx < 0) {
			throw new IllegalArgumentException("parameter [idx] should be >= 0.(" + idx
					+ ")");
		}
		if (children == null || children.size() <= idx) {
			return false;
		}
		for (Iterator it = children.iterator(); it != null && it.hasNext();) {
			Object obj = it.next();
			if (clazz.isInstance(obj)) {
				if (idx == 0) {
					it.remove();
					return true;
				}
				idx--;
			}
		}
		return false;
	}

	/*
	 * (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(IBasicDefVisitor 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 IBasicDef) {
				IBasicDef basicDef = (IBasicDef) next;
				basicDef.accept(visitor);
			}
		}

	}
}
