package multinomad.tools.kmeans;

import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
import java.util.Iterator;

import multinomad.individuals.DoubleChromosome;
import multinomad.individuals.DoubleIndividual;

public class Centroid extends DoubleIndividual implements Iterable<DoubleIndividual>{
	
	private ArrayList<DoubleIndividual> cluster;
	// Next variable is used for hierarchical kmeans
	// When set to true this cluster and all the respective data will be erased.
	private boolean markForDeleting = false;
	
	//A way to initialize the centroid
	public Centroid(DoubleIndividual ind) {
		super(ind.getChr().asdouble());
		cluster = new ArrayList<DoubleIndividual>();
	}
	
	public void updateMean(double[] newmean) {
		this.setChr(new DoubleChromosome(newmean));
	}
	
	public boolean isInCluster(DoubleIndividual ind) {
		double[] x2 =ind.getChr().asdouble();
		for(DoubleIndividual itemInCluster : cluster) {
			double[] x1 = itemInCluster.getChr().asdouble();
			if (areEqual(x1, x2)) {
				break;
			}
		}
		//return isInThere;
		return cluster.contains(ind);
	}
	
	public void eraseDuplicatesInCluster() {
		ArrayList<DoubleIndividual> auxcluster = new ArrayList<DoubleIndividual>();
		for(DoubleIndividual ind: cluster) 
			if(!auxcluster.contains(ind))
				auxcluster.add(ind);
		
		cluster = auxcluster;
	}
	/**
	 * 
	 * @param threshold
	 * @return true if there is at least one item (DoubleIndividual) that is equal or better than the threshold 
	 */
	public boolean aboveTheThreshold(double threshold) {
		markForDeleting = false;
		for (DoubleIndividual ind : cluster) {
			if (ind.getFitness() >= threshold)
				return true;
		}
		return false;
	}
	
	public void markForDelete() {
		markForDeleting = true;
	}
	
	public boolean toDelete() {
		return markForDeleting;
	}
	
	public ArrayList<DoubleIndividual> bestNinCluster(int n){
		ArrayList<DoubleIndividual> bestn = new ArrayList<DoubleIndividual>();
		
		Collections.sort(cluster, new Comparator<DoubleIndividual>() {
	        @Override
	        public int compare(DoubleIndividual i1, DoubleIndividual i2)
	        {
	        	Double fitnessI1 = new Double(i1.getFitness());
	        	Double fitnessI2 = new Double(i2.getFitness());
	            return  fitnessI2.compareTo(fitnessI1);
	        }
	    });
		
		int i = 0;
		for(DoubleIndividual ind : cluster) {
			bestn.add(ind);
			i++;
			if(i>=n) break;
		}
		
		return bestn;
	}
	
	
	
	
	private boolean areEqual(double[] x1, double[] x2) {
		for (int i=0;i<x1.length;i++) {
			if(x1[i] != x2[i])
				return false;
		}
		return true;
	}
	
	public boolean addToCluster(DoubleIndividual ind) {
		return cluster.add(ind);
	}
	
	public boolean removeFromCluster (DoubleIndividual ind) {
		return cluster.remove(ind);
	}
	
	public int clusterSize() {
		return cluster.size();
	}
	
	public ArrayList<DoubleIndividual> getCluster(){
		return cluster;
	}
	
	public Iterator<DoubleIndividual> iterator() {
		return cluster.iterator();
	}

	public void absorve(Centroid c2, ArrayList<DoubleIndividual> list) {
		this.addToCluster(c2);
		for (DoubleIndividual ind : list) {
			this.addToCluster(ind);
		}
	}

}
