package jetbrains.mps.lang.dataFlow.framework;

/*Generated by MPS */

import jetbrains.mps.annotations.GeneratedClass;
import java.util.Map;
import jetbrains.mps.lang.dataFlow.framework.instructions.Instruction;
import java.util.HashMap;
import java.util.List;
import java.util.ArrayList;
import java.util.Queue;
import java.util.LinkedList;
import java.util.Collections;

@GeneratedClass(node = "r:3dddb2c7-b2ba-4381-896a-2e702ca1fb6e(jetbrains.mps.lang.dataFlow.framework)/4074113095384031625", model = "r:3dddb2c7-b2ba-4381-896a-2e702ca1fb6e(jetbrains.mps.lang.dataFlow.framework)")
public class AnalyzerRunner<E> {
  protected Program myProgram;
  protected DataFlowAnalyzer<E> myAnalyzer;
  public AnalyzerRunner(Program program, DataFlowAnalyzer<E> analyzer) {
    myProgram = program;
    myAnalyzer = analyzer;
  }
  public AnalysisResult<E> analyze() {
    Map<ProgramState, E> stateValues = doAnalyze();
    Map<Instruction, E> result = new HashMap<>();
    for (Instruction i : myProgram.getInstructions()) {
      List<E> input = new ArrayList<>();
      input.add(stateValues.get(new ProgramState(i, true)));
      input.add(stateValues.get(new ProgramState(i, false)));
      result.put(i, myAnalyzer.merge(myProgram, input));
    }
    return new AnalysisResult<>(myProgram, myAnalyzer, stateValues, result);
  }
  private Map<ProgramState, E> doAnalyze() {
    Map<ProgramState, E> stateValues = new ProgramStateMap<>(myProgram);
    for (ProgramState ps : myProgram.getStates()) {
      stateValues.put(ps, myAnalyzer.initial(myProgram));
    }
    AnalysisDirection direction = myAnalyzer.getDirection();
    Map<ProgramState, List<ProgramState>> dependencies = new ProgramStateMap<>(myProgram);
    Map<ProgramState, List<ProgramState>> dependents = new ProgramStateMap<>(myProgram);
    for (ProgramState ps : myProgram.getStates()) {
      dependencies.put(ps, direction.dependencies(ps));
      dependents.put(ps, direction.dependents(ps));
    }
    Queue<ProgramState> workList = new LinkedList<>();
    for (Instruction i : myProgram.getInstructions()) {
      workList.add(new ProgramState(i, false));
      workList.add(new ProgramState(i, true));
    }
    while (!(workList.isEmpty())) {
      ProgramState current = workList.remove();
      List<E> input = new ArrayList<>();
      for (ProgramState s : dependencies.get(current)) {
        input.add(stateValues.get(s));
      }
      E oldValue = stateValues.get(current);
      E mergedValue = myAnalyzer.merge(myProgram, input);
      E newValue;
      if (myAnalyzer instanceof DataFlowAnalyzerBase) {
        newValue = ((DataFlowAnalyzerBase<E>) myAnalyzer).fun(mergedValue, current, Collections.unmodifiableMap(stateValues));
      } else {
        newValue = myAnalyzer.fun(mergedValue, current);
      }
      if (!(newValue.equals(oldValue))) {
        stateValues.put(current, newValue);
        workList.addAll(dependents.get(current));
      }
    }
    return stateValues;
  }
  public Program getProgramCopy() {
    return myProgram;
  }
}
