package eva2.server.go.operators.archiving;

import java.util.Arrays;
import java.util.Collections;
import java.util.Vector;

import eva2.server.go.individuals.AbstractEAIndividual;
import eva2.server.go.individuals.AbstractEAIndividualComparator;
import eva2.server.go.populations.Population;

public class ArchivingNSGAIISMeasure extends ArchivingNSGAII {

	/** This method will cacluated the NSGAII crowding distance
	 * for all individuals
	 * @param fronts    The pareto fronts
	 */
	public void calculateCrowdingDistance(Population[] fronts) { 
		//TODO Dimension der Zielfunktion checken

		for (int rank = 0; rank < fronts.length; rank++)
		{
			calculateCrowdingDistance(fronts[rank]);
		} 
	}


	/** This mehtod will test if a given individual is dominant within
	 * a given population
	 * @param indy          The individual that is to be tested.
	 * @param pop           The population that the individual is to be tested against.
	 * @return True if the individual is dominating
	 */
	@Override
	public boolean isDominant(AbstractEAIndividual indy, Population pop) {
		if (this.m_ObeyDebsConstViolationPrinciple) {
			for (int i = 0; i < pop.size(); i++) {
				if (!(indy.equals(pop.get(i))||indy.equalFitness((AbstractEAIndividual) pop.get(i))) && (((AbstractEAIndividual)pop.get(i)).isDominatingDebConstraints(indy))) return false;
			}
		} else {
			for (int i = 0; i < pop.size(); i++) {
				if (!(indy.equals(pop.get(i))||indy.equalFitness((AbstractEAIndividual) pop.get(i))) && (((AbstractEAIndividual)pop.get(i)).isDominating(indy))) return false;
			}
		}
		return true;
	}

	public void calculateCrowdingDistance(Population front) {

		Object[] frontArray= front.toArray();
		boolean[] assigned=new boolean[frontArray.length];

		double[] v=new double[frontArray.length];
		int i, left, right;

		// initialization of assignment vector
		for (i = 0; i < frontArray.length; i++)
		{
			assigned[ i ] = false;

		}


		Arrays.sort(frontArray,new AbstractEAIndividualComparator(0));


		((AbstractEAIndividual)frontArray[0]).putData("HyperCube",Double.MAX_VALUE); //die beiden außen bekommen maximal wert als smeasure
		((AbstractEAIndividual)frontArray[frontArray.length-1]).putData("HyperCube",Double.MAX_VALUE);
		v[0]=Double.MAX_VALUE;
		v[frontArray.length-1]=Double.MAX_VALUE;


		for (int e = 1; e < frontArray.length - 1; e++)
		{ // loop over all non-border elements
			for (i = 1; (assigned[  i  ]); i++); // determine 1st not assigned, non-border element

			for (left = 0; i <  frontArray.length - 1;)
			{   // loop over all not assigned elements
				// determine right not assigned neighbor
				for (right = i + 1; (assigned[ right]); right++);

				v[i] = (((AbstractEAIndividual)frontArray[right]).getFitness(0) - ((AbstractEAIndividual)frontArray[i]).getFitness(0)) *
				(((AbstractEAIndividual)frontArray[left]).getFitness(1) - ((AbstractEAIndividual)frontArray[i]).getFitness(1));

				left = i;
				i = right;
			}

			int minIndex = 0;
			double min=v[minIndex];
			for (int f = 1; f <  frontArray.length - 1; f++)
			{
				if (!assigned[  f  ])
					if (v[f] < min)
					{
						min = v[f];
						minIndex = f;
					}
			}
			assigned[  minIndex  ] = true;
			((AbstractEAIndividual)frontArray[ minIndex]).putData("HyperCube",new Double(e));
		}



	}
}
