package prefuse.demos;

import java.awt.BorderLayout;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;

import javax.swing.BorderFactory;
import javax.swing.Box;
import javax.swing.BoxLayout;
import javax.swing.JComboBox;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JToolBar;

import prefuse.Constants;
import prefuse.Display;
import prefuse.Visualization;
import prefuse.action.ActionList;
import prefuse.action.RepaintAction;
import prefuse.action.assignment.ColorAction;
import prefuse.action.assignment.DataShapeAction;
import prefuse.action.layout.AxisLayout;
import prefuse.controls.ToolTipControl;
import prefuse.data.Table;
import prefuse.data.io.DelimitedTextTableReader;
import prefuse.render.DefaultRendererFactory;
import prefuse.render.ShapeRenderer;
import prefuse.util.ColorLib;
import prefuse.visual.VisualItem;
import prefuse.visual.expression.VisiblePredicate;

/**
 * A simple scatter plot visualization that allows visual encodings to
 * be changed at runtime.
 * 
 * @author <a href="http://jheer.org">jeffrey heer</a>
 */
public class ScatterPlot extends Display {

    private static final String group = "data";
    
    private ShapeRenderer m_shapeR = new ShapeRenderer(2);
    
    public ScatterPlot(Table t, String xfield, String yfield) {
        this(t, xfield, yfield, null);
    }
    
    public ScatterPlot(Table t, String xfield, String yfield, String sfield) {
        super(new Visualization());
        
        // --------------------------------------------------------------------
        // STEP 1: setup the visualized data
        
        m_vis.addTable(group, t);
        
        DefaultRendererFactory rf = new DefaultRendererFactory(m_shapeR);
        m_vis.setRendererFactory(rf);
        
        // --------------------------------------------------------------------
        // STEP 2: create actions to process the visual data
        
        // set up the actions
        AxisLayout x_axis = new AxisLayout(group, xfield, 
                Constants.X_AXIS, VisiblePredicate.TRUE);
        m_vis.putAction("x", x_axis);
        
        AxisLayout y_axis = new AxisLayout(group, yfield, 
                Constants.Y_AXIS, VisiblePredicate.TRUE);
        m_vis.putAction("y", y_axis);

        ColorAction color = new ColorAction(group, 
                VisualItem.STROKECOLOR, ColorLib.rgb(100,100,255));
        m_vis.putAction("color", color);
        
        DataShapeAction shape = new DataShapeAction(group, sfield);
        m_vis.putAction("shape", shape);
        
        ActionList draw = new ActionList();
        draw.add(x_axis);
        draw.add(y_axis);
        if ( sfield != null )
            draw.add(shape);
        draw.add(color);
        draw.add(new RepaintAction());
        m_vis.putAction("draw", draw);
        
        // --------------------------------------------------------------------
        // STEP 3: set up a display and ui components to show the visualization

        setBorder(BorderFactory.createEmptyBorder(10,10,10,10));
        setSize(700,450);
        setHighQuality(true);
        
        ToolTipControl ttc = new ToolTipControl(new String[] {xfield,yfield});
        addControlListener(ttc);
        
        
        // --------------------------------------------------------------------        
        // STEP 4: launching the visualization
                
        m_vis.run("draw");

    }
    
    public int getPointSize() {
        return m_shapeR.getBaseSize();
    }
    
    public void setPointSize(int size) {
        m_shapeR.setBaseSize(size);
        repaint();
    }
    
    // ------------------------------------------------------------------------
    
    public static void main(String[] argv) {
        String data = "/fisher.iris.txt";
        String xfield = "SepalLength";
        String yfield = "PetalLength";
        String sfield = "Species";
        if ( argv.length >= 3 ) {
            data = argv[0];
            xfield = argv[1];
            yfield = argv[2];
            sfield = ( argv.length > 3 ? argv[3] : null );
        }
        
        final ScatterPlot sp = demo(data, xfield, yfield, sfield);
        JToolBar toolbar = getEncodingToolbar(sp, xfield, yfield, sfield);
        
        
        
        JFrame frame = new JFrame("p r e f u s e  |  s c a t t e r");
        frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        frame.getContentPane().add(toolbar, BorderLayout.NORTH);
        frame.getContentPane().add(sp, BorderLayout.CENTER);
        frame.pack();
        frame.setVisible(true);
    }
    
    public static ScatterPlot demo(String data, String xfield, String yfield) {
        return demo(data, xfield, yfield, null);
    }
    
    public static ScatterPlot demo(String data, String xfield,
                                   String yfield, String sfield)
    {
        Table table = null;
        try {
            table = new DelimitedTextTableReader().readTable(data);
        } catch ( Exception e ) {
            e.printStackTrace();
            return null;
        }
        ScatterPlot scatter = new ScatterPlot(table, xfield, yfield, sfield);
        scatter.setPointSize(10);
        return scatter;
    }
    
    private static JToolBar getEncodingToolbar(final ScatterPlot sp,
            final String xfield, final String yfield, final String sfield)
    {
        int spacing = 10;
        
        // create list of column names
        Table t = (Table)sp.getVisualization().getSourceData(group);
        String[] colnames = new String[t.getColumnCount()];
        for ( int i=0; i<colnames.length; ++i )
            colnames[i] = t.getColumnName(i);
        
        // create toolbar that allows visual mappings to be changed
        JToolBar toolbar = new JToolBar();
        toolbar.setLayout(new BoxLayout(toolbar, BoxLayout.X_AXIS));
        toolbar.add(Box.createHorizontalStrut(spacing));
        
        final JComboBox xcb = new JComboBox(colnames);
        xcb.setSelectedItem(xfield);
        xcb.addActionListener(new ActionListener() {
            public void actionPerformed(ActionEvent e) {
                Visualization vis = sp.getVisualization();
                AxisLayout xaxis = (AxisLayout)vis.getAction("x");
                xaxis.setDataField((String)xcb.getSelectedItem());
                vis.run("draw");
            }
        });
        toolbar.add(new JLabel("X: "));
        toolbar.add(xcb);
        toolbar.add(Box.createHorizontalStrut(2*spacing));
        
        final JComboBox ycb = new JComboBox(colnames);
        ycb.setSelectedItem(yfield);
        ycb.addActionListener(new ActionListener() {
            public void actionPerformed(ActionEvent e) {
                Visualization vis = sp.getVisualization();
                AxisLayout yaxis = (AxisLayout)vis.getAction("y");
                yaxis.setDataField((String)ycb.getSelectedItem());
                vis.run("draw");
            }
        });
        toolbar.add(new JLabel("Y: "));
        toolbar.add(ycb);
        toolbar.add(Box.createHorizontalStrut(2*spacing));
        
        final JComboBox scb = new JComboBox(colnames);
        scb.setSelectedItem(sfield);
        scb.addActionListener(new ActionListener() {
            public void actionPerformed(ActionEvent e) {
                Visualization vis = sp.getVisualization();
                DataShapeAction s = (DataShapeAction)vis.getAction("shape");
                s.setDataField((String)scb.getSelectedItem());
                vis.run("draw");
            }
        });
        toolbar.add(new JLabel("Shape: "));
        toolbar.add(scb);
        toolbar.add(Box.createHorizontalStrut(spacing));
        toolbar.add(Box.createHorizontalGlue());
        
        return toolbar;
    }
    
} // end of class ScatterPlot
