package ash.reverse.struct;
import java.util.List;
import java.util.ArrayList;
import java.util.Map;
import java.util.HashMap;
import java.io.PrintStream;
import ash.reverse.parser.JavaType;

/**
 * クラスの規模情報とクラスに所属するフィールド群とメソッド群を管理する。
 */
public class ClassRecord extends GroupRecord {
	private String superClass;
	private List<String> interfaces = new ArrayList<String>();
	private List<String> enumList = new ArrayList<String>();
	private int id;
	private JavaType jtype;

	public String superClass() { return superClass; }
	public List<String> interfaces() { return interfaces; }
	public int id() { return id; }
	public void id(int id) { this.id = id; }
	public boolean isEnum() { return jtype == JavaType.ENUM; }
	@Override public boolean isInterface() { return jtype==JavaType.INTERFACE; }
	public String symbol() { return jtype.symbol(); }

	public List<String> enumList() { return enumList; }
	public void addEnum(String value) { enumList.add(value); }

	/**
	 * 内部クラスを特定するための修飾名を返す。
	 */
	public String qcn() {
		String qcn = name;
		for(GroupRecord w = owner; w instanceof ClassRecord; w = w.owner) {
			qcn = ((ClassRecord)w).name + "." + qcn;
		}
		return qcn;
	}

	public ClassRecord(String type, String name, int lineno, GroupRecord owner) {
		super(type, name, lineno, owner);
		jtype = JavaType.value(type);
	}

	/**
	 * スーパクラスと実装すべきインタフェース群を解析し、それぞれ
	 * superClass と interfaces に設定する。
	 * @param decl クラス宣言部<br>
	 * declの例: public class C1<T,E> extends S1 implements I1, I2
	 */
	@Override protected void paraseDecl(String decl) {
		String[] tokens = decl.split("[,]?\\s+");
		String prevToken = "null";
		for(String token : tokens) {
			if(token.equals("extends") || token.equals("implements")) {
				prevToken = token;
			} else {
				if(prevToken.equals("extends")) {
					superClass = stripGenerics(token);
					prevToken = "null";
				} else if(prevToken.equals("implements")) {
					interfaces.add(stripGenerics(token));
				} else {
					prevToken = token;
				}
			}
		}
	}

	private String stripGenerics(String token) {
		int index = token.indexOf('<');
		return (index < 0) ? token : token.substring(0, index);
	}

	/** このクラスの現在解析中のメソッド */
	private MethodRecord theMethod;

	/** メソッドを登録する。 */
	public MethodRecord addMethod(String name, int lineno) {
		theMethod = new MethodRecord(name, lineno, this);
		members.add(theMethod);
		return theMethod;
	}
	/** 現在解析中のメソッドのメソッド規模を設定する。 */
	public void setMethodLoc(int n, int lineno) {
		theMethod.addLoc(n);
		theMethod.setLineEnd(lineno);
	}
	/** 現在解析中のメソッドの本体記述のトークンを追加する。 */
	public void addMethodToken(String sval) {
		theMethod.addToken(sval);
	}

	/**
	 * 現在解析中のメソッドのシグニチャを設定する。
	 * @param signature 現在処理中のメソッドのシグニチャ
	 */
	public void setMethodSignature(String signature) {
		theMethod.setSignature(signature);
	}

	/**
	 * クラスのLOC情報を出力する。
	 * @param out レポートの出力先
	 * @param nest ネストの深さに応じた空白文字列（１レベル２文字の空白）
	 */
	public void printLoc(PrintStream out, String nest) {
		int nMethods = 0;
		int mloc = 0;
		for(ObjectRecord rec : members) {
			if(rec.type.equals("method")) {
				nMethods++;
				mloc += rec.loc;
			}
		}
		// クラス名、クラスLOC、メソッド数、メソッドLOCの合計を出力する
		out.println(nest + this + " " + nMethods + " " + mloc);
	}

	/** エラーメッセージの文字列を組み立てる */
	@Override public String errMessage() {
		String methodInfo = (theMethod!=null) ? "\n"+theMethod.errMessage() : "";
		return super.errMessage() + methodInfo;
	}

	/**
	 * Visitorパターンの受け入れ側メソッド。訪問者を受け入れる。
	 * @param visitor このクラス記録を閲覧したい訪問者
	 */
	@Override public void accept(ObjectVisitor visitor) {
		visitor.visit(this);
	}
}
