/**
   * Tests the '{@link
   * no.hal.pg.quiz.runtime.service.QuizTaskService#getQAProposals(no.hal.pg.runtime.Player) <em>Get
   * QA Proposals</em>}' operation.
   * <!-- begin-user-doc -->
   * <!-- end-user-doc -->
   *
   * @see no.hal.pg.quiz.runtime.service.QuizTaskService#getQAProposals(no.hal.pg.runtime.Player)
   * @generated NOT
   */
  public void testGetQAProposals__Player() {
    QuizTaskService service = getFixture();
    QuizTask quizTask = service.getContext();
    quizTask.start();

    EList<Player> players = quizTask.getGame().getAllPlayers();
    Player player1 = players.get(0), player2 = players.get(1);

    EList<QAProposal> proposals = quizTask.getProposals();
    // the first proposal is for the second player, the rest for the first
    proposals.get(0).getPlayers().add(players.get(1));

    EList<QAProposal> proposals1 = service.getQAProposals(player1);
    assertEquals(3, proposals1.size());
    assertTrue(
        proposals1.containsAll(
            Arrays.asList(proposals.get(1), proposals.get(2), proposals.get(3))));

    EList<QAProposal> proposals2 = service.getQAProposals(player2);
    assertEquals(1, proposals2.size());
    assertTrue(proposals2.containsAll(Arrays.asList(proposals.get(0))));
  }
 public boolean containsAll(java.util.Collection<?> c) {
   return copy.containsAll(c);
 }
  /**
   * Validate the model. All checks are performed for all automata in the file. This method checks
   * that
   *
   * <ul>
   *   <li>only allowed extension are used. Allowed extension are {@link
   *       org.ect.ea.extensions.clocks.AutomatonClocksProvider}, {@link
   *       org.ect.ea.extensions.clocks.TCADataConstraintsProvider}, {@link
   *       org.ect.ea.extensions.constraints.ConstraintExtensionProvider}, {@link
   *       org.ect.ea.extensions.startstates.StartStateExtensionProvider}, {@link
   *       org.ect.ea.extensions.portnames.AutomatonPortNamesProvider}, {@link
   *       org.ect.ea.extensions.portnames.TransitionPortNamesProvider}, {@link
   *       org.ect.ea.extensions.clocks.TransitionUpdateProvider}, {@link
   *       org.ect.ea.extensions.clocks.TransitionGuardProvider}, {@link
   *       org.ect.ea.extension.stateMemory.StateMemoryExtensionProvider} and {@link
   *       org.ect.ea.extensions.clocks.StateInvariantProvider}.
   *   <li>all required extensions are used. Required extensions are {@link
   *       org.ect.ea.extensions.startstates.StartStateExtensionProvider}, {@link
   *       org.ect.ea.extensions.portnames.AutomatonPortNamesProvider} and {@link
   *       org.ect.ea.extensions.portnames.TransitionPortNamesProvider}.
   *   <li>depending on whether the automaton is a CA or a TCA, some other extensions are required
   *       and forbidden:
   *       <ul>
   *         <li>Required extensions of TCA are {@link
   *             org.ect.ea.extensions.clocks.TCADataConstraintsProvider}, {@link
   *             org.ect.ea.extensions.clocks.AutomatonClocksProvider}, {@link
   *             org.ect.ea.extensions.clocks.TransitionGuardProvider}, {@link
   *             org.ect.ea.extensions.clocks.TransitionUpdateProvider} and {@link
   *             org.ect.ea.extensions.clocks.StateInvariantProvider}. These are also the forbidden
   *             extensions for CA.
   *         <li>Forbidden extensions of TCA are {@link
   *             org.ect.ea.extensions.constraints.ConstraintExtensionProvider}. These are also the
   *             required extensions for CA.
   *       </ul>
   *   <li>all names (except for memory cells and ports) are unique within the system.
   *   <li>memory cell names may occur more than once in one automaton, but must be unique between
   *       different automata, and must not equal any other name.
   *   <li>port names may be shared between automata, but must be different from all other names
   *   <li>there exists exactly one start state per automaton.
   *   <li>untimed CA (that means without clock extensions) do not have transitions with empty name
   *       set
   *   <li>TCA transition with empty name set do not have an associated data constraint
   *   <li>there exist no names {@link org.ect.ea.extensions.clocks.ClockUtils#GLOBAL_CLOCK} or
   *       {@link org.ect.ea.extensions.clocks.ClockUtils#NO_FLOW_NAME}
   *   <li>the range of data values is well-formed, i.e. either on the form
   *       <tt>lowerBound..upperBound</tt>, with <tt>lowerBound=0</tt>, <tt>upperBound>=1</tt>, or
   *       empty (for infinite data domain)
   *   <li>integer values in the data constraints lie within the given range
   *   <li>unfolding depth is at least 1
   *   <li>the target language is one of [mathsat,msat]
   *   <li>the output file name is not empty
   * </ul>
   */
  public IStatus validateGenModel(IGenModel genModel) {

    EList<Automaton> automata = ((Automaton) genModel.getTarget()).getModule().getAutomata();

    String projectName = genModel.getProperty(IGenModel.PROJECT_NAME);
    if (projectName == null || projectName.equals("")) {
      return new Status(IStatus.ERROR, PLUGIN_ID, "Project name cannot be empty.");
    }

    String rng = genModel.getProperty(CodegenUtils.PROPERTY_RANGE);
    // rng = rng.replace("..", ",");

    String targetlang = genModel.getProperty("targetlang");

    // for temporary usage
    // String tempS = "";
    EList<String> tempL = new BasicEList<String>();
    boolean tempB;

    /* check extensions */
    /* Every automaton must support the automaton port names, transition
     * port names, and start state extension, and may support the state
     * memory extension.
     * If the automaton is a CA (untimed), it must support the constraint
     * extension, and must not support the TCA data constraint,
     * automaton clocks, transition guard, transition update and
     * invariant extension.
     * If the automaton is a TCA (timed), it must support the TCA data
     * constraint, automaton clocks, transition guard, transition update and
     * invariant extension, and must not support the constraint extension.
     * */

    // check that only allowed extensions are used
    // note that not all these extensions can be used at the same time
    final EList<String> allowedExtensions = new BasicEList<String>();
    allowedExtensions.add(ClockUtils.STATE_MEMORY_ID);
    allowedExtensions.add(ClockUtils.AUTOMATON_CLOCKS_ID);
    allowedExtensions.add(ClockUtils.TRANSITION_GUARD_ID);
    allowedExtensions.add(ClockUtils.TRANSITION_UPDATE_ID);
    allowedExtensions.add(ClockUtils.INVARIANT_ID);
    allowedExtensions.add(ClockUtils.DATA_CONSTRAINT_ID);
    allowedExtensions.add(ClockUtils.CONSTRAINT_ID);
    allowedExtensions.add(ClockUtils.AUTOMATON_PORT_NAMES_ID);
    allowedExtensions.add(ClockUtils.TRANSITION_PORT_NAMES_ID);
    allowedExtensions.add(ClockUtils.START_STATE_ID);

    final EList<String> requiredExtensions = new BasicEList<String>();
    requiredExtensions.add(ClockUtils.START_STATE_ID);
    requiredExtensions.add(ClockUtils.AUTOMATON_PORT_NAMES_ID);
    requiredExtensions.add(ClockUtils.TRANSITION_PORT_NAMES_ID);

    final EList<String> requiredCAExtensions = new BasicEList<String>();
    requiredCAExtensions.add(ClockUtils.CONSTRAINT_ID);

    final EList<String> forbiddenCAExtensions = new BasicEList<String>();
    forbiddenCAExtensions.add(ClockUtils.DATA_CONSTRAINT_ID);
    forbiddenCAExtensions.add(ClockUtils.AUTOMATON_CLOCKS_ID);
    forbiddenCAExtensions.add(ClockUtils.TRANSITION_GUARD_ID);
    forbiddenCAExtensions.add(ClockUtils.TRANSITION_UPDATE_ID);
    forbiddenCAExtensions.add(ClockUtils.INVARIANT_ID);

    final EList<String> requiredTCAExtensions = new BasicEList<String>(forbiddenCAExtensions);
    final EList<String> forbiddenTCAExtensions = new BasicEList<String>(requiredCAExtensions);

    EList<String> names = new BasicEList<String>(); // to check uniqueness of names
    Set<String> portNames =
        new HashSet<
            String>(); // port names can be shared, but must be different from other names, use set
                       // to automatically remove duplicates

    boolean timed = false;
    boolean untimed = false;
    boolean allMem = false;
    boolean noMem = false;

    for (Automaton automaton : automata) {

      // add automaton name to global name set name
      names.add(automaton.getName());

      // check that only allowed extensions are used
      final EList<String> usedExtensions = automaton.getUsedExtensionIds();
      if (!allowedExtensions.containsAll(usedExtensions)) {
        tempL.clear();
        tempL.addAll(usedExtensions);
        tempL.removeAll(allowedExtensions);
        return new Status(
            IStatus.ERROR, PLUGIN_ID, "Extension(s) used but not supported: " + tempL);
      }

      // check that all required extensions are used
      if (!usedExtensions.containsAll(requiredExtensions)) {
        tempL.clear();
        tempL.addAll(requiredExtensions);
        tempL.removeAll(usedExtensions);
        return new Status(
            IStatus.ERROR, PLUGIN_ID, automaton.getName() + "must support extension(s): " + tempL);
      }

      // check that exactly one of the constraint extensions is used, infer CA or TCA from that
      if (usedExtensions.contains(ClockUtils.DATA_CONSTRAINT_ID)) {
        timed = true;
        if (untimed == true) {
          return new Status(
              IStatus.ERROR,
              PLUGIN_ID,
              "All automata must support same constraint extension, either "
                  + ClockUtils.DATA_CONSTRAINT_ID
                  + " or "
                  + ClockUtils.CONSTRAINT_ID);
        }
      }
      if (usedExtensions.contains(ClockUtils.CONSTRAINT_ID)) {
        untimed = true;
        if (timed == true) {
          return new Status(
              IStatus.ERROR,
              PLUGIN_ID,
              "All automata must support same constraint extension, either "
                  + ClockUtils.DATA_CONSTRAINT_ID
                  + " or "
                  + ClockUtils.CONSTRAINT_ID);
        }
      }
      if (!(timed || untimed)) { // no constraint extension is used
        return new Status(
            IStatus.ERROR,
            PLUGIN_ID,
            "All automata must support same constraint extension, either "
                + ClockUtils.DATA_CONSTRAINT_ID
                + " or "
                + ClockUtils.CONSTRAINT_ID);
      }

      // if CA: check for required and forbidden extensions
      if (untimed) {
        if (!usedExtensions.containsAll(requiredCAExtensions)) {
          tempL.clear();
          tempL.addAll(requiredCAExtensions);
          tempL.removeAll(usedExtensions);
          return new Status(
              IStatus.ERROR, PLUGIN_ID, "Untimed CA must support extension(s): " + tempL);
        }
        tempL.clear();
        tempL.addAll(usedExtensions);
        tempL.addAll(forbiddenCAExtensions);
        tempL = new StringListExtension(tempL).getDuplicateEntries();
        if (!tempL.isEmpty()) {
          return new Status(
              IStatus.ERROR, PLUGIN_ID, "Untimed CA must not support extension(s): " + tempL);
        }
      }
      // if TCA: check for required and forbidden extensions
      if (timed) {

        if (!usedExtensions.containsAll(requiredTCAExtensions)) {
          tempL.clear();
          tempL.addAll(requiredTCAExtensions);
          tempL.removeAll(usedExtensions);
          return new Status(IStatus.ERROR, PLUGIN_ID, "TCA must support extension(s): " + tempL);
        }
        tempL.clear();
        tempL.addAll(usedExtensions);
        tempL.addAll(forbiddenTCAExtensions);
        tempL = new StringListExtension(tempL).getDuplicateEntries();
        if (!tempL.isEmpty()) {
          return new Status(
              IStatus.ERROR, PLUGIN_ID, "TCA must not support extension(s): " + tempL);
        }
      }

      // check that the automaton has states (otherwise, code generation does not make sense)
      if (automaton.getStates().isEmpty()) {
        return new Status(
            IStatus.ERROR,
            PLUGIN_ID,
            "Automaton does not have states, code generation does not make sense.");
      }

      // check that the automaton has transitions (otherwise, code generation does not make sense)
      if (automaton.getTransitions().isEmpty()) {
        return new Status(
            IStatus.ERROR,
            PLUGIN_ID,
            "Automaton does not have transitions, code generation does not make sense.");
      }

      // check for state memory extension
      if (usedExtensions.contains(ClockUtils.STATE_MEMORY_ID)) {
        allMem = true;
        if (noMem) {
          return new Status(
              IStatus.ERROR,
              PLUGIN_ID,
              "State memory must be support by all automata or not at all.");
        }
      } else {
        noMem = true;
        if (allMem) {
          return new Status(
              IStatus.ERROR,
              PLUGIN_ID,
              "State memory must be support by all automata or not at all.");
        }
      }

      // create EList of memory cell names (if memory cells are used)
      if (allMem) {
        EList<String> thisAutoMemCells = new BasicEList<String>();
        for (State s : automaton.getStates()) {
          try {
            tempL =
                StringListExtension.parse(s.findExtension(ClockUtils.STATE_MEMORY_ID).toString())
                    .getValues(); // memory cells of current state
            if (!tempL.isEmpty()) { // empty if state does not have memory cells
              thisAutoMemCells.addAll(tempL);
            }
          } catch (ParseException pe) {
            return new Status(
                IStatus.ERROR,
                PLUGIN_ID,
                "Unexpected error while trying to parse memory cells of " + s.getName());
          }
        }
        // remove duplicate cells from current automaton
        StringListExtension sle = new StringListExtension(thisAutoMemCells);
        sle.removeDuplicateEntries();
        // add names to global EList (checked later)
        tempL = sle.getValues();
        for (String s : tempL) {
          names.add(s);
        }
      }

      // add state names to global name set (checked later), and check for initial states
      tempB = false; // for initial states
      for (State s : automaton.getStates()) {
        if (s.getName() == null || s.getName().equals("")) {
          return new Status(IStatus.ERROR, PLUGIN_ID, "States must have a name.");
        }
        names.add(s.getName());
        if (CA.isStartState(s)) {
          if (tempB == true) {
            return new Status(
                IStatus.ERROR, PLUGIN_ID, "Each automaton must have exactly one initial location");
          }
          tempB = true;
        }
        if (!tempB) {
          return new Status(
              IStatus.ERROR, PLUGIN_ID, "Each automaton must have exactly one initial location");
        }
      }

      // untimed CA transitions must not have empty name set
      if (untimed) {
        for (Transition t : automaton.getTransitions()) {
          if (((StringListExtension) t.findExtension(ClockUtils.TRANSITION_PORT_NAMES_ID))
              .getValues()
              .isEmpty()) {
            return new Status(
                IStatus.ERROR, PLUGIN_ID, "Transitions of untimed CA must not have empty name set");
          }
        }
      }

      // TCA transitions with the empty name set must not have a data
      // constraint on ports (only memory cells)
      // actually this check should not be necessary, it is checked during editing (by the parser)
      // already
      if (timed) {
        for (Transition t : automaton.getTransitions()) {
          if (((StringListExtension) t.findExtension(ClockUtils.TRANSITION_PORT_NAMES_ID))
              .getValues()
              .isEmpty()) { // found transition with empty port set
            // get the constraint
            String cons = t.findExtension(ClockUtils.DATA_CONSTRAINT_ID).toString();
            TCADataParser parser = new TCADataParser(cons);
            try {
              tempL = parser.get_port_names();
              if (!tempL.isEmpty()) {
                return new Status(
                    IStatus.ERROR,
                    PLUGIN_ID,
                    "Invisible transitions (with empty port set) must not have a data constraint on ports.");
              }
            } catch (RecognitionException pe) {
              return new Status(
                  IStatus.ERROR, PLUGIN_ID, "Unexpected error while trying to parse " + cons);
            }
          }
        }
      }

      // add clock names to global name set (checked later)
      if (timed) {
        for (String c : ClockUtils.getClocks(automaton)) {
          names.add(c);
        }
      }

      // add port names to global port name set (checked later)
      for (String p :
          ((StringListExtension) automaton.findExtension(ClockUtils.AUTOMATON_PORT_NAMES_ID))
              .getValues()) {
        portNames.add(p);
      }
    }

    // check range of data values
    // either 'lowerBound..upperBound' or empty (=infinite)
    if (rng.trim().equals("")) {
      infinite = true;
    } else {
      infinite = false;
      if (!rng.contains("..")) {
        return new Status(
            IStatus.ERROR,
            PLUGIN_ID,
            "Finite range must be given in the form '0..upperBound'.\nupperBound must be an integer value strictly greater than 0.");
      }
      try {
        lowerBound = Integer.parseInt(rng.substring(0, rng.indexOf(".")));
        if (lowerBound != 0) {
          return new Status(
              IStatus.ERROR,
              PLUGIN_ID,
              "Finite range must be given in the form '0..upperBound'.\nLower bounds other than 0 are not (yet) supported.");
        }
      } catch (NumberFormatException nfe) {
        return new Status(
            IStatus.ERROR,
            PLUGIN_ID,
            "Finite range must be given in the form '0..upperBound'.\nLower bounds other than 0 are not (yet) supported.");
      }
      try {
        upperBound = Integer.parseInt(rng.substring(rng.lastIndexOf(".") + 1, rng.length()));
        if (upperBound < 1) {
          return new Status(
              IStatus.ERROR,
              PLUGIN_ID,
              "Finite range must be given in the form '0..upperBound'.\nupperBound must be an integer value strictly greater than 0.");
        }
      } catch (NumberFormatException nfe) {
        return new Status(
            IStatus.ERROR,
            PLUGIN_ID,
            "Finite range must be given in the form '0..upperBound'.\nupperBound must be an integer value strictly greater than 0.");
      }
    }

    // check that integer values used in data constraints are within the range (if finite)
    if (!infinite) {
      int min = -1;
      int max = 0;
      for (Automaton automaton : automata) {
        for (Transition t : automaton.getTransitions()) {
          String cons = t.findExtension(ClockUtils.DATA_CONSTRAINT_ID).toString();
          TCADataParser parser = new TCADataParser(cons);
          try {
            tempL = parser.get_minmax_int_values();
            min = Math.min(min, Integer.parseInt(tempL.get(0)));
            max = Math.max(max, Integer.parseInt(tempL.get(1)));
          } catch (RecognitionException re) {
            return new Status(
                IStatus.ERROR, PLUGIN_ID, "Unexpected error while trying to parse " + cons);
          }
        }
        if ((min < -1) || (max > this.upperBound)) {
          return new Status(
              IStatus.ERROR,
              PLUGIN_ID,
              "Integer values in data constraints are outside declared range.");
        }
      }
    }

    // check for spaces in all but port names
    for (String s : names) {
      if (s.contains(" ")) {
        return new Status(IStatus.ERROR, PLUGIN_ID, "Names must not contain spaces: '" + s + "'");
      }
    }

    // check for duplicate names in all but port names
    tempL = new StringListExtension(names).getDuplicateEntries();
    if (!tempL.isEmpty()) {
      return new Status(
          IStatus.ERROR,
          PLUGIN_ID,
          "Names (except for port names) must not be shared between automata: "
              + tempL.toString().replace("[", "").replace("]", ""));
    }

    // check for spaces in port names
    for (String p : portNames) {
      if (p.contains(" ")) {
        return new Status(IStatus.ERROR, PLUGIN_ID, "Names must not contain spaces: '" + p + "'");
      }
    }

    // compare port names with all other names, check for duplicates between the two sets
    StringListExtension sle = new StringListExtension(portNames);
    sle.removeDuplicateEntries();
    tempL = sle.getValues();
    tempL.addAll(names);
    tempL = new StringListExtension(tempL).getDuplicateEntries();
    if (!tempL.isEmpty()) {
      return new Status(
          IStatus.ERROR,
          PLUGIN_ID,
          "Names must be unique: " + tempL.toString().replace("[", "").replace("]", ""));
    }

    // check for reserved names CodegenUtils.NO_FLOW_NAME and CodegenUtils.GLOBAL_CLOCK
    if (names.contains(CodegenUtils.NO_FLOW_NAME)
        || portNames.contains(CodegenUtils.NO_FLOW_NAME)) {
      return new Status(
          IStatus.ERROR, PLUGIN_ID, "'" + CodegenUtils.NO_FLOW_NAME + "' is a reserved name. ");
    }
    if (names.contains(CodegenUtils.GLOBAL_CLOCK)
        || portNames.contains(CodegenUtils.GLOBAL_CLOCK)) {
      return new Status(
          IStatus.ERROR, PLUGIN_ID, "'" + CodegenUtils.GLOBAL_CLOCK + "' is a reserved name. ");
    }

    // check that the unfolding depth is at least 1
    try {
      unfoldingDepth = Integer.valueOf(genModel.getProperty(CodegenUtils.PROPERTY_DEPTH));
    } catch (NumberFormatException nfe) {
      return new Status(
          IStatus.ERROR, PLUGIN_ID, "Not a valid unfolding depth: chose an integer > 0");
    }
    if (!(unfoldingDepth > 0)) {
      return new Status(
          IStatus.ERROR, PLUGIN_ID, "Not a valid unfolding depth: chose an integer > 0");
    }

    // check that target language is one of the supported languages, hard code for the moment
    if (!targetlang.equalsIgnoreCase("mathsat") && !targetlang.equalsIgnoreCase("msat")) {
      return new Status(IStatus.ERROR, PLUGIN_ID, "Target language must be one of [mathsat|msat]");
    } else { // to standardise the property contents
      genModel.setProperty("targetlang", mathsat);
    }

    // check that output file name is not empty
    if (genModel.getProperty(CodegenUtils.PROPERTY_FILENAME).isEmpty()) {
      return new Status(IStatus.ERROR, PLUGIN_ID, "Output file name cannot be empty");
    }

    /* If none of the above occurs, the model is ok */
    return Status.OK_STATUS;
  }