package ash.reverse.struct;
import java.util.List;
import java.util.ArrayList;
import java.util.regex.Pattern;
import java.util.regex.Matcher;
import java.io.PrintStream;
import ash.reverse.parser.Mod;
import ash.reverse.parser.Visibility;

/**
 * Javaソースコードのクラス情報を保持するデータ構造のベースクラス。
 * <pre>
 *	ObjectRecord <------------+
 *	  +- GroupRecord----------+
 *	  |    |- FileRecord -----+
 *	  |    +- ClassRecord ----+
 *	  +- MethodRecord
 *	  +- FieldRecord
 * </pre>
 * ★CompositeパターンのComponenntの役割を果たす。<br>
 * ★Visitorパターンの受け入れ側の役割も果たす。
 */
public abstract class ObjectRecord {
	final static String SEP = "\t";			// UMLファイル形式のセパレータ
	GroupRecord owner;		// このオブジェクトを所有しているグループ
	String type;			// package, class, interface, enum, method, field
	String name = "";		// 名前（パッケージ名、クラス名、メソッド名など）
	int loc = 0;			// 有効コード行数
	int startLine;			// 定義の開始行番号
	int endLine;			// 定義の終了行番号
	String signature;		// 定義情報
	Visibility visibility;	// 可視性
	int modifiers;			// 修飾子情報

	/* アクセッサの定義 */
	public GroupRecord owner() { return owner; }
	public String type() { return type; }
	public void type(String type) { this.type = type; }
	public String name() { return name; }
	public void name(String name) { this.name = name; }
	public String signature() { return signature; }
	public Visibility visibility() { return visibility; }
	public void visibility(Visibility vis) { this.visibility = vis; }
	public boolean isPublic() { return (visibility == Visibility.PUBLIC); }
	public int modifiers() { return modifiers; }
	public void modifiers(int modifiers) { this.modifiers = modifiers; }
	public boolean isAbstract() { return Mod.ABSTRACT.in(modifiers); }
	public boolean isInterface() { return false; }

	public ObjectRecord root() { return (owner != null) ? owner.root() : this; }

	/**
	 * 完全修飾名を返す。
	 * @return 完全修飾名
	 */
	public String fqcn() {
		if(owner == null) return name;
		String ownerpath = owner.fqcn();
		if(ownerpath == null || ownerpath.isEmpty()) return name;
		return owner.fqcn() + "." + name;
	}

	ObjectRecord(String type, String name, int lineno, GroupRecord owner) {
		this.type = type;
		this.name = name;
		this.startLine = lineno;
		this.owner = owner;
	}

	/** 有効コード行数を追加する。 */
	public void addLoc(int n) { loc += n; }
	/** 終了行番号を設定する。 */
	public void setLineEnd(int lineno) { endLine = lineno; }

	/** メソッド数をカウントする。 */
	protected int countMethods() { return 0; }
	/** メソッドの行数を集計する。 */
	protected int sumMethodLoc() { return 0; }

	/** プリント出力する文字列を組み立てる。 */
	@Override public String toString() {
		return type + " " + name + " " + loc;
	}
	/** エラーメッセージの文字列を組み立てる。 */
	public String errMessage() {
		return type + " " + name + " at line " + startLine;
	}

	/**
	 * 定義情報を設定する。後の解析の便宜のために、型パラメータを示す
	 * カギカッコ内(<>)の文字列中に空白文字列が含まれないように、
	 * 文字列を加工する。
	 * <pre>
	 * Map<String, Color> --> Map<String,Color>
	 * Store<T extends Entity> --> Store<T+extends+Entity>
	 * </pre>
	 */
	public void setSignature(final String sig) {
		StringBuilder sb = new StringBuilder();
		int level = 0;
		char b = 0;
		for(int i = 0; i < sig.length(); i++) {
			char c = sig.charAt(i);
			if(level > 0) {
				if(c == ' ') {
					// [,<> ] に後続する空白文字をスキップする。
					if(b == ',' || b == '<' || b == '>' || b == SPACEHOLDER)
						continue;
					c = SPACEHOLDER;
				}
			}
			sb.append(c);
			if(c == '<') level++;
			if(c == '>') level--;
			b = c;
		}
		signature = sb.toString();
		paraseDecl(signature);
	}
	static final char SPACEHOLDER = '+';

	/**
	 * アノテーション情報を取り除いたシグニチャ情報を返す。
	 * @return シグニチャ文字列
	 */
	public String getSignature() {
		int i = signature.lastIndexOf('\t');
		return (i < 0) ? signature : signature.substring(i+1);
	}

	/**
	 * メソッドまたはフィールドの宣言部を解析し、データ型を typeに設定する。
	 * @param decl メンバ宣言部
	 * <pre>
	 * メソッド宣言の例: T<E,K> name(T1 p1, T2 p2, ) ...
	 * フィールド宣言の例: T<E,K> name ...
	 * </pre>
	 */
	protected void paraseDecl(String decl) {
		String[] tokens = extractDecl(decl).split("[\\s(]");
		String prevToken = "null";
		for(String token : tokens) {
			if(token.equals(name)) {
				type = prevToken;
			} else if(Mod.mask(token) == 0 && Visibility.undef(token) ) {
				prevToken = token;
			}
		}
	}
	/**
	 * 宣言部からアノテーションとメソッドの引数を取り除く。
	 */
	private String extractDecl(String decl) {
		int i1 = decl.lastIndexOf('\t');
		if(i1 >= 0) decl = decl.substring(i1+1);
		int i2 = decl.indexOf('(');
		if(i2 >= 0) decl = decl.substring(0, i2);
		return decl;
	}

	/** 本体の記述要素であるWORD型トークンのリスト */
	private List<String> tokens = new ArrayList<String>();
	/**
	 * 本体を記述したトークンを追加する。
	 * @param sval トークン文字列
	 */
	public void addToken(String sval) { tokens.add(sval); }
	/**
	 * 本体を記述したトークンリストを返す。
	 * @return トークンのリスト
	 */
	public List<String> getTokens() { return tokens; }

	/**
	 * Visitorパターンの受け入れ側メソッド
	 * @param visitor
	 */
	abstract public void accept(ObjectVisitor visitor);

	/**
	 * デバッグ用トレース文
	 * @param msg メッセージ
	 */
	boolean trace(Object msg) {
		System.err.println(msg);
		return true;
	}
}
