/*
  Het echte rekenen aan TSP gebeurt hier...
  Casper Eyckelhof - November 2000

*/
import java.lang.Math;
import java.io.*;
import java.util.Date;

public class AntsThread extends Thread{
    
    //Select debug-mode on/off (true/false)
    private final static boolean debug = false;
    
    private final static boolean visualUpdates = false;
    
    private DTSP parent;
    private Kaart kaart;

    private Tour[] tourLijst;
    private Tour kortsteTour;
    private double kortsteLengte;
    private int aantal_ants;
    private int aantal_steden;

    public int alfa;
    public int beta;
    public int Q;
    public double rho;
    public int e;

    private boolean stopThread; // 'Communicatie' met de thread kan via deze boolean
    
    private FileOutputStream debugStream;
    private FileOutputStream resultStream;
    private FileOutputStream plotStream;
    
    private final static double schudpercentage = 25.0;


    //Constructor
    AntsThread(DTSP parent) {
       this.parent = parent;
       this.kaart = parent.kaart;
       aantal_ants = kaart.grootte();
       aantal_steden = aantal_ants;   //voor juiste naam, in juiste context
       kortsteLengte = 9999999.0; //liever iets van maxdouble...
       parent.setParams(this);
       
       try {
          long filenumber = (new Date()).getTime();
          if (debug){
             debugStream = new FileOutputStream(filenumber+"debug.txt",false); //true for append
          }
          resultStream = new FileOutputStream(filenumber +"result.dat",false); //true for append
          plotStream = new FileOutputStream(filenumber+"plot"+".plt",false); //true for append
          //Maak plotfile:
          String titel = "\"Alfa ="+alfa + "\\nBeta ="+beta+"\\nRho ="+rho+"\\nQ ="+Q +"\\nEpsilon ="+e+"\"";
          String s = "set terminal png small color\nset output \""+filenumber+".png\"\nset data style lines\nset xlabel 'Iterations'\nset ylabel 'Tour Length (best sofar)'\nset xrange [0:500]\nset yrange [430:500]\nset title "+ titel+"\nplot '"+ filenumber +"result.dat'";
          //String s = "set terminal png small color\nset output \""+filenumber+".png\"\nset data style lines\nset xlabel 'Iterations'\nset ylabel 'Tour Length (best sofar)'\nset xrange [0:1000]\nset yrange [700:1000]\nset title "+ titel+"\nplot '"+ filenumber +"result.dat'";
          plotStream.write(s.concat("\n").getBytes());
          
       }  catch (IOException e) {
          System.out.println("Error with file: " + e);
       }       
    }


    public void stopThread(){
       stopThread = true;
    }

    public boolean isStopped(){
        return stopThread;
    }

    //Print s somewhere
    private void debug(String s){
        parent.uitvoerArea.append(s+"\n");
    }
    
    //Write data to file (for debug info)
    private void fdebug(String s){
     if (debug){	
       try { 
        debugStream.write(s.concat("\n").getBytes());
       } catch (Exception e) {
          System.out.println("Error with file: " + e);
       }
     }    
    } 
    
    //Write data to file (for delimited results) 
    private void result(int ronde, double besteLengte){
       try {
       	String s = ronde + " " + besteLengte + "\n"; 
        resultStream.write(s.getBytes());
       } catch (Exception e) {
          System.out.println("Error with file: " + e);
       }  
    }    

    public Tour getBestTour(){
       return kortsteTour;	
    }	

    /*
       Bepaalt de volgende stad zoals in AS TSP algoritme beschreven
    */
    private int volgendeStad(Tour t){
       int huidige_stad = t.current();
       //double[] kans_array = new double[aantal_steden+1]; //0 doet weer niet mee :)
       double[] tellers = new double[aantal_steden+1]; //0 doet weer niet mee :)
       double sum_ljk = 0;
       for (int s = 1; s <= aantal_steden; s++){
         if (!t.blackListed(s)){
         	   //WARNING!!!!! Alfa is altijd 1 in mijn prototype, dus de machtsverheffing is overdreven
         	   //maar het kan bij het spelen met parameters erg verwarrend worden!
               double geurTemp = kaart.geur(huidige_stad,s); //eigenlijk tot de macht alfa (==1)
               double visibilityTemp = Math.pow(1/kaart.afstand(huidige_stad,s),beta);
               tellers[s] = geurTemp * visibilityTemp ;
               sum_ljk += tellers[s] ;
         } else {
         //stad s is blacklisted in deze (partiele) tour.
            tellers[s] = 0.0;
         } 	
       }
       
       double kans = Math.random();
       int stad = 1;
       double kanssom = tellers[stad] / sum_ljk;
       while ( (kanssom < kans) ) {
          stad++;
          kanssom += tellers[stad] / sum_ljk;
       }
       return stad;            
    }


    public void run(){
       int[] stijgweg = new int[2]; //stijgweg[0] = van, stijgweg[1]=naar (stad index) 
       int[] daalweg = new int[2];  //idem
       int stadteller = 1; //index van stad voor verlengen
       	
       System.out.println("AntsThread running");
       stopThread = false;
       String route ="";
       boolean actie = false;
       //write settings for this run to resultfile
       try {
           String s = "# Alfa ="+alfa +"\n# Beta ="+beta+"\n# Rho ="+rho+"\n# Q ="+Q +"\n# Epsilon ="+e+"\n"; 
           resultStream.write(s.getBytes());
       }  catch (Exception e) {
       	   System.out.println("Error with file: " + e);
       }	    
       try {
          int teller = 1;
          Tour werk_tour;
          while (!stopThread) {
              //Nieuwe tourlijst, past precies voor elke mier 1 tour in
             tourLijst = new Tour[aantal_ants + 1];
             fdebug("Nieuwe ronde ("+teller+")");
             for(int k = 1 ; k <= aantal_ants ; k++){
                //Geef de huidige ant (nummer k) een (lege)tour
                werk_tour = new Tour(aantal_ants, kaart);
                //Voeg de startpositie in de tour,
                //Ant nummer k start zijn tour altijd op stad met nummer k
                // (er zijn ook even veel steden als mieren)
                werk_tour.voegToe(k);
                fdebug(teller+"."+k);
                //Nu zijn er nog k-1 steden toe te voegen
                for (int n = 2 ; n <= aantal_steden ; n++){
                   int volgende_stad = volgendeStad(werk_tour) ;
                   //fdebug("Ga toevoegen: "+volgende_stad);
                   werk_tour.voegToe( volgende_stad );
                }
                //debug("Lengte van afgelopen tour is: "+werk_tour.lengte());
                tourLijst[k] = werk_tour;
              } //alle ants hebben er weer een rondje opzitten
              //Uitzoeken welke mier de beste was deze ronde
              double besteronde = tourLijst[1].lengte();
              int bestemier = 1;
              for (int mier=2; mier <= aantal_ants;mier++){
                 if (tourLijst[mier].lengte() < besteronde){
                     besteronde = tourLijst[mier].lengte();
                     bestemier = mier;
                 }
              }
           /*
              String route = "";
              for (int i=1; i<= aantal_steden; i++){
                      route += tourLijst[bestemier].route()[i]+", ";
              }
              fdebug("   Kortste tour in ronde "+teller+" is van mier "+ bestemier +": "+ besteronde+ ". Route: "+ route );
           */
              if (besteronde < kortsteLengte) {
              //globaal ook beste ronde!
                 route = "";
                 for (int i=1; i<= aantal_steden; i++){
                      route += tourLijst[bestemier].route()[i]+", ";
                 }
                 kortsteTour = (tourLijst[bestemier]).kloon();
                 kortsteLengte = besteronde;
                 debug("Kortste tour is nu van mier "+ bestemier +": "+ kortsteLengte+ ". Route: "+ route );
                 if (visualUpdates){
                    parent.map.setRoute( tourLijst[bestemier].kloon() );
                 }   
                 
              }
              result(teller,kortsteLengte);
              yield();
              //En nu de geursporen updaten:
               // debug("Begin trail-update");
                for (int i=1; i <= aantal_steden; i++) {
                   for (int j=1; j <= aantal_steden; j++) {
                      double dtijk = 0;
                      double dtije = 0;
                      //debug("Nu zijn we bij: "+i+" , "+j);
                      for (int stad =1 ; stad <= aantal_steden; stad++){
                          // debug("Nu zijn we bij: "+i+" , "+j+" , "+stad);
                          if (tourLijst[stad].bevatWeg(i,j)) {
                             dtijk += Q / tourLijst[stad].lengte();
                          }
                      }
                      //Elitist ant
                      if (kortsteTour.bevatWeg(i,j)) {
                         dtije = Q / kortsteLengte;
                      }
                      //Nu de echte update
                      double oudeGeur = kaart.geur(i,j);
                      double nieuweGeur = (1- rho)*oudeGeur + dtijk + e * dtije;
                      //nooit minder geur dan minimum waarde
                      if (nieuweGeur < kaart.tau_0) 
                         nieuweGeur = kaart.tau_0;
                      kaart.setGeur(i,j,nieuweGeur);

                   }
                } 
              teller++;
              //Hier bouwen we onze file / warpkromming
              
              if (teller == 600){
              	  stijgweg[0] = stadteller;
              	  stijgweg[1] = kortsteTour.next(stadteller);
              	  stadteller++;
              	  //hack! weg van 0 naar 0 bestaat niet!
              	  daalweg[0] = 0;
              	  daalweg[0] = 0; 
              	  debug("Stijgweg is van "+ stijgweg[0] +" naar "+ stijgweg[1] );
              	  actie = true;
              }	 
              
              if ((teller > 600) && (teller % 50 == 0)){
              	  actie = true;
              	  daalweg = stijgweg;
              	  stijgweg = new int[2];
              	  stijgweg[0] = stadteller;
              	  stijgweg[1] = kortsteTour.next(stadteller);
              	  stadteller++;
              	  debug("Teller staat op: "+teller);
              	  debug("Stijgweg is van "+ stijgweg[0] +" naar "+ stijgweg[1] );
              	  debug("Daalweg is van "+ daalweg[0] +" naar "+ daalweg[1] );
              }
              
              if ( (teller-25) % 50 ==0 ) {
              	 actie = false;
              }	
          
              if (actie && (teller % 5 == 0)) {
              	  kaart.setAfstand(stijgweg[0],stijgweg[1], kaart.afstand(stijgweg[0],stijgweg[1]) +10);
              	  kaart.setAfstand(daalweg[0],daalweg[1], kaart.afstand(daalweg[0],daalweg[1]) -10);
              	  kortsteLengte = kortsteTour.berekenLengte();
              	  debug("stijg-daal: "+teller);
              	  //kaart.schud();
              	  //kaart.schudlokaal(stijgweg[0],stijgweg[1],schudpercentage); 
                  //kaart.schudlokaal(daalweg[0],daalweg[1],schudpercentage);
              }	          
          
        /*      	  
              if ((teller >= 50) && (teller <= 150) && (teller % 5 ==0)) {
                kaart.setAfstand(17,21,kaart.afstand(17,21)+10);
                kortsteLengte = kortsteTour.berekenLengte();
                debug("KorsteLengte is na extra file: "+kortsteLengte);
                //kaart.schud();
                //kaart.reset();
                kaart.schudlokaal(17,21,schudpercentage);
              }
          
              if ((teller >= 200) && (teller <= 250) && (teller % 5 ==0)) {
              	 kaart.setAfstand(17,21,kaart.afstand(17,21)-10);          //oude file oplossen
                kaart.setAfstand(11,12,kaart.afstand(11,12)+10);           //nieuwe bouwen
                kaart.setAfstand(2,3,kaart.afstand(2,3)+10);               //en nog 1 :)
                kortsteLengte = kortsteTour.berekenLengte();
                debug("KorsteLengte is na extra file: "+kortsteLengte);
                //kaart.schud();
                //kaart.reset();
                
                kaart.schudlokaal(17,21,schudpercentage);
                kaart.schudlokaal(11,12,schudpercentage);
                kaart.schudlokaal(2,3,schudpercentage);
              }
          */  
              if (teller == 5600){ 
                stopThread = true;
                try {
                  String s2 = "# Kortste tour van mier "+ bestemier +": "+ kortsteLengte+ ". Route: "+ route;
                  resultStream.write(s2.getBytes());
                }  catch (Exception e) {
       	           System.out.println("Error with file: " + e);
                }
              }
           }//main loop (while)
       } catch (Exception e) {
        //Exception handling here
        System.out.println("Exception in AntsThread: " + e.toString());
       }
       debug("Klaar met deze run!");
           
    }




//Niet gebruikte code dumpplek
//int volgende_stad = k-1+n;
//if (volgende >= aantal_steden+1) volgende -= aantal_steden;
/*       
       for (int stad =1; stad <= aantal_steden; stad++){
           if (t.blackListed(stad)){
              //kans_array[stad] = 0;  //kans om naar een blacklisted stad te gaan is 0!
           } else {
              double sum_ljk = 0;
              for (int s = 1; s <= aantal_steden; s++){
                 if (!t.blackListed(s)){
                   double geurTemp = Math.pow(kaart.geur(huidige_stad,s),alfa);
                   double visibilityTemp = Math.pow(1/kaart.afstand(huidige_stad,s),beta);
                   sum_ljk += geurTemp * visibilityTemp ;
                 }
              }
              double geurTemp2 = Math.pow(kaart.geur(huidige_stad,stad),alfa);
              double visibilityTemp2 = Math.pow(1/kaart.afstand(huidige_stad,stad),beta);
              //fdebug("    Stad "+stad+": geur * visibility=" + geurTemp2 +" * "+ visibilityTemp2 +" . sum_ljk="+sum_ljk);
              //kans_array[stad] = geurTemp2 * visibilityTemp2 / sum_ljk;
              kanssom += geurTemp2 * visibilityTemp2 / sum_ljk;
              if (kanssom >= kans) {
              	  return stad;
              }	
           }
       }
       
       
       //String kansregel = "";
       //for (int aantal = 1; aantal <= aantal_steden; aantal++){
       //	   kansregel = kansregel + (int) (kans_array[aantal]*100) + ","; 
       //}	
       //fdebug("Stad = "+ huidige_stad+" kans array = ["+ kansregel +"] Kansvalue ="+(int) (kans*100));
       
       //int stad = 1;
       //double kanssom = kans_array[stad];
       //while ( (kanssom < kans) && (stad <= aantal_steden) ) {
       //   stad++;
       //   kanssom += kans_array[stad];
       // }
       // return stad;
*/

}