/**
   * Détermine si une position est ou non disponible en fonction d'un blason donné
   *
   * @param blason le blason que l'on cherche à placer sur la position
   * @param position la position ou placer ce blason
   * @return <i>true</i> si l'on arrive à placer le blason sur la position, <i>false</i> sinon
   */
  public boolean isSlotAvailable(Blason blason, int position) {
    boolean placable = true;

    if (concurrents[position] != null) return false;

    // Ancrage ancrage = blason.getAncrage(position);
    for (int i = 0; i < concurrents.length; i++) {
      if (concurrents[i] != null) {
        // int otherPosition = concurrent.getPosition();
        DistancesEtBlason db =
            DistancesEtBlason.getDistancesEtBlasonForConcurrent(reglement, concurrents[i]);
        Blason otherBlason = db.getTargetFace();

        if (blason.getNbArcher() > 2 || otherBlason.getNbArcher() > 2) {
          if (!otherBlason.equals(blason)) {
            placable = false;
            break;
          }
        } else if (blason.getNbArcher() > 1) {
          if (!((!otherBlason.equals(blason) && !blason.isOver(position, otherBlason, i))
              || otherBlason.equals(blason))) {
            placable = false;
            break;
          }
        } else {
          if (blason.isOver(position, otherBlason, i)) {
            placable = false;
            break;
          }
        }
      }
    }

    return placable;
  }
 /**
  * Retourne l'objet DistancesEtBlason associé à un concurrent pour un règlement donné.
  *
  * @param reglement le règlement déterminant le DistancesEtBlason du concurrent
  * @param concurrent le concurrent pour lequel retourné l'objet
  * @return l'objet DistancesEtBlason correspondant au concurrent
  */
 public static DistancesEtBlason getDistancesEtBlasonForConcurrent(
     Reglement reglement, Concurrent concurrent) {
   List<DistancesEtBlason> ldb =
       reglement.getDistancesEtBlasonFor(
           concurrent.getCriteriaSet().getFilteredCriteriaSet(reglement.getPlacementFilter()));
   if (concurrent.isUseAlternativeTargetFace()) {
     for (DistancesEtBlason db : ldb) {
       if (concurrent.getAlternativeTargetFace().equals(db.getTargetFace())) return db;
     }
   }
   if (ldb.size() > 0) return ldb.get(0);
   return null;
 }
 /**
  * Retourne le nombre d'archer present sur la cible pour un DistancesEtBlason donnée
  *
  * @param db le DistancesEtBlason pour lequelle retourner le nombre d'archers
  * @return le nombre d'archer sur le DistancesEtBlason présent sur la cible
  */
 public int getNbArcherFor(DistancesEtBlason db) {
   int nbArcher = 0;
   for (Concurrent concurrent : concurrents) {
     if (concurrent != null) {
       DistancesEtBlason concDb =
           DistancesEtBlason.getDistancesEtBlasonForConcurrent(reglement, concurrent);
       if (db.equals(concDb)) {
         nbArcher++;
       }
     }
   }
   return nbArcher;
 }
  /**
   * Place un concurrent sur la cible à la position donné
   *
   * @param concurrent le concurrent à placer
   * @param position la positionn de ce concurrent
   * @throws PlacementException invoqué en cas d'echec d'insertion du concurrent
   */
  protected void setConcurrentAt(Concurrent concurrent, int position) throws PlacementException {
    if (concurrent != null) {
      if (position == -1) {
        Repartition repartition = Repartition.ACBD;
        if (concurrents.length == 2) repartition = Repartition.ABCD;
        insertConcurrent(concurrent, repartition);

        return;
      } else if (position >= 0 && position < concurrents.length && !isReservedPosition(position)) {
        DistancesEtBlason dbConcurrent =
            DistancesEtBlason.getDistancesEtBlasonForConcurrent(reglement, concurrent);

        assert dbConcurrent != null
            : "un concururrent doit toujours avoir un DistancesEtBlason associé"; //$NON-NLS-1$

        if (getDistancesEtBlason().size() > 0
            && !Arrays.equals(
                dbConcurrent.getDistance(), getDistancesEtBlason().get(0).getDistance()))
          throw new PlacementException(PlacementException.Nature.BAD_DISTANCES);

        if (isSlotAvailable(dbConcurrent.getTargetFace(), position)) {
          if (concurrent.isHandicape() && position % 2 != 0)
            throw new PlacementException(
                PlacementException.Nature.POSITION_AVAILABLE_FOR_VALID_CONCURRENT);
          concurrent.setCible(numCible);
          concurrent.setPosition(position);
          if (concurrents[position] != null) removeConcurrentAt(position);

          if (concurrent.isHandicape()) removeConcurrentAt(position + 1);

          nbArcher++;
          if (concurrent.isHandicape()) nbHandicap++;

          concurrent.addPropertyChangeListener(this);

          concurrents[position] = concurrent;
          fireConcurrentJoined(concurrent);

          return;
        }
        throw new PlacementException(PlacementException.Nature.BAD_TARGETFACE);
      } else {
        if (isReservedPosition(position))
          throw new PlacementException(PlacementException.Nature.POSITION_RESERVED_FOR_HANDICAP);
        throw new PlacementException(PlacementException.Nature.ANY_AVAILABLE_POSITION);
      }
    }

    throw new PlacementException(PlacementException.Nature.NULL_CONCURRENT);
  }
 /**
  * Retourne le nombre de position disponible pour l'objet DistancesEtBlason fournit en parametre
  *
  * @param distancesEtBlason l'objet DistancesEtBlason permettant de déterminer le nombre de
  *     position disponible
  * @return le nombre de slot disponible pour le DistancesEtBlason
  */
 public int getNbAvailableSlotsFor(DistancesEtBlason distancesEtBlason) {
   if (nbArcher > 0) {
     if (Arrays.equals(
         getDistancesEtBlason().get(0).getDistance(), distancesEtBlason.getDistance())) {
       int availableSlots = 0;
       for (int i = 0; i < concurrents.length; i++) {
         if (concurrents[i] == null
             && !isReservedPosition(i)
             && isSlotAvailable(distancesEtBlason.getTargetFace(), i)) {
           availableSlots++;
         }
       }
       return availableSlots;
     }
     return 0;
   }
   return concurrents.length;
 }
  /**
   * Donne la disposition de la cible
   *
   * @return DistancesEtBlason - la disposition de la cible
   */
  public List<DistancesEtBlason> getDistancesEtBlason() {
    // DistancesEtBlason db = null;
    List<DistancesEtBlason> dbs = new ArrayList<DistancesEtBlason>();

    if (nbArcher > 0) {
      // Concurrent firstConcurrent = null;
      for (Concurrent concurrent : concurrents) {
        if (concurrent != null) {
          DistancesEtBlason db =
              DistancesEtBlason.getDistancesEtBlasonForConcurrent(reglement, concurrent);
          if (!dbs.contains(db)) dbs.add(db);
          // firstConcurrent = concurrent;
          // break;
        }
      }

      // db = DistancesEtBlason.getDistancesEtBlasonForConcurrent(reglement, firstConcurrent);
    }

    return dbs;
  }
  /**
   * insere un concurrent à la premiere position libre et retourne cette position ou produit une
   * exception PlacementException en cas d'echec
   *
   * <p>L'insertion peut se dérouler en mode <i>réel</i> auquel cas, les informations de placement
   * seront ajouté au concurrent ou en mode </i>simulation</i> si l'on souhaite simplement voir si
   * l'on peut placer les archers sur la cible sans affecter les valeurs de placement au concurrent.
   *
   * @param concurrent le concurrent à inserer
   * @param repartition le mode de distribution des archers sur la cible
   * @param simulationMode <i>true</i> si l'on souhaite une insertion en mode simulation,
   *     <i>false</i> sinon
   * @return la position de concurrent
   * @throws PlacementException si l'insertion du concurrent est impossible
   */
  protected int insertConcurrent(
      Concurrent concurrent, Repartition repartition, boolean simulationMode)
      throws PlacementException {
    int position = -1;

    if (concurrent != null) {
      // verifie qu'il reste des places disponible
      if (nbArcher < concurrents.length - nbHandicap) {
        DistancesEtBlason concurrentDb =
            DistancesEtBlason.getDistancesEtBlasonForConcurrent(reglement, concurrent);
        List<DistancesEtBlason> targetDbs = getDistancesEtBlason();

        // verifie que la distance est bonne
        if (targetDbs.size() > 0
            && !Arrays.equals(concurrentDb.getDistance(), targetDbs.get(0).getDistance()))
          throw new PlacementException(PlacementException.Nature.BAD_DISTANCES);

        // si l'archer est handicapé, vérifié que les condition spécifique sont remplis
        if (concurrent.isHandicape()) {
          if (nbArcher < concurrents.length - (nbHandicap + 1)) {
            // dans le cas d'un archer handicapé, on boucle sur les emplacements 2 à 2
            for (int i = 0; i < concurrents.length; i += 2) {
              if (isSlotAvailable(concurrentDb.getTargetFace(), i) && concurrents[i + 1] == null) {
                placeConcurrent(concurrent, i, simulationMode);
                nbHandicap++;

                position = i;

                break;
              }
            }
            if (position == -1)
              throw new PlacementException(PlacementException.Nature.BAD_TARGETFACE);
          } else {
            throw new PlacementException(
                PlacementException.Nature.POSITION_AVAILABLE_FOR_VALID_CONCURRENT);
          }
        } else {
          if (repartition == Repartition.ABCD) {
            // on boucle sur les emplacements et on remplit le premier qui est libre
            for (int i = 0; i < concurrents.length; i++) {
              if (isSlotAvailable(concurrentDb.getTargetFace(), i)) {
                if (isReservedPosition(
                    i)) // la position est libre mais reservé pour un archer handicapé
                continue;
                placeConcurrent(concurrent, i, simulationMode);

                position = i;

                break;
              }
            }
          } else {
            // tente le placement en AC
            for (int i = 0; i < concurrents.length; i += 2) {
              if (isSlotAvailable(concurrentDb.getTargetFace(), i)) {
                placeConcurrent(concurrent, i, simulationMode);

                position = i;

                break;
              }
            }
            // si AC en echec tente en BD
            if (position == -1) {
              for (int i = 1; i < concurrents.length; i += 2) {
                if (isSlotAvailable(concurrentDb.getTargetFace(), i)) {
                  if (isReservedPosition(
                      i)) // la position est libre mais reservé pour un archer handicapé
                  continue;
                  placeConcurrent(concurrent, i, simulationMode);

                  position = i;

                  break;
                }
              }
            }
          }
          if (position == -1)
            throw new PlacementException(PlacementException.Nature.BAD_TARGETFACE);
        }
      } else {
        if (nbHandicap > 0)
          throw new PlacementException(PlacementException.Nature.POSITION_RESERVED_FOR_HANDICAP);
        throw new PlacementException(PlacementException.Nature.ANY_AVAILABLE_POSITION);
      }
    } else {
      throw new PlacementException(PlacementException.Nature.NULL_CONCURRENT);
    }

    return position;
  }