package jp.sourceforge.asclipse.as3.util;

import java.io.Serializable;
import java.util.List;

import org.antlr.runtime.CharStream;
import org.antlr.runtime.Token;
import org.antlr.runtime.tree.CommonTree;

/**
 * {@link CommonTree}用のユーティリティクラス。
 * @author shin1ogawa
 */
public class CommonTreeUtil {

	private CommonTreeUtil() {
	}


	/**
	 * {@link Token}をSerializable可能にしたクラス。
	 * @author shin1ogawa
	 */
	@SuppressWarnings("serial")
	public static class SerializableToken implements Serializable, Token {

		private final String text;

		final int line;

		final int charPositionInLine;

		final int channel;

		final int tokenIndex;

		final int type;


		/**
		 * Constructor.
		 * @param token
		 * @category constructor
		 */
		public SerializableToken(Token token) {
			this.text = token.getText();
			this.line = token.getLine();
			this.charPositionInLine = token.getCharPositionInLine();
			this.channel = token.getChannel();
			this.tokenIndex = token.getTokenIndex();
			this.type = token.getType();
		}

		public int getLine() {
			return line;
		}

		public int getCharPositionInLine() {
			return charPositionInLine;
		}

		public int getChannel() {
			return channel;
		}

		public int getTokenIndex() {
			return tokenIndex;
		}

		public int getType() {
			return type;
		}

		public String getText() {
			return text;
		}

		public CharStream getInputStream() {
			throw new UnsupportedOperationException();
		}

		public void setChannel(int channel) {
			throw new UnsupportedOperationException();
		}

		public void setCharPositionInLine(int pos) {
			throw new UnsupportedOperationException();
		}

		public void setInputStream(CharStream input) {
			throw new UnsupportedOperationException();
		}

		public void setLine(int line) {
			throw new UnsupportedOperationException();
		}

		public void setText(String text) {
			throw new UnsupportedOperationException();
		}

		public void setTokenIndex(int index) {
			throw new UnsupportedOperationException();
		}

		public void setType(int ttype) {
			throw new UnsupportedOperationException();
		}
	}


	/**
	 * @param tree
	 * @return {@link CommonTree}の配下にあるノードの中で最も早い開始ポジション
	 */
	public static Position getStartPos(CommonTree tree) {
		Token token = tree.getToken();
		Position position = new Position(token.getLine(), token.getCharPositionInLine());
		if (tree.getChildCount() == 0) {
			return position;
		} else {
			List<?> children = tree.getChildren();
			for (Object child : children) {
				Position startPos = getStartPos((CommonTree) child);
				if (startPos.line < position.line) {
					position = startPos;
				} else if (startPos.line == position.line
						&& startPos.charPositionInLine < position.charPositionInLine) {
					position = startPos;
				}
			}
			return position;
		}
	}

	/**
	 * @param tree
	 * @return {@link CommonTree}の最終ポジション
	 */
	public static Position getEndPos(CommonTree tree) {
		Token token = tree.getToken();
		Position position =
				new Position(token.getLine(), token.getCharPositionInLine()
						+ token.getText().getBytes().length);
		if (tree.getChildCount() == 0) {
			return position;
		} else {
			List<?> children = tree.getChildren();
			for (Object child : children) {
				Position endPos = getEndPos((CommonTree) child);
				if (endPos.line > position.line) {
					position = endPos;
				} else if (endPos.line == position.line
						&& endPos.charPositionInLine > position.charPositionInLine) {
					position = endPos;
				}
			}
			return position;
		}
	}


	/**
	 * 位置情報を表すクラス。
	 * @author shin1ogawa
	 */
	public static class Position {

		/** 行番号 */
		public final int line;

		/** 行番号内での列番号 */
		public final int charPositionInLine;


		/**
		 * Constructor.
		 * @param line
		 * @param charPositionInLine
		 * @category constructor
		 */
		public Position(int line, int charPositionInLine) {
			super();
			this.line = line;
			this.charPositionInLine = charPositionInLine;
		}
	}
}
