/**
   * Create and link a user action into a sequence.
   *
   * @param repositoryModelProvider repository model provider
   * @param correspondenceModel correspondence model
   * @param index correspondent index for string arrays
   * @param lastAction previous action
   * @param correspondent correspondent element
   * @param scenarioBehaviour behavior
   * @return return this call
   */
  private static AbstractUserAction createEntryLevelSystemCall(
      final RepositoryModelProvider repositoryModelProvider,
      final ICorrespondence correspondenceModel,
      final int index,
      final AbstractUserAction lastAction,
      final ScenarioBehaviour scenarioBehaviour) {
    final Optional<Correspondent> correspondent =
        correspondenceModel.getCorrespondent(
            ReferenceUsageModelBuilder.CLASS_SIGNATURE[index],
            ReferenceUsageModelBuilder.OPERATION_SIGNATURE[index]);
    if (correspondent.isPresent()) {
      final EntryLevelSystemCall entryLevelSystemCall =
          UsageModelBuilder.createEntryLevelSystemCall(
              repositoryModelProvider, correspondent.get());
      UsageModelBuilder.addUserAction(scenarioBehaviour, entryLevelSystemCall);
      UsageModelBuilder.connect(lastAction, entryLevelSystemCall);

      return entryLevelSystemCall;
    } else {
      return lastAction;
    }
  }
  /**
   * Creates a reference model that contains a loop element. The user sessions contain iterated call
   * sequences that share overlapping calls. Thereby, one iterated sequence consists of more calls
   * than the other. Thus, it can be checked whether the approach transforms the iterated call
   * sequence that consists of more calls to a loop (RQ-1.4)
   *
   * @param referenceUsageModelFileName file name of the reference model to store its result
   * @param repositoryModelProvider repository model provider
   * @param correspondenceModel correspondence model
   * @return a reference usage model and corresponding user sessions
   * @throws IOException on error
   */
  public static ReferenceElements getModel(
      final String referenceUsageModelFileName,
      final RepositoryModelProvider repositoryModelProvider,
      final ICorrespondence correspondenceModel)
      throws IOException {

    // Creates a random number of user sessions and random model element parameters. The user
    // sessions' behavior will be created according to the reference usage model and
    // subsequently the user sessions are used to create a usage model. The created usage model
    // is matched against the reference usage model.
    final int numberOfConcurrentUsers = TestHelper.getRandomInteger(200, 1);
    // One of the iterated sequences is iterated twice and one is iterated three times. The
    // number of iterations is set randomly.
    final int loopCount1 = TestHelper.getRandomInteger(3, 2);
    final int lengthOfSequence1 = 2 * loopCount1;
    final int loopCount2;
    if (loopCount1 == 3) {
      loopCount2 = 2;
    } else {
      loopCount2 = 3;
    }
    final int lengthOfSequence2 = 2 * loopCount2;

    final ReferenceElements referenceElements = new ReferenceElements();
    final EntryCallSequenceModel entryCallSequenceModel =
        new EntryCallSequenceModel(TestHelper.getUserSessions(numberOfConcurrentUsers));

    // In the following the reference usage model is created
    AbstractUserAction lastAction;
    final UsageModel usageModel = UsageModelBuilder.createUsageModel();
    final UsageScenario usageScenario = UsageModelBuilder.createUsageScenario("", usageModel);
    final ScenarioBehaviour scenarioBehaviour = usageScenario.getScenarioBehaviour_UsageScenario();
    final Start start = UsageModelBuilder.createStart("");
    UsageModelBuilder.addUserAction(scenarioBehaviour, start);
    final Stop stop = UsageModelBuilder.createStop("");
    UsageModelBuilder.addUserAction(scenarioBehaviour, stop);
    // According to the randomly set number of iterations the sequence that is iterated three
    // times is represented by a loop element. The other sequence is represented by a sequence
    final Loop loop = UsageModelBuilder.createLoop("", scenarioBehaviour);

    if (lengthOfSequence1 >= lengthOfSequence2) {
      UsageModelBuilder.connect(start, loop);
      final PCMRandomVariable pcmLoopIteration = CoreFactory.eINSTANCE.createPCMRandomVariable();
      pcmLoopIteration.setSpecification(String.valueOf(loopCount1));
      loop.setLoopIteration_Loop(pcmLoopIteration); // Set number of loops
      final Start loopStart = UsageModelBuilder.createStart("");
      UsageModelBuilder.addUserAction(loop.getBodyBehaviour_Loop(), loopStart);
      final Stop loopStop = UsageModelBuilder.createStop("");
      UsageModelBuilder.addUserAction(loop.getBodyBehaviour_Loop(), loopStop);

      lastAction =
          OverlappingIterationReference.createEntryLevelSystemCall(
              repositoryModelProvider, correspondenceModel, 0, loopStart, scenarioBehaviour);
      lastAction =
          OverlappingIterationReference.createEntryLevelSystemCall(
              repositoryModelProvider, correspondenceModel, 2, lastAction, scenarioBehaviour);

      UsageModelBuilder.connect(lastAction, loopStop);

      lastAction =
          OverlappingIterationReference.createEntryLevelSystemCall(
              repositoryModelProvider, correspondenceModel, 3, loop, scenarioBehaviour);
      lastAction =
          OverlappingIterationReference.createEntryLevelSystemCall(
              repositoryModelProvider, correspondenceModel, 2, lastAction, scenarioBehaviour);
      lastAction =
          OverlappingIterationReference.createEntryLevelSystemCall(
              repositoryModelProvider, correspondenceModel, 3, lastAction, scenarioBehaviour);

      UsageModelBuilder.connect(lastAction, stop);
    } else {
      lastAction =
          OverlappingIterationReference.createEntryLevelSystemCall(
              repositoryModelProvider, correspondenceModel, 0, start, scenarioBehaviour);
      lastAction =
          OverlappingIterationReference.createEntryLevelSystemCall(
              repositoryModelProvider, correspondenceModel, 2, lastAction, scenarioBehaviour);
      lastAction =
          OverlappingIterationReference.createEntryLevelSystemCall(
              repositoryModelProvider, correspondenceModel, 0, lastAction, scenarioBehaviour);

      UsageModelBuilder.connect(lastAction, loop);
      final PCMRandomVariable pcmLoopIteration = CoreFactory.eINSTANCE.createPCMRandomVariable();
      pcmLoopIteration.setSpecification(String.valueOf(loopCount2));
      loop.setLoopIteration_Loop(pcmLoopIteration); // Set number of loops
      final Start loopStart = UsageModelBuilder.createStart("");
      UsageModelBuilder.addUserAction(loop.getBodyBehaviour_Loop(), loopStart);
      final Stop loopStop = UsageModelBuilder.createStop("");
      UsageModelBuilder.addUserAction(loop.getBodyBehaviour_Loop(), loopStop);
      lastAction = loopStart;

      lastAction =
          OverlappingIterationReference.createEntryLevelSystemCall(
              repositoryModelProvider, correspondenceModel, 2, lastAction, scenarioBehaviour);
      lastAction =
          OverlappingIterationReference.createEntryLevelSystemCall(
              repositoryModelProvider, correspondenceModel, 3, lastAction, scenarioBehaviour);

      UsageModelBuilder.connect(lastAction, loopStop);
      UsageModelBuilder.connect(loop, stop);
    }

    OverlappingIterationReference.createMatchingEntryAndExitEvents(
        entryCallSequenceModel, lengthOfSequence1, lengthOfSequence2, loopCount1, loopCount2);

    // Saves the reference usage model and sets the usage model and the EntryCallSequenceModel
    // as the reference elements. Our approach is now executed with the EntryCallSequenceModel
    // and the resulting usage model can be matched against the reference usage model
    TestHelper.saveModel(usageModel, referenceUsageModelFileName);
    referenceElements.setEntryCallSequenceModel(entryCallSequenceModel);
    referenceElements.setUsageModel(usageModel);

    return referenceElements;
  }