/*
 * @(#) App.java 2003/03/22
 */

import java.util.Properties;


/**
 * The evaluation function. This implements the {@link CigarApp} 
 * interface to allow the GA to call methods that initialize the application
 * and track evolution of the phenotype.
 * 
 * @author Sushil J. Louis
 */
public class App implements CigarApp {

    /**
     * 
     */
    private int chromLength;                
    public sceneBuilder appScene = null;    //our current scene which is used
                                            //when checking for targets not
                                            //covered by fixed radars already
    
    public int lengthOfLat = 13;            //this is how many bits a latitude
                                            //coordinate takes in our chrom
                                            //representation
    public int lengthOfLong = 14;           // " " longitude " "
    
    private int mobileThreats;              //number of mobile threats
    
    
    private WaterCheck waterChecker = null; //used to check for mobile threats
                                            //landing in water
    
    private ScreenLoc carrier = null;       //where is the carrier?
    
    private int gaThreatRadius;             //how large the mobile targets'
                                            //radar coverage is
    
    /**
     * Constructor must set chromLength. This is used by {@link Population} to
     * allocate memory. We are assuming that whoever is writing the
     * evaluation, decoding, and encoding methods, must have the information
     * needed to calculate the chromsome length.
     *
     * @param props a properties object containing GA parameters including, 
     * hopefully the name of an input file with the key  "applicationFile".
     * @param r This GA system's random number generator. Use it and we
     * can get reproducible results.
     */
    public App(Properties props, GARandom r){
	chromLength = Integer.parseInt(props.getProperty("ChromosomeLength", 
							 "100"));
       
    };

    /**
     * Constructor must set chromLength. This is used by {@link Population} to
     * allocate memory. We are assuming that whoever is writing the
     * evaluation, decoding, and encoding methods, must have the information
     * needed to calculate the chromsome length.
     *
     * @param props a properties object containing GA parameters including, 
     * hopefully the name of an input file with the key  "applicationFile".
     */
    public App(Properties props, sceneBuilder newScene, int newMobileThreats,
        WaterCheck waterCheckInst, ScreenLoc newCarrier, int gaThreatRad){
            
	chromLength = Integer.parseInt(props.getProperty("ChromosomeLength", 
							 "100"));
        appScene = newScene;
        mobileThreats = newMobileThreats;
        waterChecker = waterCheckInst;
        carrier = newCarrier;
        gaThreatRadius = gaThreatRad;
    };

    /**
     * Returns an int chromosome length.
     */
    public int getChromLength(){
	return chromLength;
    }

    public void   phenoPrint(Population pop, String filename) {
	return;
    }

    public void   initPhenoPrint(Population pop, String filename) {
	return;
    }

    public void   initialize(Population pop){
	return;
    }

    public void   appInitChromosome(Individual member, Population pop){
	return;
    }

    public double eval(Individual member) throws ApplicationException {
        int i, j, k;                                      //loop counter
        int offset = 0;                             //how far we move on the chrom
        double sum = 0.0;                           //fitness
        double tempLat = 0.0;
        double tempLong = 0.0;
        ScreenLoc tempSL = new ScreenLoc(0, 0);     //used for the GA radar
        ScreenLoc targetSL = new ScreenLoc( 0, 0 );
        ScreenLoc radarSL = new ScreenLoc( 0, 0 );
        boolean targetCovered[] = new boolean[appScene.targetCount];
        
        //at first, all targets aren't covered
        for( i = 0; i < appScene.targetCount; i++ ){
            targetCovered[i] = false;
        }
        
        //check each path to each target.  Hope for dangerous routes
        for( i = 0; i < appScene.targetCount; i++ ){
                offset = 0;
                targetSL = appScene.targetArray[i].GetScreenLoc();
                
                //create a threat scene
                for( k = 0; k < mobileThreats; k++ ){
                    //get the locations out of the chrom
                    tempLat = createLat( member, offset );
                    offset += lengthOfLat;
                    tempLong = createLong( member, offset );
                    offset += lengthOfLong;

                    tempSL = appScene.targetMap.MapToScreenCoords( new
                        LatLong( tempLat, tempLong ) );
                    //oh, and if your target was on water.  you're out.
                    if( waterChecker.handlepixels(0, 0, 768, 549, tempSL.GetX(),
                            tempSL.GetY())){
                        sum = 0.1;
                        return sum;
                    }
                    
                    if( appScene.distanceTo( targetSL.GetX(), targetSL.GetY(),
                        tempSL.GetX(), tempSL.GetY()) <= 
                        (int)((double)gaThreatRadius/2.0) ){            //un hardcode this
                            sum += 1.0 * appScene.targetArray[i].GetImportance();
                            targetCovered[i] = true;
                    }
                    
                }
                for( j = 0; j < appScene.radarCount; j++ ){
                    radarSL = appScene.radarArray[j].GetScreenLoc();
                    if( appScene.distanceTo( targetSL.GetX(), targetSL.GetY(),
                        radarSL.GetX(), radarSL.GetY()) <= 
                        (int)(appScene.radarArray[j].GetRadius()/2.0) ){ 
                            targetCovered[i] = true;
                    }
                }
        }
        
        //now, if a target wasn't covered, your fitness will go down a little
        for( i = 0; i < appScene.targetCount; i++  ){
            if( targetCovered[i] == false ){
                sum -= 1.0 * appScene.targetArray[i].GetImportance();
            }
        }
        
        //don't want negative fitness
        if( sum < 0.0 ){
            sum = 0.001;
        }
       
        return sum;
    }
    
        /* CreateLat creates a Latitude coordinate from a member and
     * given index according to the encoding scheme.
     * @param member an Individual
     * @param index an integer of where the latitude coordinate begins
     */
    public static double createLat( Individual member, int index ){
        String tempString;
        tempString = Integer.toString( calcLat( member, index ));
        tempString += ".";
        tempString += Integer.toString( calcNine( member, index + 3 ));
        tempString += Integer.toString( calcNine( member, index + 5 ));
        return Double.parseDouble( tempString ) + 36.0;
    }
    
    /* CreateLong returns a longitude coordinate as a double
     * @param member an Individual from the population
     * @param index an int of where to start
     */
    public static double createLong( Individual member, int index ){
        String tempString;
        tempString = Integer.toString( calcLong( member, index ));
        tempString += ".";
        tempString += Integer.toString( calcNine( member, index + 3 ));
        tempString += Integer.toString( calcNine( member, index + 5 ));
        return Double.parseDouble( tempString ) + 118.0;
    }
    
    public static int calcLat(Individual member, int index){
        int sum = 0;
        if( member.chromosome[index] == 1 ){
            sum += 2;
        }
        if( member.chromosome[++index] == 1 ){
            sum+= 1;
        }
        if( member.chromosome[++index] == 1 ){
            sum+= 1;
        }
        return sum;
    }
    
    public static int calcNine( Individual member, int index ){
        int sum = 0;
        
        //walk through.  Max number is nine.  
        if( member.chromosome[index] == 1 ){
            sum += 4;
        }
        if( member.chromosome[++index] == 1 ){
            sum += 2;
        }
        if( member.chromosome[++index] == 1 ){
            sum += 1;
        }
        if( member.chromosome[++index] == 1 ){
            sum += 1;
        }
        if( member.chromosome[++index] == 1 ){
            sum += 1;
        }
           
        return sum;
    }
    
    public static int calcLong( Individual member, int index ){
        int sum = 0;
        if( member.chromosome[index] == 1 ){
            sum += 2;
        }
        if( member.chromosome[++index] == 1 ){
            sum += 1;
        }
        if( member.chromosome[++index] == 1 ){
            sum += 2;
        }
        if( member.chromosome[++index] == 1 ){
            sum += 1;
        }
        
        return sum;
    }
    

}
