/**
  * Gets all attackers that have been locked to a particular threat
  *
  * @return
  */
 private Set<Integer> getLockedAttackers() {
   Set<Integer> locked = new TreeSet<>();
   ArrayList<AttackerModel> attackersList = ReadAttackers.getAttackers();
   for (AttackerModel attacker : attackersList) {
     if (attacker.isAttackerLocked()) {
       locked.add(attacker.getIndex());
     }
   }
   return locked;
 }
 /** register information requirement for attackers, according to its target and location. */
 public void registerInfoRequirement() {
   ArrayList<AttackerModel> attackersList = ReadAttackers.getAttackers();
   for (AttackerModel attacker : attackersList) {
     if (attacker == null || !attacker.isOnline()) {
       continue;
     }
     Target target = attacker.getTarget_indicated_by_role();
     if (target != null) {
       float[] attacker_coord = attacker.getCenterCoordinates();
       this.msg_dispatcher.register(attacker.getIndex(), attacker_coord, target);
     }
   }
 }
  public void findAttackerAndUpdate(String addressOfNotification, boolean status) {
    /**
     * The address of the endpoint is extracted from the received notification. e.g.
     * http://127.0.0.1:8089/ms/clients/endpointIDhere/12207/0/14/observe The endpointID is the
     * extracted by splitting
     */
    String endpoint = addressOfNotification.split("/")[5];
    ArrayList<AttackerModel> attackers = ReadAttackers.getAttackers();

    for (AttackerModel attacker : attackers) {
      if (attacker.getClient().getEndpoint().contains(endpoint)) {
        attacker.setThreatDestroyed(status);
        break;
      }
    }
  }
  /**
   * assign role for uavs with subteam (size=this.subteam_size), considering the special case:
   * attacker i should be assigned with role i. Other role should be assigned to the nearest uav
   *
   * @param assigned_attacker_index
   * @param assigned_role_index
   */
  private void roleAssignForAttackerWithSubTeam(
      int assigned_attacker_index, int assigned_role_index) {
    TreeSet<Integer> assigned_attacker = new TreeSet<>();
    ArrayList<Threat> threats = kb.getThreats();
    ArrayList<AttackerModel> attackersList = ReadAttackers.getAttackers();
    int threat_num = threats.size();
    int attacker_num = HandleTree.attackersNode.getChildCount();

    for (int i = 0; i < threat_num; i++) {
      Threat threat = threats.get(i);
      if (!threat.isEnabled()) {
        continue;
      }
      Set<Integer> attackers_locked = getLockedAttackers();
      if (attackers_locked == null) {
        attackers_locked = new TreeSet<>();
      }
      assigned_attacker.addAll(attackers_locked);
      // manually assign
      if (threat.getIndex() == assigned_role_index) {
        for (AttackerModel attacker : attackersList) {
          synchronized (attacker) {
            if (attacker == null || !attacker.isOnline()) {
              continue;
            }
          }
          if (assigned_attacker_index == attacker.getIndex()) {
            if (attacker.getTarget_indicated_by_role() == null
                || attacker.getTarget_indicated_by_role().getIndex() != threat.getIndex()) {
              attacker.setFlightMode(CC_StaticInitConfig.FLYING_MODE);
            }
            attackerUtils.update.setTarget_indicated_by_role(threat, attacker);
            attackerUtils.update.setSpeed(CC_StaticInitConfig.SPEED_OF_ATTACKER_ON_TASK, attacker);
            attackerUtils.update.setReplan(true, attacker);
            assigned_attacker.add(assigned_attacker_index);
            break;
          }
        }
        continue;
      }

      int remained_team_size = this.sub_team_size - attackers_locked.size();
      ArrayList<AttackerModel> attacker_arr_to_assign = new ArrayList<>();
      ArrayList<Float> attacker_dist_to_assign = new ArrayList<>();
      for (AttackerModel current_attacker : attackersList) {

        if (!current_attacker.isEnduranceCapReachable(threat)) {
          continue;
        }
        if (assigned_attacker_index == current_attacker.getIndex()) {
          continue;
        }
        if (!current_attacker.isOnline()) {
          continue;
        }
        if (attackers_locked.contains(current_attacker.getIndex())) {
          continue;
        }
        if (assigned_attacker.contains(current_attacker.getIndex())) {
          continue;
        }

        float dist_between_uav_and_role =
            DistanceUtil.distanceBetween(
                current_attacker.getCenterCoordinates(), threat.getCoordinates());
        int index_to_insert = 0;
        boolean attacker_added = false;
        for (float attacker_dist : attacker_dist_to_assign) {
          if (dist_between_uav_and_role < attacker_dist) {
            attacker_added = true;
            break;
          }
          index_to_insert++;
        }
        if (attacker_added) {
          attacker_dist_to_assign.add(index_to_insert, dist_between_uav_and_role);
          attacker_arr_to_assign.add(index_to_insert, current_attacker);

          if (attacker_dist_to_assign.size() > remained_team_size) {
            attacker_dist_to_assign.remove(remained_team_size);
            attacker_arr_to_assign.remove(remained_team_size);
          }
        } else if (attacker_dist_to_assign.size() < remained_team_size) {
          attacker_dist_to_assign.add(dist_between_uav_and_role);
          attacker_arr_to_assign.add(current_attacker);
        }
      }

      if (attacker_arr_to_assign.size() >= remained_team_size) {
        for (AttackerModel attacker : attacker_arr_to_assign) {
          if (attacker.getFlightMode() == CC_StaticInitConfig.TARGET_LOCKED_MODE) {
            continue;
          }
          assigned_attacker.add(attacker.getIndex());
          attackerUtils.update.setTarget_indicated_by_role(threat, attacker);
          attackerUtils.update.setSpeed(CC_StaticInitConfig.SPEED_OF_ATTACKER_ON_TASK, attacker);
          attackerUtils.update.setReplan(true, attacker);
          attackerUtils.update.setFlightMode(CC_StaticInitConfig.FLYING_MODE, attacker);
        }
      }
    }

    for (AttackerModel current_attacker : attackersList) {
      if (current_attacker.getFlightMode() == CC_StaticInitConfig.TARGET_LOCKED_MODE) {
        continue;
      }
      if (!assigned_attacker.contains(current_attacker.getIndex())
          && current_attacker.getTarget_indicated_by_role() != null
          && current_attacker.getTarget_indicated_by_role().getIndex() != -1) {
        float[] dummy_threat_coord = current_attacker.getUavPositionInBaseStation();
        Threat dummy_threat = new Threat(-1, dummy_threat_coord, 0, ThreatType.DUMMY);
        attackerUtils.update.setTarget_indicated_by_role(dummy_threat, current_attacker);
        attackerUtils.update.setReplan(true, current_attacker);
        attackerUtils.update.setSpeed(CC_StaticInitConfig.SPEED_OF_ATTACKER_IDLE, current_attacker);
        attackerUtils.update.setFlightMode(CC_StaticInitConfig.FLYING_MODE, current_attacker);
      }
    }
    need_to_assign_role = false;
  }