/*
 * threatGAApp.java
 *
 * Created on April 26, 2003, 11:27 AM
 */
import java.awt.*;
import java.lang.Math;
import java.awt.event.*;
import java.io.*;
import java.net.*;
import java.util.*;
import java.beans.*;

/**
 * This is an application specific GA wrapper class.
 * @author  Nick Cole
 */
public class threatGAApp {
   boolean DEBUG = false;
   /**
     * Random seed
     */ 
    static long seed;
    
    /* number of threats we're optimizing */
    public int mobileThreats;
    private int lengthOfChunk = 27;                     //how many bits it takes to 
                                                //encode a target
    private int gaPop, gaGen;                   //number of population and gens
    
    public Properties gaProps = new Properties();
    private sceneBuilder gaScene;
    private LinkedList threats = new LinkedList();

    
    /** Creates a new instance of threatGAApp 
     *  @param newScene the scene we're working with
     *  @param mobileThreatCount the number of GA optimized threats
     *  @param newGAPop the size of the population
     *  @param newGAGen the number of generation to run (maxGen)
     *  @param waterCheckInst an initialized watercheck
     *  @param newCarrier the location of the carrier (obsolete since no route)
     *  @param gaThreatRad how large the coverage of the mobile threat is
     */
    public threatGAApp(sceneBuilder newScene, int mobileThreatCount,
        int newGAPop, int newGAGen, WaterCheck waterCheckInst,
        ScreenLoc newCarrier, int gaThreatRad ) {
           
        //copy params
        mobileThreats = mobileThreatCount;
        gaPop = newGAPop;
        gaGen = newGAGen;
        threats = initGA( newScene, mobileThreatCount, newGAPop, newGAGen, waterCheckInst,
            newCarrier, gaThreatRad );
    }
    
   /** inits the GA and returns a linkedlist of the best inidivduals
     *  @param newScene the scene we're working with
     *  @param mobileThreatCount the number of GA optimized threats
     *  @param newGAPop the size of the population
     *  @param newGAGen the number of generation to run (maxGen)
     *  @param waterCheckInst an initialized watercheck
     *  @param newCarrier the location of the carrier (obsolete since no route)
     *  @param gaThreatRad how large the coverage of the mobile threat is
     */
    public LinkedList initGA(sceneBuilder newScene, int mobileThreatCount,
        int newGAPop, int newGAGen, WaterCheck waterCheckInst,
        ScreenLoc newCarrier, int gaThreatRad){
        int i;
        Random rn = new Random();                   //creates our random seed
        
        LatLong tempLL;
        
        //set the properties
        setProps();
        
        if( DEBUG ){
            System.out.println( "ThreatGA: Init Called" );
        }

        //set the seed
	GARandom ra = new GARandom(rn.nextLong());

        Population pop = null;
	App        a   = null;
         
        
        try {
	    a = new App(gaProps, newScene, mobileThreatCount, waterCheckInst,
                newCarrier, gaThreatRad);
	    pop = new Population(gaProps, a.getChromLength(), gaGen);
	}catch(Exception e){
	    System.err.println("Cannot construct Population");
	    e.printStackTrace();
	}
        
	Selector s = new Selector(gaProps, ra);
	Recombiner r = new Recombiner(gaProps, ra);

	Generator generation = new Generator(s, r, a);

	Statistician stat = new Statistician(gaProps);
	Reporter report = new Reporter(gaProps);
        
        try {
	    pop.initialize(a, stat, report);
	} catch (ApplicationException e){
	    System.err.println("Cannot initialize Population");
	}
       
	
	for( i = 1; i < gaGen; i++){
	    try {
		generation.execute(pop, i);
	    } catch (ApplicationException e){
		System.err.println("Cannot evolve generation " + i);

	    }
	    stat.statistics(pop);
	    report.report(pop, a, i);
	}
        
        //return the best individual
        return bestIndi(pop, a );
    }
    
    /* Returns the best individuals in the form of LatLongs
     * @param pop a GA Population
     * @param tempApp an instance of our application
     */
    private LinkedList bestIndi( Population pop, App tempApp ){

        LinkedList threats = new LinkedList();
        double bestFit;
        int bestIdx;
        int i;
        int offset = 0;             //number of bits until next feature
        LatLong tempLL = new LatLong( 0, 0 );
        double tempLat, tempLong;
        
        bestFit = -10000.0;   //initialize to large number
        bestIdx = -1;
      
        //now search for best in pop CHECK: may be function that does this
        for( i = 0; i < Integer.parseInt(gaProps.getProperty("PopulationSize"));
            i++){
                if( bestFit < pop.currentPop[i].fitness ){
                    bestFit = pop.currentPop[i].fitness;
                    bestIdx = i;
                }           
        }
        
        //walk through the list and add each new threat to the list
        for( i = 0; i < mobileThreats; i++ ){
            tempLat = tempApp.createLat( pop.currentPop[bestIdx], offset );
            offset += tempApp.lengthOfLat;
            tempLong = tempApp.createLong( pop.currentPop[bestIdx], offset );
            offset += tempApp.lengthOfLong;
            tempLL = new LatLong( tempLat, tempLong );
            threats.add( tempLL );
        }
        
        return threats;
    }
    
    private int allocateChunk(){
        return mobileThreats * lengthOfChunk;
    }
    
    /*  The reason I ditched the infile was I wanted to 
     *  have all the GA options controlable from the GUI, 
     *  not from an infile (which is inaccessable over the
     *  web).
     */
    private void setProps(){
        String chromlength = Integer.toString( allocateChunk() );
        gaProps.setProperty("PopulationSize", Integer.toString(gaPop) );
        gaProps.setProperty("ChromosomeLength", chromlength );
        gaProps.setProperty("MaxGen", Integer.toString(gaGen));
        gaProps.setProperty("PCross", "0.9");
        gaProps.setProperty("PMut", "0.01");
        gaProps.setProperty("CrossoverPoints", "1");
        gaProps.setProperty("ScaleFactor", "0.0");  
    }
    
    public LinkedList getThreats(){
        return threats;
    }
}
