/*
 * GraphPanel.java
 *
 * Created on January 11, 2006, 3:02 AM
 *
 * To change this template, choose Tools | Options and locate the template under
 * the Source Creation and Management node. Right-click the template and choose
 * Open. You can then make changes to the template in the Source Editor.
 */
package cz.pcland.functions;

import cz.pcland.ga.BinaryIndividual;
import cz.pcland.ga.fitness.BaseFunctionFitness;
import javax.swing.*;
import java.awt.*;
import java.awt.event.*;
import cz.pcland.functions.lexan.*;
import cz.pcland.functions.nodes.*;
import cz.pcland.functions.syntax.*;
import java.util.*;
import java.awt.geom.*;
import java.awt.font.*;
import cz.pcland.ga.*;

/**
 *
 * @author petr
 */
public class GraphPanel extends JPanel{
    int w, h;
    int borders;
    FunctionInstance instance = null;
    Vector data = new Vector();
    double maxFunctionValue;
    double minFunctionValue;
    BaseFunctionFitness fi = null;
    Population currentPopulation = null;
    
    final static BasicStroke stroke = new BasicStroke(1.2f);
    
    /** Creates a new instance of GraphPanel */
    public GraphPanel() {
        h = this.getHeight();
        w = this.getWidth();
    }
    
     /**
     * Sets population to visualize
     * @param p population
     */
    public void setPopulation(Population p){
        this.currentPopulation = p;
    }
    
    /**
     * Sets function instance to be solved
     * @param instance Instance of function
     */
    protected void setInstance(FunctionInstance instance){
        this.instance = instance;
    }
    
    /**
     * Draws line from [x1,y1] to [x2,y2]
     * @param x1 X1 coordinate
     * @param y2 Y2 coordinate
     * @param x2 X2 coordinate
     * @param y1 Y2 coordinate
     * @param g2 Graphics2D object reference
     */
    protected void drawLine(int x1, int y1, int x2, int y2, Graphics2D g2){
        g2.setStroke(stroke);
        g2.setPaint(Color.black);
        g2.draw(new Line2D.Double(x1,y1,x2,y2));
    }
    
    
    /**
     * prints given boolean array as string
     * @param bits boolean array
     */
    public String bitsToStr(boolean[] bits) {
        String s = "";
        for(int ii=0;ii<bits.length;ii++){
            if(bits[ii])s+="1";else s+="0";
        }
        return s;
    }
    
    /**
     * Draws function value represented by given bits
     * @param bits bits of the individual
     * @param g2 Graphics2D object reference
     */
    protected void drawPoint(boolean[] bits, Graphics2D g2) {
        this.fi = (BaseFunctionFitness)GAConfiguration.firnessObj;
        
        double x = fi.decode(new BinaryIndividual(bits));
        x = fi.convertValueToInterval(x);
        double y = fi.getFunctionValue(x);
        
        x = Math.abs(x-instance.from); //makes x possitive
        y = getYCoordinate(y);
        if(y<0)y=0;
        
        x = (((double)x/(double)Math.abs(instance.from-instance.to))*(double)this.getWidth());
        
        g2.setColor(Color.blue);
        Ellipse2D.Double point = new Ellipse2D.Double(x,y,3,3);
        g2.fill(point);
    }
    
    
    /**
     * Loads data before drawing itself
     */
    protected void loadData(){
        if(instance == null) return;
        data.clear();
        
        /*
         * finds first function value
         */
        for(int ii=0;ii<this.getWidth();++ii){
            try{
                double x = instance.from + ((double)ii/(double)this.getWidth())*(double)Math.abs(instance.from - instance.to);
                double value = instance.node.evaluate(x,instance.varName);
                if(value != Double.NEGATIVE_INFINITY && value != Double.POSITIVE_INFINITY && value != Double.NaN){
                    maxFunctionValue = value;
                    break;
                }
            }catch(ArithmeticException e){
                
            }
        }
        minFunctionValue = maxFunctionValue;
        
        
        for(int ii=0;ii<this.getWidth();++ii){
            try {
                double x = instance.from +  ((double)ii/(double)this.getWidth())*(double)Math.abs(instance.from - instance.to);
                double value =instance.node.evaluate(x,instance.varName);
                if(value != Double.NEGATIVE_INFINITY && value != Double.POSITIVE_INFINITY && value != Double.NaN){
                    
                    //System.out.println("value="+value);
                    if(maxFunctionValue < value) maxFunctionValue=value;
                    if(minFunctionValue > value) {
                        minFunctionValue=value;
                    }
                }else {
                    //System.out.println("error value="+value);
                }
                this.data.add(new Double(value));
            }catch(ArithmeticException e){
                
            }
        }

    }
    
    /**
     * Returns Y coordinate of function value
     * @param value Function value
     */
    protected int getYCoordinate(double value) {
        h = this.getHeight();
        return (int)((double)h - (double)(( Math.abs(value-this.minFunctionValue) / ((double)Math.abs(this.minFunctionValue-this.maxFunctionValue)))*(double)h));
    }
    
    
    /**
     * Returns X coordinate of given point
     */
    protected int getXCoordinate(int value){
        w = this.getWidth();
        return (int)(((double)value/(double)Math.abs(instance.from - instance.to))*(double)w);
    }
    
    
    /**
     * Overriden paint component function
     */
    protected void paintComponent(Graphics g) {
        super.paintComponent(g);
        Graphics2D g2 = (Graphics2D)g;
        g2.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
        g2.setRenderingHint(RenderingHints.KEY_RENDERING, RenderingHints.VALUE_RENDER_QUALITY);
        if(instance == null) return;
        this.loadData();
        
        
        for(int ii=0;ii<data.size()-1;++ii){
            
            double value = ((Double)data.get(ii)).doubleValue();
            if(value != Double.NEGATIVE_INFINITY && value != Double.POSITIVE_INFINITY && value != Double.NaN){    
                this.drawLine(ii,getYCoordinate(value),ii+1,getYCoordinate( ((Double)data.get(ii+1)).doubleValue()  ), g2);
            }
        }
        if(instance == null)return;
        if(this.currentPopulation == null){
            g2.setColor(Color.orange);
            Font f = new Font("Courier", 2, 10);
            g2.drawString( "No population available ...", 20, 18);
            return;
        }
        
        for(int ii=0;ii<this.currentPopulation.getPopulation().length;ii++){
            boolean[] bits = ((BinaryIndividual)this.currentPopulation.getPopulation()[ii]).getString();
            //System.out.println("draw point: " + bits[0]);
            this.drawPoint(bits,g2);
        }
        
    }
    
    
}
