// rdk

import java.util.*;
import java.io.*;

public class Evolve {
	
	public ArrayList organismFarm;
	public String idealPhrase;
	public double probabilityForMutation = .1;
	public int numOfStartingOrganisms = 400000;
	public int generationNumber = 1;
	public boolean sexualRep;
	

	
	public static void main(String[] args) {
		Evolve myEvolve = new Evolve();
		myEvolve.organismFarm = new ArrayList();
		ArrayList childrenOrganisms = new ArrayList();		// to put the children in before adding to the farm
		
		
		// get the 'ideal' organism for the environment from the user
		
		 BufferedReader stdin = new BufferedReader(new InputStreamReader( System.in ) );
		 try{
			 System.out.print("Enter the ideal phrase (amino acid 1-letter uppercase): ");
			 myEvolve.idealPhrase = stdin.readLine();
			 
			 System.out.print("asexual or sexual reproduction: ");
			 String reproduction = stdin.readLine();
			 if(reproduction.equals("asexual"))
				 myEvolve.sexualRep = false;
			 else
				 myEvolve.sexualRep = true;
			 
			 System.out.print("Enter the number of starting organisms: ");
			 String numOrgString = stdin.readLine();
			 myEvolve.numOfStartingOrganisms = Integer.parseInt(numOrgString);
			 
		 }
		 catch(Exception e){
			 e.printStackTrace();
		 }
		 
		 
		 
		Organism.idealPhrase = myEvolve.idealPhrase;
		Organism.probabilityForMutation = myEvolve.probabilityForMutation;
		
		// add single-letter organisms to the ArrayList
		for(int i = 0; i < myEvolve.numOfStartingOrganisms; i++){
			myEvolve.organismFarm.add(new Organism());
			
		}
		
		System.out.println("\nInitial");
		myEvolve.printStats(myEvolve);
		System.out.println("");
		
		myEvolve.removeZeroYearOrgs();		// remove the intial organisms that are too weak
		
		System.out.println("\nAfter initial organism death");
		System.out.println("***Generation : " + myEvolve.generationNumber + "vvv");
		myEvolve.printStats(myEvolve);
		System.out.println("***Generation : " + myEvolve.generationNumber + "^^^");
		System.out.println("");
		
		String userInput = null;
		
		while(true){
			System.out.print("Enter command: ");
			try{
				userInput = stdin.readLine();
				StringTokenizer stringTok = new StringTokenizer(userInput);
					if(stringTok.hasMoreTokens()){
						String command = stringTok.nextToken();
						
						// user wants to move forward generations
						if(command.equals("forward")){
							if(stringTok.hasMoreTokens()){
								int yearsToMove = Integer.parseInt(stringTok.nextToken());
								myEvolve.progressGeneration(myEvolve, childrenOrganisms, yearsToMove);
							}
							else{
								myEvolve.progressGeneration(myEvolve, childrenOrganisms, 1);
							}
						}
						
						// user wants to change the ideal organism
						else if(command.equals("changeto")){
							if(stringTok.hasMoreTokens()){
								myEvolve.idealPhrase = stringTok.nextToken();
								Organism.idealPhrase = myEvolve.idealPhrase;
								// update all organisms
								Iterator updateOrgIt = myEvolve.organismFarm.iterator();
								Organism tempOrg = null;
								while(updateOrgIt.hasNext()){
									tempOrg = (Organism)updateOrgIt.next();
									tempOrg.updateOrganism();
								}
								myEvolve.removeZeroYearOrgs();
								System.out.println("\nIdeal phrase changed to: " + myEvolve.idealPhrase);
							}
							else{
								System.out.println("\nError.  not valid input");
							}
						}
						
						// user wants to print out the current organisms
						else if(command.equals("printfarm"))
							myEvolve.printOrganisms();
						else if(command.equals("changemut")){
							if(stringTok.hasMoreTokens()){
								 myEvolve.probabilityForMutation = Double.parseDouble(stringTok.nextToken());
								
							}
							else{
								System.out.println("\nError.  not valid input");
							}
							
						}
							
						else if(command.equals("printstats"))
							myEvolve.printStats(myEvolve);
						
						else if(command.equals("printmaxsurvivevalue"))
							myEvolve.printMaxSurvivalValue();
						
						else if(command.equals("quit"))
							break;
						
						}
					else{
						System.out.println("\nError.  not valid input");
					}
				}catch(Exception e){
				e.printStackTrace();
				}
		}
		
		
		System.out.println("");
		System.out.println("*** Program terminated ***");
	}
	
	private void progressGeneration(Evolve myEvolve, ArrayList childrenOrganisms, int numberOfGenerations){
		for(int i = 0; i < numberOfGenerations; i++){
			myEvolve.subtractYear();
		
			if(sexualRep)
				myEvolve.sexualMatingPeriod(childrenOrganisms);
			else
				myEvolve.asexualMatingPeriod(childrenOrganisms);
			
			myEvolve.organismFarm.addAll(childrenOrganisms);
			
			childrenOrganisms.clear();
			
			myEvolve.removeZeroYearOrgs();
			
			myEvolve.generationNumber++;
			System.out.println("***Generation : " + myEvolve.generationNumber + "vvv");
			myEvolve.printStats(myEvolve);
			System.out.println("***Generation : " + myEvolve.generationNumber + "^^^\n");
			
			if(myEvolve.organismFarm.isEmpty()){
				System.out.println("All organisms dead");
				break;
			}
		}
	}
	
	private void printStats(Evolve myEvolve){
		System.out.println("Organisms remaining: " + myEvolve.organismFarm.size());
		
		Iterator counter = myEvolve.organismFarm.iterator();
		int numberOfSuccesses = 0;
		Organism counterTempOrg = null;
		double totalSurvivalRating = 0;
		while(counter.hasNext()){
			counterTempOrg = (Organism)counter.next();
			totalSurvivalRating += counterTempOrg.survivalValue;
			if(counterTempOrg.aminoAcidChain.toString().equals(myEvolve.idealPhrase))
				numberOfSuccesses++;
		}
		
		System.out.println("Number of successful organisms (" + myEvolve.idealPhrase + "): " + numberOfSuccesses);
		double percentage = 100 * ((double)numberOfSuccesses / (double)myEvolve.organismFarm.size());
		System.out.println("Percentage successful: " + percentage + "%");
		System.out.println("Avg survival rating: " + (totalSurvivalRating / (double)myEvolve.organismFarm.size()) + " out of: " + Organism.maxSurvivalValue);
		
	}
	
	private void printMaxSurvivalValue(){
		System.out.println("Max survival value: " + Organism.maxSurvivalValue);
	}
	
	private void sexualMatingPeriod(ArrayList tempOrganisms){
		// each organism in the farm mates once, if odd, then one organism does not mate that year
		ArrayList matingOrganisms = new ArrayList(organismFarm);
		
		while((!matingOrganisms.isEmpty()) && matingOrganisms.size() != 1){
			
			// randomly choose 2 organisms
			int p1 = getRandomNumber(matingOrganisms.size() - 1);
			Organism parent1 = (Organism)matingOrganisms.get(p1); 
			matingOrganisms.remove(parent1);
			int p2 = getRandomNumber(matingOrganisms.size() - 1);
			Organism parent2 = (Organism)matingOrganisms.get(p2); 
			matingOrganisms.remove(parent2);
			
			tempOrganisms.add(new Organism(parent1, parent2));	
		}	
		
	}
	
	private void asexualMatingPeriod(ArrayList tempOrganisms){
		// each organism in the farm mates once, if odd, then one organism does not mate that year
		Iterator organismIt = organismFarm.iterator();
		Organism tempOrg = null;
		
		while(organismIt.hasNext()){
			tempOrg = (Organism)organismIt.next();
			
			tempOrganisms.add(new Organism(tempOrg));	
		}	
		
	}
	
	private int getRandomNumber(int max){
		Random r = new Random();
		
		int randNum = Math.abs(r.nextInt()) % (max + 1);
		
		return randNum;
	}
	
	private void subtractYear(){
		Organism tempOrg;
		Iterator organismIterator = organismFarm.iterator();
		
		while(organismIterator.hasNext()){
			tempOrg = (Organism)organismIterator.next();
			tempOrg.yearsToLive--;
		}
		// subtract year from each 
	}
	
	private void removeZeroYearOrgs(){
		Organism tempOrg;
		Iterator organismIterator = organismFarm.iterator();
		
		while(organismIterator.hasNext()){
			tempOrg = (Organism)organismIterator.next();
			if(tempOrg.yearsToLive <= 0)
				organismIterator.remove();
		}
	}
	
	private void printOrganisms(){
		Organism tempOrg;
		Iterator organismIterator = organismFarm.iterator();
		
		while(organismIterator.hasNext()){
			tempOrg = (Organism)organismIterator.next();
			System.out.println("RNA: " + tempOrg.DNA + "\tAmino Acid Chain: " + tempOrg.aminoAcidChain + "\n\tsurvival value: " + tempOrg.survivalValue + "\n\tyears left: "
								+ tempOrg.yearsToLive + "\n\t parent1DNA: " + tempOrg.parent1DNA +" \t parent2DNA: " 
								+ tempOrg.parent2DNA +"\n\tnumber of mutations: " + tempOrg.numOfMuts + "\n\t lengthBonus: " 
								+ tempOrg.myLengthBonus +" \t matchingBonus: " + tempOrg.myMatchingBonus + " \t containsBonus: " + tempOrg.myContainsBonus);
		}
	}

}
