import java.io.File;
import java.io.FileWriter;
import java.io.IOException;
import java.text.DateFormat;
import java.text.DecimalFormat;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Date;
import java.util.Locale;
/**
 * \brief Trida ktera obsluhuje chod genetickeho algoritmu
 * @author nemec
 *
 */
public class Populate {
	/**
	 * Funkce pro chod genetickeho algoritmu podle diagramu: Nova populace->Hodnoceni nove populace->Vytvor novou populaci pomoci selekce, krizeni a mutace->Ohodnot novou populaci
	 * @param selectionType Typ selekce 1-Tournament
	 * @param crossoverType Typ krizeni 1-Order one, 2-PMX, 3-Edge Recombination, Default-Order One
	 * @param mutationType Typ mutace 1-Insert 2-Swap 3-Inverse 4-Scramble Default-Insert
	 * @param mutationRate Pravdepodobnost mutace v rozsahu 0.0-1.0
	 * @param numOfGenerations Pocet generaci - pocet iteraci algoritmu
	 * @param numOfChromozomes Pocet chromozomu v populaci
	 * @param cityLoader Objekt CityLoader - Pokud je pouzit tspParser, pak je tuto hodnotu treba nastavit na null
	 * @param tspParser Objekt tspParser - Pokud je pouzit cityLoader, pak je tuto hodnotu treba nastavit na null
	 * @param tournamentSize - Velikost turnaje
	 * @param fileName - Nazev vstupniho souboru
	 * @throws IOException
	 */
	public static void populateTSP(int selectionType, int crossoverType, int mutationType, double mutationRate, int numOfGenerations, int numOfChromozomes, CityLoader cityLoader, TspParser tspParser, int tournamentSize, String fileName) throws IOException{
		
		final String COMMA_DELIMITER = ";";
		final String NEW_LINE_SEPARATOR = "\n";
		final String fileHeader = "Generation;Best fitness";
		final String selectionTypeString = "Selection type: "+String.valueOf(selectionType);
		final String tournamentSizeString = "Tournament size: "+String.valueOf(tournamentSize);
		final String crossoverTypeString = "Crossover type: "+String.valueOf(crossoverType);
		final String mutationTypeString = "Mutation type: "+String.valueOf(mutationType);
		final String mutationRateString = "Mutation rate: "+String.valueOf(mutationRate);
		final String numOfChromozomesString = "Number of chromozomes: "+String.valueOf(numOfChromozomes);
		
		Long time1 = System.currentTimeMillis();
		Long time2;
		
		DateFormat dateFormat = new SimpleDateFormat("dd_MM_yyyy_HH_mm_ss");
		Date date = new Date();
		String namecsv = "results of_"+fileName+"_"+dateFormat.format(date).toString()+"_crossover_type_"+".csv";
		
		File file = new File("C:\\Users\\nemec\\workspace\\GeneticAlgorithm\\results\\"+namecsv);
		FileWriter fileWriter = new FileWriter(file);
		
		fileWriter.append(selectionTypeString);
		fileWriter.append(NEW_LINE_SEPARATOR);
		fileWriter.append(tournamentSizeString);
		fileWriter.append(NEW_LINE_SEPARATOR);
		fileWriter.append(crossoverTypeString);
		fileWriter.append(NEW_LINE_SEPARATOR);
		fileWriter.append(mutationTypeString);
		fileWriter.append(NEW_LINE_SEPARATOR);
		fileWriter.append(mutationRateString);
		fileWriter.append(NEW_LINE_SEPARATOR);
		fileWriter.append(numOfChromozomesString);
		fileWriter.append(NEW_LINE_SEPARATOR);
		fileWriter.append(fileHeader);
		fileWriter.append(NEW_LINE_SEPARATOR);
		
		Population population1;
		
		if(tspParser == null){
			population1 = new Population(numOfChromozomes, cityLoader.getNumOfCities(), cityLoader.getDistances());
		}
		else{
			population1 = new Population(numOfChromozomes, tspParser.getSize(), tspParser.getDistances());
		}
		Population population2;
		Chromozome parent1;
		Chromozome parent2;
		Chromozome offspring;
		int generationsCount = 1;
		
		population1.ratePopulation();
		
		while(generationsCount < numOfGenerations+1){
			
			if(tspParser == null){
				population2 = new Population(cityLoader.getDistances());
			}
			else{
				population2 = new Population(tspParser.getDistances());
			}
			
			while(population2.getSize() < numOfChromozomes){
				
				switch(selectionType){
				
				case 1:
					parent1 = Selection.tournamentSelection(tournamentSize, population1);
					parent2 = Selection.tournamentSelection(tournamentSize, population1);
					break;
					
				case 2:
					parent1 = Selection.rouletteWheelBasedFitness(population1);
					parent2 = Selection.rouletteWheelBasedFitness(population1);
					break;
					
				case 3:
					parent1 = Selection.rouletteWheelBaseRank(population1, 1);
					parent2 = Selection.rouletteWheelBaseRank(population1, 1);
					break;
					
				default:
					parent1 = Selection.tournamentSelection(tournamentSize, population1);
					parent2 = Selection.tournamentSelection(tournamentSize, population1);
					break;
				}
				
				switch(crossoverType){
				
				case 1:
					offspring = Crossover.orderOneCrossOver(parent1, parent2, mutationRate, mutationType);
					break;
					
				case 2:
					offspring = Crossover.pmxCrossOver(parent1, parent2, mutationRate, mutationType);
					break;
					
				case 3:
					offspring = Crossover.edgeRecombinationCrossOver(parent1, parent2, mutationRate, mutationType);
					break;
					
				default:
					offspring = Crossover.orderOneCrossOver(parent1, parent2, mutationRate, mutationType);
					break;
				}
				
				population2.addChromozome(offspring);
			}
			
			//System.out.println(population2.getSize());
			population2.ratePopulation();
			System.out.println("Best fitness is: " + population2.getChromozome(population2.getBestFit()).getFitness());
			System.out.println("The best chromosome: ");
			population2.getChromozome(population2.getBestFit()).printChromozome();
			System.out.println("--------------------------------------------------------------------------");
			population1 = population2;
			
			Long result = Math.round(population2.getChromozome(population2.getBestFit()).getFitness());
			String print = String.valueOf(result);
			
			fileWriter.append(String.valueOf(generationsCount));
			fileWriter.append(COMMA_DELIMITER);
			fileWriter.append(print);
			fileWriter.append(NEW_LINE_SEPARATOR);
			
			generationsCount++;
		}
		
		time2 = System.currentTimeMillis() - time1;
		String time = String.valueOf(time2);
		
		fileWriter.append(time);
		fileWriter.append(NEW_LINE_SEPARATOR);
		
		fileWriter.flush();
		fileWriter.close();
	}
	
	public static void populateHW(int selectionType, int crossoverType, int mutationType, double mutationRate, int numOfGenerations, int numOfChromozomes, int tournamentSize, String target){
		
		PopulationHW population1 = new PopulationHW(numOfChromozomes, target);
		PopulationHW population2;
		ChromozomeHW parent1;
		ChromozomeHW parent2;
		ArrayList<ChromozomeHW> offsprings;
		int generationsCount = 1;
		boolean done = false;
		
		System.out.println("Best fitness is: " + population1.getChromozome(population1.getBestFit()).getFitness());
		System.out.println("The best chromosome: ");
		population1.getChromozome(population1.getBestFit()).printChromozome();
		System.out.println("--------------------------------------------------------------------------");
		
		while(done != true){
			
			population2 = new PopulationHW(target);
			
			while(population2.getSize() < numOfChromozomes){
				
				switch(selectionType){
				
				case 1:
					parent1 = SelectionHW.tournamentSelection(tournamentSize, population1);
					parent2 = SelectionHW.tournamentSelection(tournamentSize, population1);
					break;
					
				default:
					parent1 = SelectionHW.tournamentSelection(tournamentSize, population1);
					parent2 = SelectionHW.tournamentSelection(tournamentSize, population1);
					break;
					
				}

				switch(crossoverType){
				
				case 1:
					offsprings = CrossoverHW.onePointCrossover(parent1, parent1, mutationRate, mutationType);
					break;
					
				case 2:
					offsprings = CrossoverHW.twoPointCrossover(parent1, parent2, mutationRate, mutationType);
					break;
					
				case 3:
					offsprings = CrossoverHW.uniPointCrossover(parent1, parent2, mutationRate, mutationType);
					break;
					
				default:
					offsprings = CrossoverHW.onePointCrossover(parent1, parent2, mutationRate, mutationType);
					break;
				}
				
				population2.addChromozome(offsprings.get(0));
				population2.addChromozome(offsprings.get(1));		
			}
			
			population2.ratePopulation();
			System.out.println("Best fitness is: " + population2.getChromozome(population2.getBestFit()).getFitness());
			System.out.println("The best chromosome: ");
			population2.getChromozome(population2.getBestFit()).printChromozome();
			System.out.println("--------------------------------------------------------------------------");
			
			population1 = population2;
			generationsCount++;
			
			if(population2.getChromozome(population2.getBestFit()).getFitness() == 0){
				done = true;
			}
			System.out.println(generationsCount);	
		}	
	}	
}
