class CarteTopo extends DataSet {

  @SuppressWarnings("unchecked")
  public void creeTopologieFaces() {
    List<Arc> arcsDejaTraitesADroite = new ArrayList<Arc>();
    List<Arc> arcsDejaTraitesAGauche = new ArrayList<Arc>();
    List<Arc> arcsDuCycle;
    List<Boolean> orientationsArcsDuCycle;
    ILineString geometrieDuCycle;
    IPopulation<Face> popFaces = this.getPopFaces();
    List<Cycle> cycles = new ArrayList<Cycle>();
    this.fireActionPerformed(
        new ActionEvent(this, 0, I18N.getString("CarteTopo.FaceTopologyEdges"), //$NON-NLS-1$
            this.getPopArcs().size()));
    int iteration = 0;
    /*
     * Parcours de tous les arcs du graphe. Puis, pour chaque arc: - recherche
     * du cycle à droite et du cycle à gauche - creation des faces
     * correspondantes - on note les arcs par lesquels on est déjà passé pour ne
     * pas refaire le travail TODO Regrouper le traitement à droite et à gauche
     * des arcs.
     */
    for (Arc arc : this.getPopArcs()) {
      Face face = null;
      // a droite
      if (!arcsDejaTraitesADroite.contains(arc)) {
        Cycle cycle = arc.cycle(false);
        // face = null;
        if (cycle == null) {
          CarteTopo.logger
              .error(I18N.getString("CarteTopo.RightNullCycle") + arc.getId()); //$NON-NLS-1$
          continue;
        }
        arcsDuCycle = cycle.getArcs();
        orientationsArcsDuCycle = cycle.getOrientationsArcs();
        geometrieDuCycle = cycle.getGeometrie();
        boolean multiGeometrie = false;
        boolean simple = geometrieDuCycle.isSimple();
        if (!simple) {
          List<Cycle> cyclesGeometrieNonSimple = CarteTopo
              .construireGeometrieCycleExterieur(arc, false);
          // la géométrie est corrigée
          if (cyclesGeometrieNonSimple.size() == 1) {
            geometrieDuCycle = cyclesGeometrieNonSimple.get(0).getGeometrie();
          } else {
            for (Cycle cycleCourant : cyclesGeometrieNonSimple) {
              boolean ccw = JtsAlgorithms.isCCW(cycleCourant.getGeometrie());
              if (ccw) {
                cycles.add(cycleCourant);
              } else {
                face = popFaces
                    .nouvelElement(new GM_Polygon(cycleCourant.getGeometrie()));
                CarteTopo.logger.debug("NEW FACE = " + face.getGeometrie());
              }
            }
            multiGeometrie = true;
          }
        }
        if (!multiGeometrie) {
          if (geometrieDuCycle.sizeControlPoint() > 3) {
            boolean ccw = JtsAlgorithms.isCCW(geometrieDuCycle);
            if (ccw) {
              cycles.add(new Cycle(arcsDuCycle, orientationsArcsDuCycle,
                  geometrieDuCycle, false));
            } else {
              face = popFaces.nouvelElement(new GM_Polygon(geometrieDuCycle));
              CarteTopo.logger.debug("NEW FACE = " + face.getGeometrie());
            }
          }
        }
        // if ( persistant ) JeuDeDonnees.db.makePersistent(face);
        this.marquerCycle(arcsDuCycle, orientationsArcsDuCycle, false, face,
            arcsDejaTraitesAGauche, arcsDejaTraitesADroite);
      }
      // a gauche
      if (!arcsDejaTraitesAGauche.contains(arc)) {
        Cycle cycle = arc.cycle(true);
        face = null;
        if (cycle == null) {
          if (CarteTopo.logger.isDebugEnabled()) {
            CarteTopo.logger
                .debug(I18N.getString("CarteTopo.LeftNullCycle") + arc.getId());
          }
          continue;
        }
        arcsDuCycle = cycle.getArcs();
        orientationsArcsDuCycle = cycle.getOrientationsArcs();
        geometrieDuCycle = cycle.getGeometrie();
        boolean multiGeometrie = false;
        boolean simple = geometrieDuCycle.isSimple();
        if (!simple) {
          List<Cycle> cyclesGeometrieNonSimple = CarteTopo
              .construireGeometrieCycleExterieur(arc, true);
          // la géométrie est corrigée
          if (cyclesGeometrieNonSimple.size() == 1) {
            geometrieDuCycle = cyclesGeometrieNonSimple.get(0).getGeometrie();
          } else {
            for (Cycle cycleCourant : cyclesGeometrieNonSimple) {
              boolean ccw = JtsAlgorithms.isCCW(cycleCourant.getGeometrie());
              if (!ccw) {
                cycles.add(cycleCourant);
              } else {
                face = popFaces
                    .nouvelElement(new GM_Polygon(cycleCourant.getGeometrie()));
                CarteTopo.logger.debug("NEW FACE = " + face.getGeometrie());
              }
            }
            multiGeometrie = true;
          }
        }
        if (!multiGeometrie && geometrieDuCycle.sizeControlPoint() > 3) {
          boolean ccw = JtsAlgorithms.isCCW(geometrieDuCycle);
          if (!ccw) {
            cycles.add(new Cycle(arcsDuCycle, orientationsArcsDuCycle,
                geometrieDuCycle, true));
          } else {
            face = popFaces.nouvelElement(new GM_Polygon(geometrieDuCycle));
            if (CarteTopo.logger.isDebugEnabled()) {
              CarteTopo.logger.debug("NEW FACE = " + face.getGeometrie());
            }
          }
        }
        // if ( persistant ) JeuDeDonnees.db.makePersistent(face);
        this.marquerCycle(arcsDuCycle, orientationsArcsDuCycle, true, face,
            arcsDejaTraitesAGauche, arcsDejaTraitesADroite);
      }
      this.fireActionPerformed(new ActionEvent(this, 1,
          I18N.getString("CarteTopo.FaceTopologyEdge"), iteration++)); //$NON-NLS-1$
    }
    // détection des arcs pendants ie des culs-de-sac
    for (Arc arcCourant : this.getPopArcs()) {
      if ((arcCourant.getFaceDroite() == null)
          || (arcCourant.getFaceGauche() == null)) {
        continue;
      }
      if (arcCourant.getFaceDroite() == arcCourant.getFaceGauche()) {
        arcCourant.setPendant(true);
      }
    }
    /*
     * création de l'index spatial. On n'active pas la mise à jour automatique
     * afin que, lorsque l'on ajoute la face infinie, elle n'apparaisse pas dans
     * chaque requête select.
     */
    this.getPopFaces().initSpatialIndex(Tiling.class, false);
    Face faceInfinie = null;
    if (this.buildInfiniteFace) {
      IEnvelope envelope = this.getPopArcs().envelope();
      if (envelope != null) {
        faceInfinie = this.getPopFaces()
            .nouvelElement(new GM_Polygon(
                new GM_Envelope(envelope.minX() - 1, envelope.maxX() + 1,
                    envelope.minY() - 1, envelope.maxY() + 1)));
        faceInfinie.setInfinite(true);
      }
      // CarteTopo.logger.debug("INFINITE FACE = " +
      // faceInfinie.getGeometrie());
    }
    this.fireActionPerformed(new ActionEvent(this, 2,
        I18N.getString("CarteTopo.FaceTopologyCycles"), cycles.size())); //$NON-NLS-1$
    iteration = 0;
    for (Cycle cycle : cycles) {
      Face face = ((cycle.isAGauche()
          && cycle.getOrientationsArcs().get(0).booleanValue())
          || (!cycle.isAGauche()
              && !cycle.getOrientationsArcs().get(0).booleanValue()))
                  ? cycle.getArcs().get(0).getFaceGauche()
                  : cycle.getArcs().get(0).getFaceDroite();
      if (face == null) {
        ILineString geom = cycle.getGeometrie();
        // logger.debug("NULL Face for " + cycle);
        if (geom == null || geom.numPoints() < 2 || !geom.isValid()) {
          logger.error("PB WITH " + cycle);
          face = faceInfinie;
        } else {
          Collection<Face> selection = this.getPopFaces().select(geom);
          if (selection.isEmpty()) {
            face = faceInfinie;
          } else {
            selection.removeAll(cycle.getListeFacesInterieuresDuCycle());
            Iterator<Face> it = selection.iterator();
            while (it.hasNext()) {
              if (!it.next().getGeometrie().contains(geom)) {
                it.remove();
              }
            }
            if (selection.isEmpty()) {
              face = faceInfinie;
            } else {
              it = selection.iterator();
              face = it.next();
              // s'il y a plus d'une face qui contient celle-ci
              while (it.hasNext()) {
                Face f = it.next();
                // on sélectionne la plus petite
                if (f.getGeometrie().area() < face.getGeometrie().area()) {
                  face = f;
                }
              }
            }
          }
        }
      }
      this.marquerCycle(cycle, face);
      // on ajoute un trou à la géométrie de la face infinie
      if (this.buildInfiniteFace && faceInfinie != null) {
        // CarteTopo.logger.debug("faceInfinie " + face.getGeometrie());
        if (cycle.getGeometrie().sizeControlPoint() > 3) {
          // FIXME That does not look very efficient...
          IPolygon holePolygon = (IPolygon) new GM_Polygon(cycle.getGeometrie())
              .buffer(0);
          // CarteTopo.logger.debug("hole " + holePolygon);
          if (!holePolygon.coord().isEmpty() && holePolygon.coord().size() > 3
              && holePolygon.isValid()) {
            // if (face.getGeometrie().getInterior().isEmpty()) {
            // face.getGeometrie().addInterior(new
            // GM_Ring(cycle.getGeometrie()));
            // CarteTopo.logger.debug("Added as interior");
            // } else {

            // Correction Mickael
            IGeometry geom = faceInfinie.getGeometrie().difference(holePolygon);
            faceInfinie.setGeometrie((IPolygon) geom);

            // Former code :
            // IGeometry geom = face.getGeometrie().difference(holePolygon);
            // faceInfinie.setGeometrie((IPolygon) geom);

            // CarteTopo.logger.debug("Removed from infinite face "
            // + newPolygon);
            // }
          }
          /*
           * IRing trou = new GM_Ring(cycle.getGeometrie()); if
           * (!trou.coord().isEmpty() && trou.coord().size() > 3 &&
           * trou.isValid()) { if (face.getGeometrie().getInterior().isEmpty())
           * { face.getGeometrie().addInterior(trou); } else { // union des
           * trous IPolygon polygonHole = new GM_Polygon(trou); List<IPolygon>
           * trous = new ArrayList<IPolygon>(); for (IRing ring :
           * face.getGeometrie().getInterior()) { trous.add(new
           * GM_Polygon(ring)); } // ajout du nouveau trou à la liste
           * trous.add(polygonHole); if (CarteTopo.logger.isDebugEnabled()) {
           * CarteTopo.logger.debug("Union de " + trous.size() + " trous"); if
           * (CarteTopo.logger.isDebugEnabled()) { for (IPolygon t : trous) {
           * CarteTopo.logger.debug("trou " + t); } } } try { IGeometry union =
           * JtsAlgorithms.union(trous); if (union.isPolygon()) { GM_Polygon
           * polygon = (GM_Polygon) union; // suppression des trous existants
           * face.getGeometrie().getInterior().clear();
           * face.getGeometrie().addInterior(polygon.getExterior()); } else { if
           * (union.isMultiSurface()) { IMultiSurface<IPolygon> multipolygon =
           * (IMultiSurface<IPolygon>) union; // suppression des trous existants
           * face.getGeometrie().getInterior().clear(); for (IPolygon polygon :
           * multipolygon) {
           * face.getGeometrie().addInterior(polygon.getExterior()); } } else {
           * CarteTopo.logger.error(union); } } } catch (Exception e) {
           * CarteTopo.logger.debug("Cycle " + cycle.getGeometrie());
           * CarteTopo.logger.debug(cycle.getArcs().size() + " arcs"); for (Arc
           * arc : cycle.getArcs()) { CarteTopo.logger.debug("arc " + arc); }
           * CarteTopo.logger.debug("face " + face); } } }
           */
        }
      }
      this.fireActionPerformed(new ActionEvent(this, 3,
          I18N.getString("CarteTopo.FaceTopologyCycle"), iteration++)); //$NON-NLS-1$
    }
    // détection des arcs pendants ie des culs-de-sac de la face Infinie
    if (this.buildInfiniteFace && faceInfinie != null) {
      for (Arc arcCourant : faceInfinie.arcs()) {
        if ((arcCourant.getFaceDroite() == null)
            || (arcCourant.getFaceGauche() == null)) {
          continue;
        }
        if (arcCourant.getFaceDroite() == arcCourant.getFaceGauche()) {
          arcCourant.setPendant(true);
        }
      }
    }
    this.fireActionPerformed(
        new ActionEvent(this, 4, I18N.getString("CarteTopo.FaceTopologyEnd"))); //$NON-NLS-1$
    // détection des arcs pendants ie des culs-de-sac de la face Infinie
    for (Arc arcCourant : this.getPopArcs()) {
      if (arcCourant.getFaceDroite() == null) {
        arcCourant.setFaceDroite(faceInfinie);
        if (arcCourant.getFaceGauche() == faceInfinie) {
          arcCourant.setPendant(true);
        }
      }
      if (arcCourant.getFaceGauche() == null) {
        arcCourant.setFaceGauche(faceInfinie);
        if (arcCourant.getFaceDroite() == faceInfinie) {
          arcCourant.setPendant(true);
        }
      }
    }
  }

  public  CarteTopo();
  public  CarteTopo(String nomLogique);

}