import java.util.ArrayList;
/**
 * \brief Trida, ktera reprezentuje populaci
 * @author nemec
 *
 */
public class Population {

	private ArrayList<Chromozome> population = new ArrayList<Chromozome>();
	private int popCount;
	private int genCount;
	private int bestFit;
	private double[][] distances;
	/**
	 * Konstruktor, ktery vytvari populaci
	 * @param populationCount Pocet chromozomu v populaci
	 * @param numOfGenes Pocet genu, jednotlivych chromozomu
	 * @param distances Tabulka se vzdalenostmi mezi jednotlivymi mesty
	 */
	public Population(int populationCount, int numOfGenes, double[][] distances){
		
		for(int i=0; i<populationCount; i++){
			
			this.popCount = populationCount;
			this.genCount = numOfGenes;
			this.distances = distances;
			
			this.population.add(new Chromozome(numOfGenes));
			
		}
		
		
		System.out.println("Population with size of " + this.popCount + " chromozomes was created!");
		System.out.println("--------------------------------------------------------------------------");
		
	}
	
	public Population(double[][] distances){
		
		this.distances = distances;
		
	}
	
	public void printPopulation(){
		
		for(int i=0; i<this.popCount; i++){
			
			this.population.get(i).printChromozome();
			System.out.println();
			
		}
	}
	/**
	 * Trida, ktera hodnoti jednotlive chromozomy v populaci pomoci funkce tspFitness
	 */
	public void ratePopulation(){
		
		for(int i=0; i<this.popCount; i++){
			this.population.get(i).setFitness(Fitness.tspFitness(this.population.get(i), distances));
		}
	}
	/**
	 * Funkce ktera vraci vybrani chromozom
	 * @param index Pozice chromozomu
	 * @return Vybrany chromozom
	 */
	public Chromozome getChromozome(int index){
		return this.population.get(index);
	}
	/**
	 * Funkce, ktera vraci velikost populace
	 * @return Velikost populace
	 */
	public int getSize(){
		return this.popCount;
	}
	
	public int getFitnessSum(){
		
		int fitnesssum=0;
		
		for(int i=0;i<this.popCount;i++){
			fitnesssum+=this.population.get(i).getFitness();
		}
		
		return fitnesssum;
	}
	
	public void sortByFitness(){
		int k;
		for(int i=this.popCount; i>=0; i--){
			for(int j=0; j<this.popCount-1;j++){
				k = j+1;
				if(this.population.get(j).getFitness() > this.population.get(k).getFitness()){
					swapChromozomes(j, k, this.population);
				}
			}
		}
	}
	
	private void swapChromozomes(int i, int j, ArrayList<Chromozome> arrayList){
		Chromozome temp;
		temp = arrayList.get(i);
		arrayList.set(i, arrayList.get(j));
		arrayList.set(j, temp);
	}
	/**
	 * Funkce, ktera pridava chromozom na posledni misto v populaci
	 * @param chromozome Vstupni chromozom
	 */
	public void addChromozome(Chromozome chromozome){
		this.population.add(chromozome);
		this.popCount++;
	}
	/**
	 * Funkce, slouzici k nalezeni nejlepsiho chromozomu v populaci
	 */
	public void findBestFitness(){
		
		int bestFit = 0;
		double bestFitness = this.population.get(0).getFitness();
		
		
		for (int i=0; i< this.popCount; i++){
			if(this.population.get(i).getFitness() <= bestFitness){
				bestFit = i;
				bestFitness = this.population.get(i).getFitness();
			}
		}
		
		this.bestFit = bestFit;
	}
	/**
	 * Funkce, ktera vraci pozici nejlepsiho chromozomu v populaci
	 * @return Pozice nejlepsiho chromozomu
	 */
	public int getBestFit(){
		return this.bestFit;
	}
}
