package cd.transform.optimizers;

import cd.ir.Ast;
import cd.ir.Ast.*;
import cd.ir.BasicBlock;
import cd.ir.ControlFlowGraph;
import cd.ir.ExprVisitor;
import cd.ir.Symbol.VariableSymbol;
import cd.transform.analysis.LiveVarAnalysis;

import java.util.*;

public class WriteOnlyVarRemoval extends OptimizerVisitor<Set<VariableSymbol>> {
	@Override
	public boolean go(ControlFlowGraph cfg) {
		LiveVarAnalysis analysis = new LiveVarAnalysis(cfg);
		for (BasicBlock block : cfg.allBlocks) {
			Set<VariableSymbol> state = new HashSet<>(analysis.outStateOf(block));
			if (block.condition != null)
				new LiveVarAnalysis.Visitor().visit(block.condition, state);
			for (ListIterator<Stmt> iterator = block.stmts.listIterator(block.stmts.size()); iterator.hasPrevious(); ) {
				Stmt stmt = iterator.previous();
				Ast res = visit(stmt, state);
				// TODO: this code should work? even if we iterate through the newly created Stmt nodes
				if (stmt != res) {
					iterator.remove();
					assert res instanceof Seq;
					Seq seq = (Seq) res;
					for (Ast ast : seq.rwChildren) {
						assert ast instanceof Stmt;
						iterator.add((Stmt) ast);
					}
					nodeReplaced(stmt, res);
				}
			}
		}
		return hasChanged();
	}

	@Override
	public Ast assign(Assign ast, Set<VariableSymbol> arg) {
		if (ast.left() instanceof Var && !arg.contains(((Var) ast.left()).sym)) {
			List<Ast> methodCalls = new ArrayList<>();
			new MethodCallFinder().visit(ast.right(), methodCalls);
			return new Seq(methodCalls);
		} else {
			return ast;
		}
	}

	private class MethodCallFinder extends ExprVisitor<Void, List<Ast>> {
		@Override
		public Void methodCall(Ast.MethodCallExpr ast, List<Ast> arg) {
			arg.add(new MethodCall(ast));
			return null;
		}
	}
}
