package jp.sourceforge.asclipse.as3.internal.element;

import java.util.ArrayList;
import java.util.List;

import jp.sourceforge.asclipse.as3.adapter.IAS3ElementAdapter;
import jp.sourceforge.asclipse.as3.element.AS3Element;
import jp.sourceforge.asclipse.as3.element.AS3Package;
import jp.sourceforge.asclipse.as3.element.AS3Type;
import jp.sourceforge.asclipse.as3.util.CommonTreeUtil;
import jp.sourceforge.asclipse.as3.util.CommonTreeUtil.Position;

import org.antlr.runtime.tree.CommonTree;

/**
 * {@link AS3Element}の内部用の実装のための抽象クラス。
 * <p>とりあえず、ソースのOutline表示に必要な要素を揃えるための暫定実装。</p>
 * @author shin1ogawa
 */
@SuppressWarnings("serial")
public abstract class AbstractAS3Element implements AS3Element, ModifiableAS3Element {

	/** 子要素のリスト */
	protected List<AS3Element> children = new ArrayList<AS3Element>(1);

	private final int startLine;

	private final int startCharPositionInLine;

	private final int endLine;

	private final int endCharPositionInLine;

	private AS3Element parent;

	private List<IAS3ElementAdapter> adapters = new ArrayList<IAS3ElementAdapter>(0);


	/**
	 * Constructor.
	 * @param tree
	 * @category constructor
	 */
	public AbstractAS3Element(CommonTree tree) {
		Position startPos = CommonTreeUtil.getStartPos(tree);
		Position endPos = CommonTreeUtil.getEndPos(tree);
		this.startLine = startPos.line;
		this.startCharPositionInLine = startPos.charPositionInLine;
		this.endLine = endPos.line;
		this.endCharPositionInLine = endPos.charPositionInLine;
	}

	/**
	 * Constructor.
	 * <p>MXMLEmbeddedな場合は奇麗なツリーを構築できないため、その場合の構築時用。</p>
	 * 
	 * @param startLine
	 * @param startCharPositionInLine
	 * @param endLine
	 * @param endCharPositionInLine
	 * @category constructor
	 */
	public AbstractAS3Element(int startLine, int startCharPositionInLine, int endLine,
			int endCharPositionInLine) {
		this.startLine = startLine;
		this.startCharPositionInLine = startCharPositionInLine;
		this.endLine = endLine;
		this.endCharPositionInLine = endCharPositionInLine;
	}

	public int getChildCount() {
		return children.size();
	}

	public int getStartLine() {
		return startLine;
	}

	public int getCharPositionInLine() {
		return startCharPositionInLine;
	}

	public int getEndLine() {
		return endLine;
	}

	public int getEndCharPositionInLine() {
		return endCharPositionInLine;
	}

	@SuppressWarnings("deprecation")
	public int getElementType() {
		throw new UnsupportedOperationException("deprecated");
	}

	@SuppressWarnings("deprecation")
	public void setElementType(int elementType) {
		throw new UnsupportedOperationException("deprecated");
	}

	public List<AS3Element> getChildren() {
		return children;
	}

	public void addChildren(AS3Element child) {
		children.add(child);
		if (child instanceof ModifiableAS3Element) {
			((ModifiableAS3Element) child).setParent(this);
		}
	}

	public void setParent(AS3Element parent) {
		this.parent = parent;
	}

	public AS3Element getParent() {
		return parent;
	}

	public boolean hasAdapter(Class<?> clazz) {
		for (Object adapter : adapters) {
			if (clazz.isAssignableFrom(adapter.getClass())) {
				return true;
			}
		}
		return false;
	}

	@SuppressWarnings("unchecked")
	public <T>T getAdapter(Class<T> clazz) {
		for (Object adapter : adapters) {
			if (clazz.isAssignableFrom(adapter.getClass())) {
				return (T) adapter;
			}
		}
		return null;
	}

	public void addAdapter(IAS3ElementAdapter adapter) {
		adapters.add(adapter);
		adapter.connect(this);
	}

	public void dispose() {
		for (IAS3ElementAdapter adapter : adapters) {
			adapter.disconnect();
		}
		adapters.clear();
		for (AS3Element child : children) {
			child.dispose();
		}
		children.clear();
		parent = null;
	}

	@Override
	public String toString() {
		StringBuilder b = new StringBuilder();
		b.append(String.format("[%5d,%3d]-[%5d,%3d] ", startLine, startCharPositionInLine, endLine,
				endCharPositionInLine));
		b.append(getTitle());
		return b.toString();
	}

	public AS3Type getEnclosureType() {
		if (getParent() == null) {
			return null;
		}
		return getParent().getEnclosureType();
	}

	public AS3Package getEnclosurePackage() {
		if (getParent() == null) {
			return null;
		}
		return getParent().getEnclosurePackage();
	}
}
