package soottocfg.cfg.statement;

import java.util.HashSet;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.Set;

import soottocfg.cfg.SourceLocation;
import soottocfg.cfg.expression.Expression;
import soottocfg.cfg.expression.IdentifierExpression;
import soottocfg.cfg.variable.ClassVariable;
import soottocfg.cfg.variable.Variable;

/**
 * @author schaef
 * @author rodykers
 *
 */
public class PullStatement extends Statement {

	private static final long serialVersionUID = 9221818898828469398L;
	private final ClassVariable classConstant;
	private IdentifierExpression object;
	private final List<IdentifierExpression> left;

	private final List<Expression> ghostExpressions;
	
	private Set<PushStatement> canAffect;
	
	public PullStatement(SourceLocation loc, ClassVariable c, IdentifierExpression obj,
			List<IdentifierExpression> lhs) {
		this(loc, c, obj, lhs, null);
	}
	/**
	 * @param loc
	 */
	public PullStatement(SourceLocation loc, ClassVariable c, IdentifierExpression obj,
			List<IdentifierExpression> lhs, List<Expression> ghostExp) {
		super(loc);
		classConstant = c;
		object = obj;
		left = new LinkedList<IdentifierExpression>(lhs);
		this.ghostExpressions = new LinkedList<Expression>();
		if (ghostExp!=null) {
			this.ghostExpressions.addAll(ghostExp);
		}
		assert (c.getAssociatedFields().length == left.size());
		this.canAffect = new HashSet<PushStatement>();
	}

	public List<IdentifierExpression> getLeft() {
		return left;
	}
	
    public List<Expression> getGhostExpressions() {
    	return this.ghostExpressions;
    }
		
    public ClassVariable getClassSignature() {
        return classConstant;
    }

    public Expression getObject() {
        return object;
    }

	@Override
	public Set<IdentifierExpression> getUseIdentifierExpressions() {
		Set<IdentifierExpression> used = new HashSet<IdentifierExpression>();
		used.add(object);
//		for (Expression e: ghostExpressions) {
//			used.addAll(e.getUseIdentifierExpressions());
//		}
		return used;
	}

	@Override
	public Set<IdentifierExpression> getDefIdentifierExpressions() {
		Set<IdentifierExpression> res = new HashSet<IdentifierExpression>();
		res.addAll(left);
		for (Expression e: ghostExpressions) {
			res.addAll(e.getUseIdentifierExpressions());
		}

		return res;
	}

	public Set<PushStatement> getAffectingPushes() {
		return canAffect;
	}
	
	public void canAffect(PushStatement push) {
		canAffect.add(push);
	}

	public void canAffect(Set<PushStatement> pushes) {
		canAffect.addAll(pushes);
	}
	
	@Override
	public String toString() {
		StringBuilder sb = new StringBuilder();
		String comma = "";
		for (IdentifierExpression v : left) {
			sb.append(comma);
			sb.append(v);
			comma = ", ";
		}
		sb.append(" := pull(");
		sb.append(classConstant.getName());
		sb.append(", ");
		sb.append(object);
		for (Expression e : this.ghostExpressions) {
			sb.append(", ");
			sb.append(e);
		}
		sb.append(")");
		sb.append("[");
		comma = "";
		for (PushStatement push : canAffect) {
			sb.append(comma);
			sb.append(push.getID());
			comma = ", ";
		}
		sb.append("]");
		return sb.toString();
	}

	@Override
	public PullStatement deepCopy() {
		List<IdentifierExpression> leftCopy = new LinkedList<IdentifierExpression>();
		for (IdentifierExpression e : left) {
			leftCopy.add(e.deepCopy());
		}
		List<Expression> ghostCopy = new LinkedList<Expression>();
		for (Expression e : ghostExpressions) {
			ghostCopy.add(e.deepCopy());
		}
		return new PullStatement(getSourceLocation(), classConstant, object.deepCopy(), leftCopy, ghostCopy);
	}

	@Override
	public PullStatement substitute(Map<Variable, Variable> subs) {
		List<IdentifierExpression> leftCopy = new LinkedList<IdentifierExpression>();
		for (IdentifierExpression e : left) {
			leftCopy.add(e.substitute(subs));
		}
		List<Expression> ghostCopy = new LinkedList<Expression>();
		for (Expression e : ghostExpressions) {
			ghostCopy.add(e.substitute(subs));
		}
		
		return new PullStatement(getSourceLocation(), classConstant, object.substitute(subs), leftCopy, ghostCopy);
	}

	@Override
	public PullStatement substituteVarWithExpression(Map<Variable, Expression> subs) {
		List<IdentifierExpression> leftCopy = new LinkedList<IdentifierExpression>();
		for (IdentifierExpression e : left) {
			leftCopy.add(e.deepCopy());
		}
		List<Expression> ghostCopy = new LinkedList<Expression>();
		for (Expression e : ghostExpressions) {
			ghostCopy.add(e.substituteVarWithExpression(subs));
		}
		
//		Verify.verify(!subs.containsKey(object.getVariable()));
		return new PullStatement(getSourceLocation(), classConstant, object.deepCopy(), leftCopy, ghostCopy);
	}
	
}
