package de.lmu.ifi.dbs.elki.database;

import java.util.HashMap;
import java.util.Map;
import java.util.Map.Entry;

import de.lmu.ifi.dbs.elki.data.ClassLabel;
import de.lmu.ifi.dbs.elki.data.Cluster;
import de.lmu.ifi.dbs.elki.data.Clustering;
import de.lmu.ifi.dbs.elki.data.DatabaseObject;
import de.lmu.ifi.dbs.elki.data.model.Model;
import de.lmu.ifi.dbs.elki.database.ids.DBIDs;
import de.lmu.ifi.dbs.elki.utilities.ClassGenericsUtil;
import de.lmu.ifi.dbs.elki.utilities.exceptions.ExceptionMessages;
import de.lmu.ifi.dbs.elki.utilities.exceptions.UnableToComplyException;

/**
 * This class derives a database partitioning based on a clustering result.
 * 
 * @author Erich Schubert
 * 
 * @apiviz.uses de.lmu.ifi.dbs.elki.database.Database oneway - - partitions
 */
public class PartitionsFromClustering {
  private String label_prefix = "C";

  /**
   * Use an existing clustering to partition a database.
   * 
   * @param <O> Database object type
   * @param <R> Clustering class
   * @param <L> Label class
   * @param olddb Original database
   * @param clustering Clustering to use for partitioning
   * @param classLabel ClassLabel class to use.
   * @return map from classlabels to database partitions.
   * @throws UnableToComplyException thrown on invalid data
   */
  public <O extends DatabaseObject, R extends Clustering<? extends Model>, L extends ClassLabel> Map<L,Database<O>> makeDatabasesFromClustering(Database<O> olddb, R clustering, Class<L> classLabel) throws UnableToComplyException {
    // we need at least one cluster
    if (clustering.getToplevelClusters().size() <= 0) {
      throw new UnableToComplyException(ExceptionMessages.CLUSTERING_EMPTY);
    }

    // prepare a map for the partitioning call.
    Map<Integer, DBIDs> partitions = new HashMap<Integer, DBIDs>();
    int clusterID = 1;
    for(Cluster<? extends Model> c : clustering.getAllClusters()) {
      DBIDs col = c.getIDs();
      partitions.put(clusterID, col);
      clusterID++;
    }
    Map<Integer,Database<O>> newdb = olddb.partition(partitions);
    
    Map<L, Database<O>> map = new HashMap<L, Database<O>>();

    // build result map
    for(Entry<Integer, Database<O>> e : newdb.entrySet()) {
      L label = ClassGenericsUtil.instantiate(classLabel, classLabel.getName());
      label.init(label_prefix + e.getKey().toString());
      map.put(label, e.getValue());
    }

    return map;
  }

  /**
   * Use an existing clustering to partition a database.
   * 
   * @param <O> Database object type
   * @param <R> Clustering class
   * @param olddb Original database
   * @param clustering Clustering to use for partitioning
   * @return map from clusters to database partitions.
   * @throws UnableToComplyException thrown on invalid data
   */
  public <O extends DatabaseObject, R extends Clustering<? extends Model>> Map<Cluster<? extends Model>,Database<O>> makeDatabasesFromClustering(Database<O> olddb, R clustering) throws UnableToComplyException {
    // we need at least one cluster
    if (clustering.getToplevelClusters().size() <= 0) {
      throw new UnableToComplyException(ExceptionMessages.CLUSTERING_EMPTY);
    }

    // prepare a map for the partitioning call.
    Map<Integer, DBIDs> partitions = new HashMap<Integer, DBIDs>();
    Map<Integer, Cluster<? extends Model>> clusters = new HashMap<Integer, Cluster<? extends Model>>();
    int clusterID = 1;
    for(Cluster<? extends Model> c : clustering.getAllClusters()) {
      DBIDs col = c.getIDs();
      partitions.put(clusterID, col);
      clusters.put(clusterID, c);
      clusterID++;
    }
    Map<Integer,Database<O>> newdb = olddb.partition(partitions);
    
    Map<Cluster<? extends Model>, Database<O>> map = new HashMap<Cluster<? extends Model>, Database<O>>();

    // build result map
    for(Entry<Integer, Database<O>> e : newdb.entrySet()) {
      map.put(clusters.get(e.getKey()), e.getValue());
    }

    return map;
  }
}
@@ -1333,34 +1333,24 @@
  Clustering%3C
-? extends 
 Model%3E, L ex
@@ -1828,34 +1828,24 @@
 for(Cluster%3C
-? extends 
 Model%3E c : c
@@ -2516,32 +2516,59 @@
 lustering class%0A
+   * @param %3CM%3E Model type%0A
    * @param oldd
@@ -2823,17 +2823,21 @@
 stering%3C
-?
+M%3E, M
  extends
@@ -2843,17 +2843,16 @@
 s Model%3E
-%3E
  Map%3CClu
@@ -2856,31 +2856,17 @@
 Cluster%3C
-? extends Model
+M
 %3E,Databa
@@ -3283,31 +3283,17 @@
 Cluster%3C
-? extends Model
+M
 %3E%3E clust
@@ -3327,31 +3327,17 @@
 Cluster%3C
-? extends Model
+M
 %3E%3E();%0A  
@@ -3373,31 +3373,17 @@
 Cluster%3C
-? extends Model
+M
 %3E c : cl
@@ -3621,39 +3621,25 @@
 Map%3CCluster%3C
-? extends Model
+M
 %3E, Database%3C
@@ -3672,23 +3672,9 @@
 ter%3C
-? extends Model
+M
 %3E, D

Undo generics removal - we need it in FIRES code.