package cz.pcland.gavisualization;
import cz.pcland.ga.GAConfiguration;
import cz.pcland.ga.GenerationInfo;
import cz.pcland.ga.Population;
import cz.pcland.ga.crossover.CrossoverOpearator;
import cz.pcland.ga.fitness.Evaluator;
import javax.swing.*;
import java.awt.*;
import java.util.*;
import cz.pcland.scripting.*;
import cz.pcland.functions.syntax.*;
import cz.pcland.ga.selection.*;
/**
 *
 * @author petr
 */
public abstract class BaseProblemPanel extends JPanel {
    // protected GAConfiguration GAConfig;
    protected MainApplet3 parent;
    protected SwingWorker progressWorker;
    
    protected Population population = null;
    protected GenerationInfo gi;
    protected Vector infoData = new Vector();
    
    //indicates some population available
    protected boolean populationReady = false;
    //protected Evaluator selectedEvaluator;
    
    protected boolean reset;
    protected BaseVisualization panelVisualization = null;
    //   protected GAVisualization panelVisualizationFitness = null;
    protected JList infoList = null;
    protected int strLen;
    protected int generationsDone;
    
    protected int popType;
    
    protected int iterations = 0;
    protected int iteration = -1;
    
    private long startTime;
    private long endTime;
    
    private String time;
    
    
    protected void setStrLen(int strLen) {
        this.strLen = strLen;
    }
    
    protected int getCurrentIteration(){
        return iteration;
    }
    
    /** Creates a new instance of BaseProblemPanel */
    public BaseProblemPanel(MainApplet3 parent) {
        super();
        this.parent = parent;
    }
    
    /**
     * Method running GA.
     * Thread safe component updates.
     * @return status
     */
    public Object doWork() {
        startTime = System.currentTimeMillis();
        
        for(iteration=0; iteration < iterations; iteration++){
            onDoWorkBegin();
            
            parent.lblStatus.setText("Computing ...");
            generationsDone = 0;
            
            
            if(reset || !this.populationReady){ //button start pressed
                onDoWorkRunBegin();
                System.out.println("reseting infodata");
                this.infoData = new Vector();
                init();
                generationsDone = 1;
            }else { //button steps pressed
                onDoWorkStepsBegin();
            }
            
            
            
            
            try {
                for(int i=1;i<=(GAConfiguration.steps - generationsDone);i++) {
                    
                    if (Thread.interrupted()) {
                        throw new InterruptedException();
                    }
                    
                    
                    gi = population.nextGeneration();
                    
                    
                    
                    if( (i%GAConfiguration.showAfter) == 0 || i==(GAConfiguration.steps - generationsDone)) {
                        infoData.add(0,gi);
                     //   System.out.println("adding gi");
                        if(parent.showProgress) {
                            //System.out.println("parent.showprogress");
                            updateGraph();
                            updateInfoList();
                            onProgress();
                        }
                    }
                    
                    if(population.isEnd()) {
                        if(!((i%GAConfiguration.showAfter) == 0 || i==GAConfiguration.steps - 2)){
                            //System.out.println("adding gi");
                            infoData.add(0,gi);
                        }
                        break; //breaks if best string from current generation is solution
                    }
                    
                }
                
            } catch (InterruptedException e) {
                endTime = System.currentTimeMillis();
                return "Interrupted in " + Operations.getTime(endTime - startTime);
            } catch (SelectionException e) {
                System.out.println(e.getMessage());
                return e.getMessage();
            }
            
            updateInfoList();
            updateGraph();
            
            onDoWorkEnd();
        }
        
        endTime = System.currentTimeMillis();
        time = Operations.getTime(endTime - startTime);
        return "All Done in " + time;
        
    }
    
    public Object init() {
       /*
        * set length of individuals in population
        * and clear current progress saved in infoData vector
        */
        
        onInitBegin();
        GAConfiguration.strLen = strLen;
        
        // ((BaseVisualization)this.panelVisualization).setGAConfig(this.GAConfig);
        infoData.clear();
        
        
        /*
         * Initiate new population
         */
        population = new Population(/*GAConfig*/);
        infoData.add(0, gi = population.initPopulation(popType));
        populationReady = true;
        
        /*
         * Update components
         */
        updateInfoList();
        updateGraph();
        onInitEnd();
        
        return "Population created, computing...";
    }
    
    abstract public void visualizeFitness();
    abstract public void visualizeRank();
    
    /**
     * Called whenever new population is created. That coresponds to button start pressed.
     */
    public abstract void onInitBegin();
    public abstract void onInitEnd();
    
    public abstract void onDoWorkBegin();
    
    
    /**
     * Called whenever GA invoked. That coresponds to button start or step pressed.
     */
    public abstract void onDoWorkRunBegin();
    
    /**
     * Called whenever GA ends.
     */
    public abstract void onDoWorkEnd();
    
    /**
     * Called whenever button steps pressed.
     */
    public abstract void onDoWorkStepsBegin();
    
    
    /**
     * Called whenever button steps pressed.
     */
    public abstract void onProgress();
    
    /**
     * Updates GAInfo list.
     */
    public void updateInfoList() {
        Runnable doSetData = new Runnable() {
            public void run() {
                if(infoList != null) {
                    infoList.setListData(infoData);
               //     infoList.repaint();
                 //   System.out.println("adding to list");
                }else{
                    System.out.println("list = null");
                }
            }
        };
        SwingUtilities.invokeLater(doSetData);
    }
    
    /**
     * Updates progress graph.
     */
    public void updateGraph() {
        Runnable doSetData = new Runnable() {
            public void run() {
                if(panelVisualization != null) {
                    panelVisualization.updateGraph(infoData);
                }
            }
        };
        SwingUtilities.invokeLater(doSetData);
    }
    
   
    
    
    /**
     * Button start or steps pressed. User must call start method to start GA.
     * steps == true => button step pressed.
     */
    abstract public Object run(boolean steps);
    abstract public void finish();
    
    
    
    
    
    /**
     * Invokes doWork method in dispatch thread.
     * After GA is finished, default button states are set.
     */
    public void initWorker(BaseVisualization panelVisualization, JList infoList, int popType, boolean reset, Object data) {
        this.reset = reset;
        this.panelVisualization = panelVisualization;
        this.infoList = infoList;
        this.popType = popType;
        
        GAConfiguration.firnessObj.setDataObject(data);
        
        progressWorker = new SwingWorker() {
            public Object construct() {
                return doWork();
                // return null;
            }
            public void finished() {
                parent.btnStart.setEnabled(true);
                parent.btnSteps.setEnabled(true);
                parent.btnCancel.setEnabled(false);
                finish();
                try {
                    
                    if(Script.isTest) {
                        Script.appendResult(infoData, time);
                    } else {
                        System.out.println("not script, cant save results");
                    }
                    
                    if(Script.prepared()) {
                        System.out.println("running next script part");
                        Script.runNext();
                    }
                    
                }catch(ParseErrorException e) {
                    JOptionPane.showMessageDialog(null, e.getMessage());
                }
                
                
                parent.lblStatus.setText(get().toString());
            }
        };
        
        
    }
    

    public Object start(int iterations) {
        //this.strLen = strLen;
        this.iterations = iterations;
        progressWorker.start();
        return "Done";
    }
    
    public void deletePopulation() {
        this.infoData.clear();
        this.population = null;
        this.populationReady = false;
        updateInfoList();
    }
    
    /**
     * Button cancel pressed. Interrupts current job.
     */
    public void cancel() {
        parent.btnCancel.setEnabled(false);
        this.progressWorker.interrupt();
    }
    
    /**
     * Returns list of available evaluators specific for selected problem.
     */
    public abstract Evaluator[] getEvaluators();
    
    /**
     * Returns list of available crossover operators specific for selected problem.
     */
    public abstract CrossoverOpearator[] getCrossoverOperators();
    
    
}
