/*
 * ScatterPlot.java
 *
 * Copyright 2005-2006                                
 * Performance Research Laboratory, University of Oregon
 */
package edu.uoregon.tau.vis;

import java.util.Observable;

import javax.media.opengl.GL;
import javax.media.opengl.GL2;
import javax.media.opengl.GLAutoDrawable;
import javax.media.opengl.glu.GLU;
import javax.media.opengl.glu.GLUquadric;

/**
 * Draws a scatterplot along 4 axes
 *
 * @author Alan Morris
 *
 * <P>CVS $Id: ScatterPlot.java,v 1.7 2009/08/20 22:09:34 amorris Exp $</P>
 * @author  Alan Morris
 * @version $Revision: 1.7 $
 */
public class ScatterPlot {


    // settings
    private float xSize = 15, ySize = 15, zSize = 15;
    private float sphereSize = 0.4f;
    private int sphereDetail = 8;
    private boolean visible = true;
    private boolean normalized = true;
    
    private float[][] values;

    // rendering details
    private int displayList;
    private boolean dirty = true;

    
    // grr... get rid of these
    private int selectedRow = 5;
    private int selectedCol = 5;

    
    public ScatterPlot() {
    }

    
    public void setSize(float xSize, float ySize, float zSize) {
        this.xSize = xSize;
        this.ySize = ySize;
        this.zSize = zSize;

        
        if (values != null)
            processValues();
        
        this.dirty = true;
    }

//    public void clearValues(){
//    	this.values=null;
//    }
//    float[][] origValues=null;
    /**
     * Sets the values.  The 2nd dimension must be of size 4 (one value for each axis).
     * @param values
     */
    public void setValues(float values[][]) {
        this.values = values;
        
//        if(isTopo)
//        {
//        	origValues=new float[values.length][];
//        	for(int i=0;i<values.length;i++){
//        		origValues[i]=values[i].clone();
//        	}
//        }
//        else
//        	origValues=null;
        processValues();
//        isTopo=false;
        this.dirty = true;
    }

    /**
     * Returns the current sphere size.
     * @return the current sphere size.
     */
    public float getSphereSize() {
        return sphereSize;
    }

    /**
     * Sets the sphere size.
     * @param sphereSize the desired sphere size.
     */
    public void setSphereSize(float sphereSize) {
        this.sphereSize = sphereSize;
        this.dirty = true;
    }

   
    /**
     * Returns the current sphere detail level.
     * @return the current sphere detail level.
     */
    public int getSphereDetail() {
        return sphereDetail;
    }

    /**
     * Sets the sphere detail level.
     * @param sphereDetail level of detail.
     */
    public void setSphereDetail(int sphereDetail) {
        this.sphereDetail = sphereDetail;
        this.dirty = true;
	}

    public float getWidth() {
        return xSize;
    }

    public float getDepth() {
        return ySize;
    }

    public float getHeight() {
        return zSize;
    }

    public String getName() {
        return "ScatterPlot";
    }

    public void cleanUp() {

    }

    

    public void render(VisRenderer visRenderer) {
        GLAutoDrawable glDrawable = visRenderer.getGLAutoDrawable();
        //Vec direction = visRenderer.getViewDirection();



        if (!visible)
            return;

        GL2 gl = glDrawable.getGL().getGL2();

        if (dirty || displayList == 0) {
            displayList = gl.glGenLists(1);
            gl.glNewList(displayList, GL2.GL_COMPILE);
            privateRender(glDrawable);
            gl.glEndList();
            dirty = false;
        }
        gl.glCallList(displayList);
    }

    private void processValues() {

        float[] norms = new float[4];
        norms[0] = xSize;
        norms[1] = ySize;
        norms[2] = zSize;
        norms[3] = 1.0f;

        for (int f = 0; f < 4; f++) {
            float maxValue = Float.MIN_VALUE;
            float minValue = Float.MAX_VALUE;
            for (int i = 0; i < values.length; i++) {
                maxValue = Math.max(maxValue, values[i][f]);
                minValue = Math.min(minValue, values[i][f]);
            }
            
//            if(maxValue!=tmpMax)
//            	System.out.println("Bad Max: "+maxValue+" vs. "+tmpMax);
//            if(minValue!=tmpMin)
//            	System.out.println("Bad Min: "+minValue+" vs. "+tmpMin);

//            if(isTopo&&f==3)
//            {
//            	if(minVis>0)
//            	{
//            		useMinCutoff=true;
//            		float tmp = maxValue-minValue;
//            		mincut=minValue+tmp*(minVis/100.0f);
//            	}
//            	else
//            		mincut=minValue;
//            	if(maxVis<100){
//            		useMaxCutoff=true;
//            		float tmp = maxValue-minValue;
//            		maxcut=minValue+tmp*(maxVis/100.0f);
//            	}
//            	else
//            		maxcut=maxValue;
//            
//
//            }

            for (int i = 0; i < values.length; i++) {
                if (maxValue - minValue == 0) {
                    values[i][f] = 0;
                    continue;
                } 
//                
//                if(useMinCutoff&&values[i][f]<mincut){
//                	if(useMaxCutoff&&values[i][f]<maxcut&&mincut>maxcut){
//                		
//                	}
//                	else
//                	{
//                		values[i][f]=Float.NaN;
//                		continue;
//                	}
//                }
//                else if(useMaxCutoff&&values[i][f]>maxcut){
//                	if(useMinCutoff&&values[i][f]>mincut&&mincut>maxcut){
//                		
//                	}
//                	else
//                	{
//                		values[i][f]=Float.NaN;
//                		continue;
//                	}
//                }
//                if(isTopo&&i>=0&&i<4){
//                	continue;
//                }
//                else
                {
                    if (normalized) {
                        values[i][f] = (values[i][f] - minValue) / (maxValue - minValue) * norms[f];
                    } else {
                        values[i][f] = values[i][f] / maxValue * norms[f];
                    }
                }
            }

        }
    }


    
//    private boolean showCoord(int dex){
//    	boolean use = checkCoord(dex);
//    	if(use&&origValues!=null){
//    		statCount++;
//    		statAcc+=origValues[dex][3];
//    		statMin=Math.min(origValues[dex][3], statMin);
//    		statMax=Math.max(origValues[dex][3], statMax);
//    	}
//    	return use;
//    }
    
//    private boolean checkCoord(int dex){
//    	if(Float.compare(values[dex][3],Float.NaN)==0)
//    		return false;
//    	if(topoVis==null||origValues==null)
//    		return true;
//    	
//    	//System.out.println(topoVis[0]+" "+topoVis[1]+" "+topoVis[2]+" vs "+origValues[dex][0]+" "+origValues[dex][1]+" "+origValues[dex][2]);
//    	
//    	for(int i=0;i<3;i++){
//    		if(topoVis[i]!=-1&&topoVis[i]!=origValues[dex][i])
//    			return false;
//    	}
//    	
//    	return true;
//    }
    
    private void privateRender(GLAutoDrawable glDrawable) {
        if (values == null)
            return;
        
        GL2 gl = glDrawable.getGL().getGL2();
        GLU glu = new GLU();

        gl.glShadeModel(GL2.GL_SMOOTH);

        // Set to red, in case there is no colorScale
        gl.glColor3f(1.0f, 0, 0);

        if (sphereSize < 0.1f||sphereDetail<3) {
            gl.glDisable(GL2.GL_LIGHTING);
            float actualSize=2.5f;
            if(sphereSize>=0.1f){
            	actualSize=sphereSize*30;
            }
            gl.glPointSize(actualSize);
            gl.glBegin(GL.GL_POINTS);
            for (int i = 0; i < values.length; i++) {
            	//if(showCoord(i)){
                
                gl.glVertex3f(values[i][0], values[i][1], values[i][2]);
            	//}

            }
            gl.glEnd();
        } else {
            gl.glEnable(GL2.GL_LIGHTING);
            gl.glEnable(GL.GL_DEPTH_TEST);
            gl.glFrontFace(GL.GL_CCW);
            GLUquadric qobj = glu.gluNewQuadric();
            gl.glEnable(GL.GL_CULL_FACE);
            glu.gluQuadricDrawStyle(qobj, GLU.GLU_FILL);
            glu.gluQuadricOrientation(qobj, GLU.GLU_OUTSIDE);
            glu.gluQuadricNormals(qobj, GLU.GLU_SMOOTH);

            for (int i = 0; i < values.length; i++) {
            	//if(showCoord(i)){
                gl.glPushMatrix();
                gl.glTranslatef(values[i][0], values[i][1], values[i][2]);

                glu.gluSphere(qobj, sphereSize, sphereDetail, sphereDetail);
                gl.glPopMatrix();
            	//}
            }
        }

    }

    public void update(Observable o, Object arg) {

    }

    public int getSelectedRow() {
        return selectedRow;
    }

    public void setSelectedRow(int selectedRow) {
        this.selectedRow = selectedRow;
    }

    public int getSelectedCol() {
        return selectedCol;
    }

    public void setSelectedCol(int selectedCol) {
        this.selectedCol = selectedCol;
    }

    
    /**
     * Returns whether or not this <tt>ScatterPlot</tt> is normalizing values.
     * @return whether or not this <tt>ScatterPlot</tt> is normalizing values.
     */
    public boolean getNormalized() {
        return normalized;
    }
    /**
     * Sets whether or not this <tt>ScatterPlot</tt> should normalize the values along its axes.
     * @param normalized <tt>true</tt> to normalize; <tt>false</tt> to leave values alone.
     */
    public void setNormalized(boolean normalized) {
        this.normalized = normalized;
    }

    /**
     * Returns whether or not this <tt>ScatterPlot</tt> instance is visible.
     * @return whether or not this <tt>ScatterPlot</tt> instance is visible.
     */
    public boolean getVisible() {
        return visible;
    }

    /**
     * Makes this <tt>ScatterPlot</tt> instance visible or invisible.
     * @param visible <tt>true</tt> to make the <tt>ScatterPlot</tt> visible; <tt>false</tt> to make it invisible.
     */
    public void setVisible(boolean visible) {
        this.visible = visible;
    }

    public void resetCanvas() {
        dirty = true;
        displayList = 0;

    }
}
