package us.msu.cse.repair.core.parser.ingredient;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;

import org.eclipse.jdt.core.dom.ASTNode;
import org.eclipse.jdt.core.dom.Statement;

import us.msu.cse.repair.core.parser.ModificationPoint;
import us.msu.cse.repair.core.parser.SeedStatement;
import us.msu.cse.repair.core.parser.SeedStatementInfo;
import us.msu.cse.repair.core.parser.StatementInfoExtractor;
import us.msu.cse.repair.core.parser.VarInfo;
import us.msu.cse.repair.core.util.visitors.VarConvASTVisitor;

public class VarTypeMatchIngredientScreener extends AbstractIngredientScreener {
	public VarTypeMatchIngredientScreener(List<ModificationPoint> modificationPoints,
			Map<SeedStatement, SeedStatementInfo> seedStatements, IngredientMode ingredientMode) {
		super(modificationPoints, seedStatements, ingredientMode);
		// TODO Auto-generated constructor stub
	}

	@Override
	protected void screenIngredients(ModificationPoint mp, IngredientMode ingredientMode, boolean includeSelf) {
		// TODO Auto-generated method stub
		Set<SeedStatement> ingredientSet = new HashSet<SeedStatement>();
		for (Map.Entry<SeedStatement, SeedStatementInfo> entry : seedStatements.entrySet()) {
			if (canPreFiltered(mp, entry, ingredientMode, includeSelf))
				continue;

			Statement seed = entry.getKey().getStatement();
			seed = getVarTypeMatchedStatement(seed, mp);
			if (seed != null)
				ingredientSet.add(new SeedStatement(seed));
		}

		List<Statement> ingredients = new ArrayList<Statement>();
		for (SeedStatement seedStatement : ingredientSet)
			ingredients.add(seedStatement.getStatement());

		mp.setIngredients(ingredients);
	}

	Statement getVarTypeMatchedStatement(Statement seed, ModificationPoint mp) {
		StatementInfoExtractor sie = new StatementInfoExtractor(seed);
		sie.extract();

		Map<String, VarInfo> vars = sie.getVars();
		Map<String, VarInfo> thisVars = sie.getThisVars();
		Map<String, VarInfo> superVars = sie.getSuperVars();

		Map<String, String> varMatchMap = new HashMap<String, String>();
		Map<String, String> thisVarMatchMap = new HashMap<String, String>();
		Map<String, String> superVarMatchMap = new HashMap<String, String>();

		Map<String, VarInfo> declaredFields = new HashMap<String, VarInfo>(mp.getDeclaredFields());
		Map<String, VarInfo> inheritedFields = new HashMap<String, VarInfo>(mp.getInheritedFields());
		Map<String, VarInfo> outerFields = new HashMap<String, VarInfo>(mp.getOuterFields());
		Map<String, VarInfo> localVars = new HashMap<String, VarInfo>(mp.getLocalVars());

		if (IngredientUtil.isInMethodScope(seed, mp, sie)) {
			IngredientUtil.findSuperVars(superVars, inheritedFields);
			IngredientUtil.findThisVars(thisVars, declaredFields, inheritedFields);
			IngredientUtil.findVars(vars, localVars, declaredFields, inheritedFields, outerFields);
			if (!IngredientUtil.doSuperVarMatch(superVarMatchMap, superVars, inheritedFields))
				return null;
			else if (!IngredientUtil.doThisVarMatch(thisVarMatchMap, thisVars, declaredFields, inheritedFields))
				return null;
			else if (!IngredientUtil.doVarMatch(varMatchMap, vars, localVars, declaredFields, inheritedFields,
					outerFields))
				return null;
			else if (varMatchMap.size() > 0 || thisVarMatchMap.size() > 0 || superVarMatchMap.size() > 0) {
				seed = (Statement) ASTNode.copySubtree(seed.getAST(), seed);
				VarConvASTVisitor visitor = new VarConvASTVisitor(varMatchMap, thisVarMatchMap, superVarMatchMap,
						sie.getVarIDs());
				seed.accept(visitor);
			}

			return seed;
		} else
			return null;

	}

}
