/**
   * *************************************************************************** BEGIN: Methods to
   * handle specificActor communication
   *
   * <p>taken from original communication pattern from Alex
   * ***************************************************************************
   */
  public static void answerSpecificActor(BasicActor gridActor, ActorRef sender) {
    if (!gridActor.detectCircle()) {
      gridActor.reportToParentEnabled = false;
      sender.tell(
          new DirectAnswerMessage(gridActor.getCurrentTimeStep(), gridActor.upStreamTrace, 0.0),
          gridActor.getSelf());
    } else {
      gridActor.reportToParentEnabled = true;

      sender.tell(
          new DirectAnswerMessage(gridActor.getCurrentTimeStep(), gridActor.upStreamTrace, 0.0),
          gridActor.getSelf());

      BasicAnswer basicAnswer =
          new BasicAnswer(
              gridActor.getCurrentTimeStep(),
              gridActor.upStreamTrace,
              true,
              false,
              gridActor.getSelf().path().toString(),
              gridActor.answerContent);

      gridActor.getContext().parent().tell(basicAnswer, gridActor.getSelf());
    }
  }
  /**
   * *************************************************************************** Begin: Methods to
   * handle children communication
   * ***************************************************************************
   */
  public static void askChildren(BasicActor gridActor) throws Exception {

    // If there are children, then ...
    if (gridActor.getContext().getChildren().iterator().hasNext()) {
      BasicRequest request;
      RequestContent requestContent = gridActor.returnRequestContentToSend();

      // check if it is a MultiRequestContainer
      if (requestContent instanceof MultiRequestContainer) {
        MultiRequestContainer mrequest = (MultiRequestContainer) requestContent;

        // send the current message to the children
        if (mrequest.getCurrentRequestContent() instanceof SingleReceiverRequestContainer) {
          executeAskSpecificChildrenLogic(
              gridActor, (SingleReceiverRequestContainer) mrequest.getCurrentRequestContent());
        } else {
          request =
              new BasicRequest(
                  gridActor.getCurrentTimeStep(),
                  gridActor.downStreamTrace,
                  mrequest.getCurrentRequestContent());
          executeAskChildrenLogic(gridActor, request);
        }
      } else // normal execution without MultiRequestContainer
      {
        request =
            new BasicRequest(
                gridActor.getCurrentTimeStep(), gridActor.downStreamTrace, requestContent);
        executeAskChildrenLogic(gridActor, request);
      }
    }
    gridActor.upStreamTrace.add(gridActor.getSelf());
  }
  private static void executeAskSpecificChildrenLogic(
      BasicActor gridActor, SingleReceiverRequestContainer messages) throws Exception {
    // Patterns.ask() returns a Future<Object>
    List<Future<Object>> childrenResponseList = new ArrayList<Future<Object>>();

    for (SingleReceiverRequestContent secondMessage : messages.getRequests()) {
      for (ActorRef child : gridActor.getContext().getChildren()) {
        if (secondMessage.recieverPath.equals(child.path().toString())) {

          BasicRequest request =
              new BasicRequest(
                  gridActor.getCurrentTimeStep(), gridActor.downStreamTrace, secondMessage);
          ConstantLogger.logMessageSendCounter(request);
          // wait x ms for response
          childrenResponseList.add(
              Patterns.ask(child, request, GridArchitectConfiguration.childrenResponseTime));
        }
      }
    }

    Future<Iterable<Object>> childrenFuturesIterable =
        sequence(childrenResponseList, gridActor.getContext().system().dispatcher());
    Iterable<Object> childrenResponsesIterable =
        Await.result(
            childrenFuturesIterable,
            Duration.create(
                (GridArchitectConfiguration.childrenResponseTime),
                TimeUnit.MILLISECONDS)); // childrenResponsesIterableTimeOut

    gridActor.answerListReceived = new ArrayList<BasicAnswer>();
    for (Object receivedAnswer : childrenResponsesIterable) {

      gridActor.answerListReceived.add((BasicAnswer) receivedAnswer);

      ConstantLogger.logMessageSendCounter((BasicAnswer) receivedAnswer);

      if (((BasicAnswer) receivedAnswer).overrideReportToParent) {
        gridActor.reportToParentEnabled = true;
        gridActor.overrideReportToParent = true;
      }

      for (ActorRef actor : ((BasicAnswer) receivedAnswer).upstreamActorTrace) {
        gridActor.upStreamTrace.add(actor);
      }
    }
  }
  private static void executeAskChildrenLogic(BasicActor gridActor, BasicRequest request)
      throws Exception {

    // Patterns.ask() returns a Future<Object>
    List<Future<Object>> childrenResponseList = new ArrayList<Future<Object>>();

    Iterable<ActorRef> children = gridActor.getContext().getChildren();

    for (ActorRef child : children) {
      ConstantLogger.logMessageSendCounter(request);
      // wait x ms for response
      childrenResponseList.add(
          Patterns.ask(
              child,
              request,
              GridArchitectConfiguration.childrenResponseTime)); // childrenResponseListTimeOut
    }

    Future<Iterable<Object>> childrenFuturesIterable =
        sequence(childrenResponseList, gridActor.getContext().system().dispatcher());
    Iterable<Object> childrenResponsesIterable =
        Await.result(
            childrenFuturesIterable,
            Duration.create(
                (GridArchitectConfiguration.childrenResponseTime),
                TimeUnit.MILLISECONDS)); // childrenResponsesIterableTimeOut	

    gridActor.answerListReceived = new ArrayList<BasicAnswer>();
    for (Object iteratorResponses : childrenResponsesIterable) {
      BasicAnswer receivedAnswer = (BasicAnswer) iteratorResponses;

      gridActor.answerListReceived.add(receivedAnswer);
      ConstantLogger.logMessageSendCounter(receivedAnswer);
      if (receivedAnswer.overrideReportToParent) {
        gridActor.reportToParentEnabled = true;
        gridActor.overrideReportToParent = true;
      }

      // if multiple levels of hierarchie exist all actors will be included in the list
      for (ActorRef actor : receivedAnswer.upstreamActorTrace) {
        gridActor.upStreamTrace.add(actor);
      }
    }
  }
  public static void askSpecificActor(BasicActor gridActor) {
    if (!gridActor.actorOptions.directConnectionsPathList.isEmpty() && !gridActor.detectCircle()) {
      for (String actorPath : gridActor.actorOptions.directConnectionsPathList) {

        gridActor
            .getContext()
            .actorSelection("/user/ActorSupervisor/" + actorPath)
            .tell(
                new DirectMessage(gridActor.getCurrentTimeStep(), gridActor.downStreamTrace),
                gridActor.getSelf());
      }
      List<Future<Object>> actorResponseList = new ArrayList<Future<Object>>();
      for (String actorPath : gridActor.actorOptions.directConnectionsPathList)
        actorResponseList.add(
            ask(
                gridActor.getContext().actorSelection(actorPath),
                new DirectMessage(gridActor.getCurrentTimeStep(), gridActor.downStreamTrace),
                2000));
      Future<Iterable<Object>> actorFuturesIterable =
          sequence(actorResponseList, gridActor.getContext().system().dispatcher());
      try {
        Iterable<Object> actorResponsesIterable =
            Await.result(
                actorFuturesIterable,
                Duration.create(
                    (GridArchitectConfiguration.childrenResponseTime),
                    TimeUnit.MILLISECONDS)); // childrenResponsesIterableTimeOut
        for (Object response : actorResponsesIterable) {
          for (ActorRef actor : ((DirectAnswerMessage) response).actorTrace)
            gridActor.upStreamTrace.add(actor);
        }
        gridActor.upStreamTrace.add(gridActor.getSelf());
      } catch (Exception e) {
        System.out.println(e);
      }
    }
  }