package sokobanevo;

import java.awt.Font;
import java.io.IOException;
import java.util.ArrayList;
import org.newdawn.slick.AppGameContainer;
import org.newdawn.slick.BasicGame;
import org.newdawn.slick.Color;
import org.newdawn.slick.GameContainer;
import org.newdawn.slick.Graphics;
import org.newdawn.slick.SlickException;
import org.newdawn.slick.TrueTypeFont;
import sokobanevo.evolution.EvolutionSokoSolver;
import sokobanevo.levels.LevelDraw;

/**
 * Main class
 * 
 * Contains Slick2D rendering and calls static functions of class EvolutionSokoSolver,
 * which is class dedicated to evolutional sokoban solving.
 * 
 * @author Payne
 */
public class SokobanEvo extends BasicGame{
  
    private LevelDraw levelDraw;
    private ArrayList<char[]> currentMap = null;
    private ArrayList<char[]> baseMap = null;
    private ArrayList<char[]> deadlockMap = null;
    private TrueTypeFont trueTypeFont = null;
    private ArrayList<char[]> population = null;
    private int[] generationFitness = null;
    private int bestFitness;
    private double mediumFitness;
    private int generationNum = 1;
    
    public SokobanEvo()
    {
        super("Sokoban Evo");
    }
  
    /**
    * Function, called when Slick2D initializes itself. 
    * 
    * Used for preparations and assets loading.
    * Id in row: baseMap = levelDraw.readMapFromFileById(ID) represents ID of map to be loaded from given text file (LevelDraw.mapFilePath).
    */
    @Override
    public void init(GameContainer gc) throws SlickException
    {
        levelDraw = new LevelDraw();
        
        Font font = new Font("Verdana", Font.BOLD, 15);
        trueTypeFont = new TrueTypeFont(font, true);
        
        baseMap = null;
        try{       
            baseMap = levelDraw.readMapFromFileById(6);
        }catch(IOException ex){
            System.out.println("Nenasiel sa subor");
            return;
        }
        if(baseMap.isEmpty()){
            System.out.println("Nenasiel sa subor alebo mapa so zadanym ID neexistuje!");
            return;
        }else{
            currentMap = EvolutionSokoSolver.cloneMap(baseMap);
            deadlockMap = EvolutionSokoSolver.cloneMap(baseMap);
            EvolutionSokoSolver.markAllVisitablePositionsOnMap(deadlockMap);
        }
        
        population = EvolutionSokoSolver.generateBrandNewGeneration(baseMap);
    }

    /**
    * Function for Slick2D, gets called every time before rendering. Time between calls depends on processing speed.
    */
    @Override
    public void update(GameContainer gc, int delta) throws SlickException
    {
        generationFitness = EvolutionSokoSolver.computeFitnessOfGeneration(population, baseMap, deadlockMap);
        bestFitness = Integer.MAX_VALUE;
        int bestFitnessIndex = 0;
        int worseFitness = 0;
        
        int i;
        for(i = 0; i < generationFitness.length; i++){
            if(generationFitness[i] < bestFitness){
                bestFitnessIndex = i;
                bestFitness = generationFitness[i];
            }
            if(generationFitness[i] > worseFitness){
                worseFitness = generationFitness[i];
            }
        }
        
        currentMap = EvolutionSokoSolver.cloneMap(baseMap);
        EvolutionSokoSolver.MoveInMap(currentMap, population.get(bestFitnessIndex));
        //EvolutionSokoSolver.MoveInMap(currentMap, new char[]{2,0,1,0,2,2,2,0,0,3,1,0,0,1,0,3,0,2,2,0,0,0,1,3,0,2,1,0,1,3,2,3,1,1,2,1,1,3});
        
        /*System.out.print("best one:");
        for(int l = 0; l < population.get(bestFitnessIndex).length; l++){
            System.out.print((int)population.get(bestFitnessIndex)[l]);
        }
        System.out.println();*/
        
        mediumFitness = 0;
        for(i = 0; i < generationFitness.length; i++){
            generationFitness[i] = worseFitness - generationFitness[i];
            mediumFitness += generationFitness[i];
        }
        mediumFitness = mediumFitness / generationFitness.length;
        
        population = EvolutionSokoSolver.createNextGeneration(population, generationFitness,baseMap);
        generationNum++;
        
        /*try {
            Thread.sleep(2000);
        } catch (InterruptedException ex) {
            //
        }*/
    }

    /**
    * Function for Slick2D, gets called every time after update function.
    * 
    * Renders everything on screen. Eg. maps and captions.
    */
    @Override
    public void render(GameContainer gc, Graphics g) throws SlickException
    {
       g.setBackground(Color.white);
       levelDraw.drawLvlAt(10,30,baseMap);
       levelDraw.drawLvlAt(300,30,currentMap);
       levelDraw.drawLvlAt(590,30,deadlockMap);
       trueTypeFont.drawString(10, 10, "Zakladna mapa", Color.black);
       trueTypeFont.drawString(300, 10, "Najlepsie riesenie", Color.black);
       trueTypeFont.drawString(590, 10, "Miesta kde nieje deadlock", Color.black);
       trueTypeFont.drawString(10, 600, "Cislo generacie:" + generationNum, Color.black);
       trueTypeFont.drawString(10, 620, "Best fitness:" + bestFitness, Color.red);
       trueTypeFont.drawString(10, 640, "Medium fitness:" + mediumFitness, Color.blue);
    }

    /**
    * Main function.
    * 
    * Creates new Slick2D instance.
    */
    public static void main(String[] args) throws SlickException
    {
       AppGameContainer app = new AppGameContainer(new SokobanEvo());

       app.setShowFPS(false);
       app.setVSync(true);
       app.setDisplayMode(1024, 768, false);
       app.start();
       
    }
  
}