package ca.queensu.cs.mase.generator.capsules.methods;

import ca.queensu.cs.mase.urml.State_;
import ca.queensu.cs.mase.urml.Transition;
import com.google.common.base.Objects;
import java.util.Deque;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import org.eclipse.emf.ecore.EObject;
import org.eclipse.xtend2.lib.StringConcatenation;
import org.eclipse.xtext.EcoreUtil2;

/**
 * Used by CapsuleGenerator as a printer to
 * emit possible transitions
 * @author Keith
 */
@SuppressWarnings("all")
public class ExecuteTransitionGenerator {
  private List<Transition> allTransitions;
  
  private Map<Transition, Integer> nonameTrans;
  
  public ExecuteTransitionGenerator(final List<Transition> allTrans, final Map<Transition, Integer> nonameTrans) {
    this.allTransitions = allTrans;
    this.nonameTrans = nonameTrans;
  }
  
  public CharSequence generate() {
    return this.transitions();
  }
  
  /**
   * Generates code for executing transition
   * @return generated code
   */
  private CharSequence transitions() {
    StringConcatenation _builder = new StringConcatenation();
    _builder.append("/**");
    _builder.newLine();
    _builder.append(" ");
    _builder.append("* Executes the transition t and returns whether the");
    _builder.newLine();
    _builder.append(" ");
    _builder.append("* destination state of t is final.  ");
    _builder.newLine();
    _builder.append(" ");
    _builder.append("*/");
    _builder.newLine();
    _builder.append("public boolean transitionAndIfFinal(");
    _builder.newLine();
    _builder.append("\t\t");
    _builder.append("Transition t, List<? extends CommonObj> params) {");
    _builder.newLine();
    _builder.append("\t");
    _builder.append("switch (t.name) {");
    _builder.newLine();
    {
      for(final Transition t : this.allTransitions) {
        {
          boolean _isInit = t.isInit();
          boolean _not = (!_isInit);
          if (_not) {
            {
              String _name = t.getName();
              boolean _tripleEquals = (_name == null);
              if (_tripleEquals) {
                _builder.append("\t\t");
                _builder.append("case \"_noname_");
                Integer _get = this.nonameTrans.get(t);
                _builder.append(_get, "\t\t");
                _builder.append("\":");
                _builder.newLineIfNotEmpty();
              } else {
                _builder.append("\t\t");
                _builder.append("case \"");
                String _name_1 = t.getName();
                _builder.append(_name_1, "\t\t");
                _builder.append("\":");
                _builder.newLineIfNotEmpty();
              }
            }
            _builder.append("\t\t");
            _builder.append("\t");
            _builder.append("if (_state_");
            String _name_2 = t.getFrom().getName();
            _builder.append(_name_2, "\t\t\t");
            _builder.append(" != currentState)");
            _builder.newLineIfNotEmpty();
            _builder.append("\t\t");
            _builder.append("\t\t");
            _builder.append("throw new CurrentStateIsNotSourceStateInTransitionException();");
            _builder.newLine();
            _builder.append("\t\t");
            _builder.append("\t");
            _builder.append("synchronized (lock) {");
            _builder.newLine();
            _builder.append("\t\t");
            _builder.append("\t\t");
            String _genTransitionSwitchCase = this.genTransitionSwitchCase(t);
            _builder.append(_genTransitionSwitchCase, "\t\t\t\t");
            _builder.newLineIfNotEmpty();
            _builder.append("\t\t");
            _builder.append("\t\t");
            _builder.append("currentState = _state_");
            String _name_3 = t.getTo().getName();
            _builder.append(_name_3, "\t\t\t\t");
            _builder.append(";");
            _builder.newLineIfNotEmpty();
            _builder.append("\t\t");
            _builder.append("\t\t");
            _builder.append("return ");
            boolean _isFinal = t.getTo().isFinal();
            _builder.append(_isFinal, "\t\t\t\t");
            _builder.append(";");
            _builder.newLineIfNotEmpty();
            _builder.append("\t\t");
            _builder.append("\t");
            _builder.append("}");
            _builder.newLine();
          }
        }
      }
    }
    _builder.append("\t\t");
    _builder.append("default:");
    _builder.newLine();
    _builder.append("\t\t\t");
    _builder.append("return false;");
    _builder.newLine();
    _builder.append("\t");
    _builder.append("}");
    _builder.newLine();
    _builder.append("}");
    _builder.newLine();
    return _builder;
  }
  
  /**
   * Generates the exit/action/entry code for transition
   * chains using the least common ancestor algorithm
   * 
   * @param t the transition chain of which the code is
   * to be generated
   * @return generated code
   */
  private String genTransitionSwitchCase(final Transition t) {
    String result = "";
    Deque<State_> fromWithAnc = this.getStatesWithAnc(t.getFrom());
    Deque<State_> toWithAnc = this.getStatesWithAnc(t.getTo());
    this.removeCommonAnc(fromWithAnc, toWithAnc);
    while ((!fromWithAnc.isEmpty())) {
      StringConcatenation _builder = new StringConcatenation();
      _builder.append("_state_");
      String _name = fromWithAnc.removeLast().getName();
      _builder.append(_name);
      _builder.append(".exit.run();");
      _builder.newLineIfNotEmpty();
      String _plus = (result + _builder);
      result = _plus;
    }
    String _xifexpression = null;
    String _name = t.getName();
    boolean _tripleEquals = (_name == null);
    if (_tripleEquals) {
      Integer _get = this.nonameTrans.get(t);
      _xifexpression = ("_noname_" + _get);
    } else {
      _xifexpression = t.getName();
    }
    String tname = _xifexpression;
    StringConcatenation _builder = new StringConcatenation();
    _builder.append("_tran_");
    _builder.append(tname);
    _builder.append(".action.accept(params);");
    _builder.newLineIfNotEmpty();
    String _plus = (result + _builder);
    result = _plus;
    while ((!toWithAnc.isEmpty())) {
      StringConcatenation _builder_1 = new StringConcatenation();
      _builder_1.append("_state_");
      String _name_1 = toWithAnc.pop().getName();
      _builder_1.append(_name_1);
      _builder_1.append(".entry.run();");
      _builder_1.newLineIfNotEmpty();
      String _plus_1 = (result + _builder_1);
      result = _plus_1;
    }
    return result;
  }
  
  /**
   * Removes common ancestors of fromWithAnc and
   * toWithAnc
   * @param fromWithAnc from state
   * @param toWithAnc to state
   */
  private void removeCommonAnc(final Deque<State_> fromWithAnc, final Deque<State_> toWithAnc) {
    State_ from = null;
    State_ to = null;
    while (true) {
      {
        from = fromWithAnc.peek();
        to = toWithAnc.peek();
        if ((((!Objects.equal(from, to)) || fromWithAnc.isEmpty()) || toWithAnc.isEmpty())) {
          return;
        }
        fromWithAnc.pop();
        toWithAnc.pop();
      }
    }
  }
  
  /**
   * Gets state along with its ancestors
   * @param s specified state
   * @return a list containing the state and its ancestors
   */
  private Deque<State_> getStatesWithAnc(final State_ s) {
    State_ state = s;
    Deque<State_> statesWithAnc = new LinkedList<State_>();
    while ((state != null)) {
      {
        statesWithAnc.push(state);
        State_ parent = this.<State_>getContainer(state.eContainer(), State_.class);
        state = parent;
      }
    }
    return statesWithAnc;
  }
  
  /**
   * Returns the container of obj, of which the container has
   * the specified class
   * @param obj the EObject to find whose container from
   * @param t the type of the container
   * @return the EObject that is the container of obj with the type t
   */
  private <T extends EObject> T getContainer(final EObject obj, final Class<T> t) {
    return EcoreUtil2.<T>getContainerOfType(obj, t);
  }
}
