package multinomad.algorithms;

import java.util.ArrayList;

import multinomad.config.Configuration;
import multinomad.config.LoadProperties;
import multinomad.config.Logger;
import multinomad.individuals.DoubleIndividual;
import multinomad.individuals.Population;
import multinomad.individuals.Trajectory;
import multinomad.tools.Selection;
import multinomad.tools.convex.ConvexFunction;
import multinomad.tools.Nomad;
import multinomad.tools.Seeds;
import multinomad.tools.kmeans.Centroid;
import multinomad.tools.kmeans.Kmeans;
import multinomad.tools.kmeansML.Centroids;
import multinomad.tools.kmeansML.SimpleKmeansJavaML;

// Exploring, Filtering, Clustering, Local search
public class EFCL implements Runnable{
	
	private Population solutions;
	private Population filteredPop;
	
public void run() {
		
	// Step 1: Explore (or sampling) the landscape with a large population
		Population pop = new Population(true);
		for(DoubleIndividual ind : pop) {
			//System.out.println("Initial:\t\t"+ ind.asString());
		}
		
	// Step 2: Filter the best solutions (using cooperative selection)
		filteredPop = new Population(false);
		
		for(int i=0;i<pop.size();i++) {
			filteredPop.add(
					Selection.select(pop)
					);
		}
		
		for(DoubleIndividual ind : filteredPop) {
			//System.out.println("Filtered:\t\t"+ ind.asString());
		}
		
	// Step 3: Calculate centroids (using kmeans)
		// -- Uncomment for SimpleKmeansJamaML
		// -- tested and Working
//		Centroids[] centroids = SimpleKmeansJavaML.computeCentroidsKmeans(filteredPop, Configuration.f.getNoOptima()*2, 100);
//		for (int i=0;i<centroids.length;i++) {
//			System.out.println("Centroids:\t\t"+ centroids[i].getAttributesAsString());
//		}
		
		// -- Uncomment for own implementation of Kmeans
		Kmeans kmeans = new Kmeans(filteredPop, Configuration.f.getNoOptima()*3);
		//System.out.println("Number of clusters: "+Configuration.f.getNoOptima());
		Population centroids = kmeans.cluster();
		int i=0;
		for (DoubleIndividual c : centroids) {
			Centroid cent = (Centroid)c;
			cent.setFitness(Configuration.f.evaluate(c.getChr().asdouble()));
			System.out.println("Centroid"+i+"Initial:\t"+cent.asString());
			
			for (DoubleIndividual cluster : cent) {
				//Logger.append("F"+Configuration.indexf+"Cluster", "Cluster"+i+":\t"+cluster.asString());
			}
				
			i++;
		}
		
		
		
	// Step 4: Reducing the number of centroids (using convex function)
		// 4.1. For every centroid, we locate its closest one
		// 4.2. Using the definition of convex function, we establish whether both centroids belong to the same basin of attraction
		//     4.2.1. If yes: we keep a single centroid and merge the items of both clusters 
		//     4.2.2. If not: we do nothing
		// 4.3. We rerun kmeans.cluster() in order to recompute the centroids

		centroids = ConvexFunction.reduceNumberOfCentroids(centroids, filteredPop);
		
		i=0;
		for (DoubleIndividual c : centroids) {
			Centroid cent = (Centroid)c;
			cent.setFitness(Configuration.f.evaluate(c.getChr().asdouble()));
			System.out.println("Centroid"+i+"Recomputed:\t"+cent.asString());
			
			for (DoubleIndividual cluster : cent) {
				//Logger.append("F"+Configuration.indexf+"Cluster", "Cluster"+i+":\t"+cluster.asString());
			}
				
			i++;
		}
		
	
	// Step 5: Salient solutions. 
		// 5.1. So far we have kept a list of centroids each having a list (i.e. cluster) of items. 
		// 5.2. We erase duplicates for every cluster.
		// 5.2. For every cluster, we just keep the best solutions (or alternatively the 2 or 3 best ones).
		
		
		
		
	// Step 6: Local search	performed over salient solutions
		// We apply MADS (as implemented in NOMAD) as a local search
	/*---------------------------------------------------------------	
	  solutions = new Population(false);
	 

		for(DoubleIndividual ind : filteredPop) {
			// Performs a hill-climber 
			Trajectory trajectory = Nomad.callPyNomadCec2013(ind.getChr().asdouble(), 0.1, 1000);
			DoubleIndividual result = trajectory.getBest();
			result.setFitness(Configuration.f.evaluate(result.getChr().asdouble()));
			// Shows results
			System.out.println("Result:\t\t"+ result.asString());
			//System.out.println("[Trajectory] Elapsed time: "+(System.currentTimeMillis()-start));
			
			
			solutions.add(result);
		}

		Seeds seeds = Configuration.f.how_many_goptima(solutions, 0.01);
		
		System.out.println("Found "+seeds.getCount()+" optima");
	--------------------------------------------------------------------*/	
//		if (numberOfOptima == Configuration.comp.getNoGoptima(Configuration.indexf)) {
//			System.out.println("Success: "+ numberOfOptima + " optima were found");	
//		}else {
//			System.out.println("Failure: "+ numberOfOptima + " out of "+ Configuration.comp.getNoGoptima(Configuration.indexf) +" optima were found");	
//		}
		
	}

	public Population getSolutions() {
		return solutions;
	}
	
	public Population getFilteredPop() {
		return filteredPop;
	}
	
	public static void main(String[] args) {
		//System.out.println(Pattern.quote("["));
		LoadProperties lp = new LoadProperties(args,null);
		Configuration.setConfiguration(lp);
		EFCL efcl = new EFCL();
		Thread t1 =new Thread(efcl);  
		t1.start();		
	}
}
