/*
 * Vision.java
 *
 * Created on May 12, 2004, 3:19 PM
 */

package PER;

import PER.rover.*;
import PER.rover.control.*;
import java.awt.image.MemoryImageSource;
import java.awt.Image;
import javax.swing.ImageIcon;
import java.io.*;
import java.awt.*;

/**
 * This interface demonstrates the vision capabilities of the robot.
 * Start by connecting to a robot.  The code version on the robot should be at
 * least 2.0.1.  Motion detection is new in this version and tracking is improved.
 * <p>
 * Once you have connected, click on the "Take Picture" button.  You can change
 * the pan and tilt by using the grid at the bottom of the window.  Pictures will
 * be taken at the current pan and tilt.  The current pan and tilt is shown by
 * a red 'x'.  This position is updated during tracking.
 * <p>
 * By clicking on the left image, good track color parameters for the object will be
 * put into the text fields below the right image.  You can click on the image multiple times to relax
 * the track color parameters.  On the right image, you will see which pixels will
 * be tracked using the parameters you selected.  Different colors are given to
 * the object you selected and pixels outside of your object falling within the
 * range of the track color parameters.  By clicking on the right image, the
 * head will be recentered on the point you clicked on and a new picture will
 * be taken.
 * <p>
 * When you start tracking, the bounding box is shown and a blue box is shown at
 * the middle mass of the pixels.  If more than 2/3 of the pixels in the box are
 * tracked, the box is green.  If more than 1/3 are tracked, the box is yellow
 * and if less than one third are tracked, the box is red.
 * <p>
 * If the "Line Follow" check box is selected while tracking is enabled, the
 * rover is driven so that it will follow the object being tracked.  The rover
 * will drive toward the tracked object as long as at least 30 pixels are being
 * tracked and the object is below -5 degrees.  Otherwise, the rover will stop.
 * It's a good idea to move the head to about -30 degrees and uncheck the
 * "Track Move Tilt" box so that the rover will look ahead farther.
 * <p>
 * When you click on the "Show Motion" button, you will see a graphical display
 * of the difference of the current frame from the previous one.  When there is
 * no motion, everything is black.  Motion is shown in red.
 *
 * @author  Eric Porter
 */
public class Vision extends javax.swing.JFrame {
    
    private Rover rover = null;
    private TrackFinder trackFinder = null;
    private MemoryImageSource source = null, previewSource = null;
    private static final int width = VisionUtil.TRACK_WIDTH;
    static final int height = VisionUtil.TRACK_HEIGHT;
    private int rgb[], yuv[], previewPix[];
    private int pan=0, tilt=0;
    
    private ShowTrackThread trackThread = null;
    private ShowMotionThread motionThread = null;
    
    private static final int HEAD_STARTX = 10;
    private static final int HEAD_STARTY = 10;
    private static final int HEAD_WIDTH = 360;
    private static final int HEAD_HEIGHT = 140;
    
    /** Creates new form Vision */
    public Vision() {
        initComponents();
        rover = new Rover();
        setTitle("PER Vision Window");
        ipTF.setText(Rover.getDefaultIP());
        trackFinder = new TrackFinder(20, 10, 10);
        
        rgb = new int [width*height];
        yuv = new int [width*height];
        previewPix = new int [width*height];
        
        //set up the labels for showing images.  I'm setting them to be animated
        //so that I can just change the source array and update the image easily.
        imageLabel.setSize(width, height);
        previewLabel.setSize(width, height);
        source = new MemoryImageSource(width, height, rgb, 0, width);
        previewSource = new MemoryImageSource(width, height, previewPix, 0, width);
        source.setAnimated(true);
        previewSource.setAnimated(true);
        imageLabel.setIcon(new ImageIcon(createImage(source)));
        previewLabel.setIcon(new ImageIcon(createImage(previewSource)));
    }
    
    /** This method is called from within the constructor to
     * initialize the form.
     * WARNING: Do NOT modify this code. The content of this method is
     * always regenerated by the Form Editor.
     */
    private void initComponents() {//GEN-BEGIN:initComponents
        java.awt.GridBagConstraints gridBagConstraints;

        connectPanel = new javax.swing.JPanel();
        ipLabel = new javax.swing.JLabel();
        ipTF = new javax.swing.JTextField();
        connectButton = new javax.swing.JButton();
        statusLabel = new javax.swing.JLabel();
        trackPanel = new javax.swing.JPanel();
        imageLabel = new javax.swing.JLabel();
        colorPanel = new javax.swing.JPanel();
        iyLabel = new javax.swing.JLabel();
        iuLabel = new javax.swing.JLabel();
        ivLabel = new javax.swing.JLabel();
        igLabel = new javax.swing.JLabel();
        irLabel = new javax.swing.JLabel();
        ibLabel = new javax.swing.JLabel();
        previewLabel = new javax.swing.JLabel();
        trackParamsPanel = new javax.swing.JPanel();
        minyLabel = new javax.swing.JLabel();
        minuLabel = new javax.swing.JLabel();
        minvLabel = new javax.swing.JLabel();
        minyTF = new javax.swing.JTextField();
        minuTF = new javax.swing.JTextField();
        minvTF = new javax.swing.JTextField();
        maxyLabel = new javax.swing.JLabel();
        maxuLabel = new javax.swing.JLabel();
        maxvLabel = new javax.swing.JLabel();
        maxyTF = new javax.swing.JTextField();
        maxuTF = new javax.swing.JTextField();
        maxvTF = new javax.swing.JTextField();
        controlPanel = new javax.swing.JPanel();
        grabButton = new javax.swing.JButton();
        trackMovePanCheckBox = new javax.swing.JCheckBox();
        trackMoveTiltCheckBox = new javax.swing.JCheckBox();
        startTrackButton = new javax.swing.JButton();
        stopStreamingButton = new javax.swing.JButton();
        detectMotionButton = new javax.swing.JButton();
        lineFollowCheckBox = new javax.swing.JCheckBox();
        headPanelA = new javax.swing.JPanel();
        headPanel = new javax.swing.JPanel();
        upLabel = new javax.swing.JLabel();
        tiltLabel = new javax.swing.JLabel();
        downLabel = new javax.swing.JLabel();
        leftLabel = new javax.swing.JLabel();
        panLabel = new javax.swing.JLabel();
        rightLabel = new javax.swing.JLabel();

        getContentPane().setLayout(new java.awt.GridBagLayout());

        addWindowListener(new java.awt.event.WindowAdapter() {
            public void windowClosing(java.awt.event.WindowEvent evt) {
                exitForm(evt);
            }
        });

        connectPanel.setLayout(new java.awt.GridBagLayout());

        ipLabel.setText("IP or host name: ");
        connectPanel.add(ipLabel, new java.awt.GridBagConstraints());

        ipTF.setPreferredSize(new java.awt.Dimension(200, 19));
        ipTF.addActionListener(new java.awt.event.ActionListener() {
            public void actionPerformed(java.awt.event.ActionEvent evt) {
                ipTFActionPerformed(evt);
            }
        });

        connectPanel.add(ipTF, new java.awt.GridBagConstraints());

        connectButton.setText("Connect");
        connectButton.addMouseListener(new java.awt.event.MouseAdapter() {
            public void mouseClicked(java.awt.event.MouseEvent evt) {
                connectButtonMouseClicked(evt);
            }
        });

        connectPanel.add(connectButton, new java.awt.GridBagConstraints());

        statusLabel.setPreferredSize(new java.awt.Dimension(380, 15));
        gridBagConstraints = new java.awt.GridBagConstraints();
        gridBagConstraints.gridx = 0;
        gridBagConstraints.gridy = 1;
        gridBagConstraints.gridwidth = 3;
        gridBagConstraints.anchor = java.awt.GridBagConstraints.WEST;
        connectPanel.add(statusLabel, gridBagConstraints);

        gridBagConstraints = new java.awt.GridBagConstraints();
        gridBagConstraints.insets = new java.awt.Insets(5, 5, 5, 5);
        getContentPane().add(connectPanel, gridBagConstraints);

        trackPanel.setLayout(new java.awt.GridBagLayout());

        imageLabel.setMaximumSize(new java.awt.Dimension(176, 144));
        imageLabel.setMinimumSize(new java.awt.Dimension(176, 144));
        imageLabel.setPreferredSize(new java.awt.Dimension(176, 144));
        imageLabel.addMouseListener(new java.awt.event.MouseAdapter() {
            public void mouseClicked(java.awt.event.MouseEvent evt) {
                imageLabelMouseClicked(evt);
            }
            public void mouseExited(java.awt.event.MouseEvent evt) {
                imageLabelMouseExited(evt);
            }
        });
        imageLabel.addMouseMotionListener(new java.awt.event.MouseMotionAdapter() {
            public void mouseMoved(java.awt.event.MouseEvent evt) {
                imageLabelMouseMoved(evt);
            }
        });

        gridBagConstraints = new java.awt.GridBagConstraints();
        gridBagConstraints.insets = new java.awt.Insets(0, 0, 0, 10);
        trackPanel.add(imageLabel, gridBagConstraints);

        colorPanel.setLayout(new java.awt.GridBagLayout());

        colorPanel.setPreferredSize(new java.awt.Dimension(176, 32));
        iyLabel.setText("Y:");
        iyLabel.setPreferredSize(new java.awt.Dimension(58, 16));
        gridBagConstraints = new java.awt.GridBagConstraints();
        gridBagConstraints.anchor = java.awt.GridBagConstraints.WEST;
        colorPanel.add(iyLabel, gridBagConstraints);

        iuLabel.setText("U:");
        iuLabel.setPreferredSize(new java.awt.Dimension(58, 16));
        colorPanel.add(iuLabel, new java.awt.GridBagConstraints());

        ivLabel.setText("V:");
        ivLabel.setPreferredSize(new java.awt.Dimension(58, 16));
        gridBagConstraints = new java.awt.GridBagConstraints();
        gridBagConstraints.anchor = java.awt.GridBagConstraints.EAST;
        colorPanel.add(ivLabel, gridBagConstraints);

        igLabel.setText("G:");
        igLabel.setPreferredSize(new java.awt.Dimension(40, 16));
        gridBagConstraints = new java.awt.GridBagConstraints();
        gridBagConstraints.gridx = 0;
        gridBagConstraints.gridy = 1;
        gridBagConstraints.anchor = java.awt.GridBagConstraints.WEST;
        colorPanel.add(igLabel, gridBagConstraints);

        irLabel.setText("R:");
        irLabel.setPreferredSize(new java.awt.Dimension(58, 16));
        gridBagConstraints = new java.awt.GridBagConstraints();
        gridBagConstraints.gridx = 1;
        gridBagConstraints.gridy = 1;
        colorPanel.add(irLabel, gridBagConstraints);

        ibLabel.setText("B:");
        ibLabel.setPreferredSize(new java.awt.Dimension(58, 16));
        gridBagConstraints = new java.awt.GridBagConstraints();
        gridBagConstraints.gridx = 2;
        gridBagConstraints.gridy = 1;
        gridBagConstraints.anchor = java.awt.GridBagConstraints.EAST;
        colorPanel.add(ibLabel, gridBagConstraints);

        gridBagConstraints = new java.awt.GridBagConstraints();
        gridBagConstraints.gridx = 0;
        gridBagConstraints.gridy = 1;
        gridBagConstraints.insets = new java.awt.Insets(0, 0, 0, 10);
        gridBagConstraints.anchor = java.awt.GridBagConstraints.EAST;
        trackPanel.add(colorPanel, gridBagConstraints);

        previewLabel.setMaximumSize(new java.awt.Dimension(176, 144));
        previewLabel.setMinimumSize(new java.awt.Dimension(176, 144));
        previewLabel.setPreferredSize(new java.awt.Dimension(176, 144));
        previewLabel.addMouseListener(new java.awt.event.MouseAdapter() {
            public void mouseClicked(java.awt.event.MouseEvent evt) {
                previewLabelMouseClicked(evt);
            }
        });

        gridBagConstraints = new java.awt.GridBagConstraints();
        gridBagConstraints.insets = new java.awt.Insets(0, 10, 0, 0);
        trackPanel.add(previewLabel, gridBagConstraints);

        trackParamsPanel.setLayout(new java.awt.GridBagLayout());

        minyLabel.setText("Min Y: ");
        trackParamsPanel.add(minyLabel, new java.awt.GridBagConstraints());

        minuLabel.setText(" Min U: ");
        gridBagConstraints = new java.awt.GridBagConstraints();
        gridBagConstraints.gridx = 2;
        gridBagConstraints.gridy = 0;
        trackParamsPanel.add(minuLabel, gridBagConstraints);

        minvLabel.setText(" Min V: ");
        gridBagConstraints = new java.awt.GridBagConstraints();
        gridBagConstraints.gridx = 4;
        gridBagConstraints.gridy = 0;
        trackParamsPanel.add(minvLabel, gridBagConstraints);

        minyTF.setPreferredSize(new java.awt.Dimension(40, 19));
        gridBagConstraints = new java.awt.GridBagConstraints();
        gridBagConstraints.gridx = 1;
        gridBagConstraints.gridy = 0;
        trackParamsPanel.add(minyTF, gridBagConstraints);

        minuTF.setPreferredSize(new java.awt.Dimension(40, 19));
        gridBagConstraints = new java.awt.GridBagConstraints();
        gridBagConstraints.gridx = 3;
        gridBagConstraints.gridy = 0;
        trackParamsPanel.add(minuTF, gridBagConstraints);

        minvTF.setPreferredSize(new java.awt.Dimension(40, 19));
        gridBagConstraints = new java.awt.GridBagConstraints();
        gridBagConstraints.gridx = 5;
        gridBagConstraints.gridy = 0;
        trackParamsPanel.add(minvTF, gridBagConstraints);

        maxyLabel.setText("Max Y: ");
        gridBagConstraints = new java.awt.GridBagConstraints();
        gridBagConstraints.gridx = 0;
        gridBagConstraints.gridy = 1;
        trackParamsPanel.add(maxyLabel, gridBagConstraints);

        maxuLabel.setText(" Max U: ");
        gridBagConstraints = new java.awt.GridBagConstraints();
        gridBagConstraints.gridx = 2;
        gridBagConstraints.gridy = 1;
        trackParamsPanel.add(maxuLabel, gridBagConstraints);

        maxvLabel.setText(" Max V: ");
        gridBagConstraints = new java.awt.GridBagConstraints();
        gridBagConstraints.gridx = 4;
        gridBagConstraints.gridy = 1;
        trackParamsPanel.add(maxvLabel, gridBagConstraints);

        maxyTF.setPreferredSize(new java.awt.Dimension(40, 19));
        gridBagConstraints = new java.awt.GridBagConstraints();
        gridBagConstraints.gridx = 1;
        gridBagConstraints.gridy = 1;
        trackParamsPanel.add(maxyTF, gridBagConstraints);

        maxuTF.setPreferredSize(new java.awt.Dimension(40, 19));
        gridBagConstraints = new java.awt.GridBagConstraints();
        gridBagConstraints.gridx = 3;
        gridBagConstraints.gridy = 1;
        trackParamsPanel.add(maxuTF, gridBagConstraints);

        maxvTF.setPreferredSize(new java.awt.Dimension(40, 19));
        gridBagConstraints = new java.awt.GridBagConstraints();
        gridBagConstraints.gridx = 5;
        gridBagConstraints.gridy = 1;
        trackParamsPanel.add(maxvTF, gridBagConstraints);

        gridBagConstraints = new java.awt.GridBagConstraints();
        gridBagConstraints.gridx = 1;
        gridBagConstraints.gridy = 1;
        gridBagConstraints.insets = new java.awt.Insets(0, 10, 0, 0);
        trackPanel.add(trackParamsPanel, gridBagConstraints);

        gridBagConstraints = new java.awt.GridBagConstraints();
        gridBagConstraints.gridx = 0;
        gridBagConstraints.gridy = 1;
        gridBagConstraints.insets = new java.awt.Insets(5, 5, 5, 5);
        getContentPane().add(trackPanel, gridBagConstraints);

        controlPanel.setLayout(new java.awt.GridBagLayout());

        grabButton.setText("Take Picture");
        grabButton.addMouseListener(new java.awt.event.MouseAdapter() {
            public void mouseClicked(java.awt.event.MouseEvent evt) {
                grabButtonMouseClicked(evt);
            }
        });

        controlPanel.add(grabButton, new java.awt.GridBagConstraints());

        trackMovePanCheckBox.setSelected(true);
        trackMovePanCheckBox.setText("Track Move Pan");
        controlPanel.add(trackMovePanCheckBox, new java.awt.GridBagConstraints());

        trackMoveTiltCheckBox.setSelected(true);
        trackMoveTiltCheckBox.setText("Track Move Tilt");
        gridBagConstraints = new java.awt.GridBagConstraints();
        gridBagConstraints.gridx = 1;
        gridBagConstraints.gridy = 1;
        gridBagConstraints.anchor = java.awt.GridBagConstraints.WEST;
        controlPanel.add(trackMoveTiltCheckBox, gridBagConstraints);

        startTrackButton.setText("Start Track");
        startTrackButton.addMouseListener(new java.awt.event.MouseAdapter() {
            public void mouseClicked(java.awt.event.MouseEvent evt) {
                startTrackButtonMouseClicked(evt);
            }
        });

        controlPanel.add(startTrackButton, new java.awt.GridBagConstraints());

        stopStreamingButton.setText("Stop Streaming");
        stopStreamingButton.setEnabled(false);
        stopStreamingButton.addMouseListener(new java.awt.event.MouseAdapter() {
            public void mouseClicked(java.awt.event.MouseEvent evt) {
                stopStreamingButtonMouseClicked(evt);
            }
        });

        gridBagConstraints = new java.awt.GridBagConstraints();
        gridBagConstraints.gridx = 2;
        gridBagConstraints.gridy = 1;
        gridBagConstraints.gridwidth = 2;
        controlPanel.add(stopStreamingButton, gridBagConstraints);

        detectMotionButton.setText("Show Motion");
        detectMotionButton.addMouseListener(new java.awt.event.MouseAdapter() {
            public void mouseClicked(java.awt.event.MouseEvent evt) {
                detectMotionButtonMouseClicked(evt);
            }
        });

        gridBagConstraints = new java.awt.GridBagConstraints();
        gridBagConstraints.gridx = 3;
        gridBagConstraints.gridy = 0;
        controlPanel.add(detectMotionButton, gridBagConstraints);

        lineFollowCheckBox.setText("Line Follow");
        gridBagConstraints = new java.awt.GridBagConstraints();
        gridBagConstraints.gridx = 0;
        gridBagConstraints.gridy = 1;
        controlPanel.add(lineFollowCheckBox, gridBagConstraints);

        gridBagConstraints = new java.awt.GridBagConstraints();
        gridBagConstraints.gridx = 0;
        gridBagConstraints.gridy = 2;
        gridBagConstraints.insets = new java.awt.Insets(10, 5, 5, 5);
        getContentPane().add(controlPanel, gridBagConstraints);

        headPanelA.setLayout(new java.awt.GridBagLayout());

        headPanel.setLayout(new java.awt.GridBagLayout());

        headPanel.setPreferredSize(new java.awt.Dimension(380, 180));
        headPanel.addMouseListener(new java.awt.event.MouseAdapter() {
            public void mouseClicked(java.awt.event.MouseEvent evt) {
                headPanelMouseClicked(evt);
            }
        });
        headPanel.addMouseMotionListener(new java.awt.event.MouseMotionAdapter() {
            public void mouseDragged(java.awt.event.MouseEvent evt) {
                headPanelMouseDragged(evt);
            }
        });

        gridBagConstraints = new java.awt.GridBagConstraints();
        gridBagConstraints.gridx = 1;
        gridBagConstraints.gridy = 0;
        gridBagConstraints.gridwidth = 3;
        gridBagConstraints.gridheight = 3;
        headPanelA.add(headPanel, gridBagConstraints);

        upLabel.setFont(new java.awt.Font("Dialog", 2, 10));
        upLabel.setText("up");
        gridBagConstraints = new java.awt.GridBagConstraints();
        gridBagConstraints.gridx = 0;
        gridBagConstraints.gridy = 0;
        gridBagConstraints.anchor = java.awt.GridBagConstraints.NORTHEAST;
        headPanelA.add(upLabel, gridBagConstraints);

        tiltLabel.setHorizontalAlignment(javax.swing.SwingConstants.CENTER);
        tiltLabel.setText("Tilt");
        gridBagConstraints = new java.awt.GridBagConstraints();
        gridBagConstraints.gridx = 0;
        gridBagConstraints.gridy = 1;
        gridBagConstraints.weighty = 1.0;
        headPanelA.add(tiltLabel, gridBagConstraints);

        downLabel.setFont(new java.awt.Font("Dialog", 2, 10));
        downLabel.setText("down");
        gridBagConstraints = new java.awt.GridBagConstraints();
        gridBagConstraints.gridx = 0;
        gridBagConstraints.gridy = 2;
        gridBagConstraints.anchor = java.awt.GridBagConstraints.EAST;
        gridBagConstraints.weighty = 0.2;
        headPanelA.add(downLabel, gridBagConstraints);

        leftLabel.setFont(new java.awt.Font("Dialog", 2, 10));
        leftLabel.setText("left");
        gridBagConstraints = new java.awt.GridBagConstraints();
        gridBagConstraints.gridx = 1;
        gridBagConstraints.gridy = 3;
        gridBagConstraints.anchor = java.awt.GridBagConstraints.NORTHWEST;
        headPanelA.add(leftLabel, gridBagConstraints);

        panLabel.setHorizontalAlignment(javax.swing.SwingConstants.CENTER);
        panLabel.setText("Pan");
        gridBagConstraints = new java.awt.GridBagConstraints();
        gridBagConstraints.gridx = 2;
        gridBagConstraints.gridy = 3;
        gridBagConstraints.weightx = 0.1;
        headPanelA.add(panLabel, gridBagConstraints);

        rightLabel.setFont(new java.awt.Font("Dialog", 2, 10));
        rightLabel.setText("right");
        gridBagConstraints = new java.awt.GridBagConstraints();
        gridBagConstraints.gridx = 3;
        gridBagConstraints.gridy = 3;
        gridBagConstraints.anchor = java.awt.GridBagConstraints.NORTHEAST;
        headPanelA.add(rightLabel, gridBagConstraints);

        gridBagConstraints = new java.awt.GridBagConstraints();
        gridBagConstraints.gridx = 0;
        gridBagConstraints.gridy = 3;
        gridBagConstraints.insets = new java.awt.Insets(5, 5, 5, 5);
        getContentPane().add(headPanelA, gridBagConstraints);

        pack();
    }//GEN-END:initComponents
    
    /** Clear the YUV and RGB labels when the mouse leaves the image label. */
    private void imageLabelMouseExited(java.awt.event.MouseEvent evt) {//GEN-FIRST:event_imageLabelMouseExited
        ivLabel.setText("V: ");
        iyLabel.setText("Y: ");
        iuLabel.setText("U: ");
        irLabel.setText("R: ");
        igLabel.setText("G: ");
        ibLabel.setText("B: ");
    }//GEN-LAST:event_imageLabelMouseExited
    
   private void detectMotionButtonMouseClicked(java.awt.event.MouseEvent evt) {//GEN-FIRST:event_detectMotionButtonMouseClicked
       if(!detectMotionButton.isEnabled())
           return;
       if(rover.isConnected()) {
           if(rover.startMotionDetection()) {
               statusLabel.setText("starting motion detection worked");
               //disable conflicting buttons
               grabButton.setEnabled(false);
               startTrackButton.setEnabled(false);
               detectMotionButton.setEnabled(false);
               stopStreamingButton.setEnabled(true);
               
               motionThread = new ShowMotionThread();
               motionThread.start();
           }else
               statusLabel.setText(ActionConstants.getErrorText(rover.state.getStatus()));
       }else
           statusLabel.setText("Connect first!");
   }//GEN-LAST:event_detectMotionButtonMouseClicked
   
   private class ShowMotionThread extends Thread {
       
       boolean keepGoing = true;
       
       public ShowMotionThread(){
       }
       
       public void run() {
           rover.receive.registerObject(this, ReceiveThread.MOTION_RECEIVE);
           waitForData();
           while(keepGoing){
               drawMotionRegion();
               waitForData();
           }
           rover.receive.unregisterObject(this, ReceiveThread.MOTION_RECEIVE);
       }
       
       private void drawMotionRegion() {
           int pos = 0;
           for(int y=0; y<height; y++)
               for(int x=0; x<width; x++) {
                   previewPix[pos++] = 0xff000000 | (rover.receive.motion[11*(y/16) + (x/16)] << 16);
               }
           previewSource.newPixels();
       }
       
       private synchronized void waitForData() {
           try {
               wait();
           }catch(InterruptedException ie) {}
       }
       
       public synchronized void kill() {
           keepGoing = false;
           notify();
       }
       
   }
   
   private void previewLabelMouseClicked(java.awt.event.MouseEvent evt) {//GEN-FIRST:event_previewLabelMouseClicked
       int newPan = pan+VisionUtil.objectPan(evt.getX())*3/4;
       int newTilt = tilt+VisionUtil.objectTilt(evt.getY())*3/4;
       statusLabel.setText("Recentering at pan="+newPan+", tilt="+tilt+".");
       if(grabPicture(newPan, newTilt, width, height)) {
           pan = newPan;
           tilt = newTilt;
           repaint();
       }
   }//GEN-LAST:event_previewLabelMouseClicked
   
   private void headPanelMouseDragged(java.awt.event.MouseEvent evt) {//GEN-FIRST:event_headPanelMouseDragged
       moveHead(evt);
       /*if(!gettingPicture){
           Thread grabThread = new Thread(){
               public void run(){
                   grabPicture(pan, tilt, width, height);
               }
           };
           grabThread.start();
       }*/
   }//GEN-LAST:event_headPanelMouseDragged
   
   private void headPanelMouseClicked(java.awt.event.MouseEvent evt) {//GEN-FIRST:event_headPanelMouseClicked
       moveHead(evt);
       grabPicture(pan, tilt, width, height);
   }//GEN-LAST:event_headPanelMouseClicked
   
   private boolean headMoving = false;
   private void moveHead(java.awt.event.MouseEvent evt) {
       synchronized(headPanel) {
           if(headMoving)
               return;
           headMoving = true;
       }
       int x = evt.getX()-HEAD_STARTX;
       int y = evt.getY()-HEAD_STARTY;
       if(x > 0 && y > 0 && x < HEAD_WIDTH && y < HEAD_HEIGHT){
           x *= 360 / HEAD_WIDTH;  //scale x
           y *= 140 / HEAD_HEIGHT; //scale y
           x = 180 - x;
           y = 90 - y;
           if(rover.look(x, y)) {
               statusLabel.setText("Moved head to pan="+x+", tilt="+y+".");
               pan = x;
               tilt = y;
               repaint();
           }else
               statusLabel.setText(ActionConstants.getErrorText(rover.state.getStatus()));
       }
       headMoving = false;
   }
   
   private void stopStreamingButtonMouseClicked(java.awt.event.MouseEvent evt) {//GEN-FIRST:event_stopStreamingButtonMouseClicked
       if(!stopStreamingButton.isEnabled())
           return;
       if(rover.isConnected()) {
           if(rover.stopStreaming()) {
               statusLabel.setText("stopping streaming worked");
               //enable buttons
               grabButton.setEnabled(true);
               startTrackButton.setEnabled(true);
               detectMotionButton.setEnabled(true);
               stopStreamingButton.setEnabled(false);
               
               if(trackThread != null) {
                   trackThread.kill();
                   trackThread = null;
               }
               if(motionThread != null) {
                   motionThread.kill();
                   motionThread = null;
               }
           }else
               statusLabel.setText(ActionConstants.getErrorText(rover.state.getStatus()));
       }else
           statusLabel.setText("Connect first!");
   }//GEN-LAST:event_stopStreamingButtonMouseClicked
   
   private void startTrackButtonMouseClicked(java.awt.event.MouseEvent evt) {//GEN-FIRST:event_startTrackButtonMouseClicked
       if(!startTrackButton.isEnabled())
           return;
       if(rover.isConnected()) {
           int miny, minu, minv, maxy, maxu, maxv;
           try {
               miny = Integer.parseInt(minyTF.getText());
               minu = Integer.parseInt(minuTF.getText());
               minv = Integer.parseInt(minvTF.getText());
               maxy = Integer.parseInt(maxyTF.getText());
               maxu = Integer.parseInt(maxuTF.getText());
               maxv = Integer.parseInt(maxvTF.getText());
           }catch(Exception e) {
               statusLabel.setText("You need ints in the text fields!");
               return;
           }
           if(rover.startTrack(miny, maxy, minu, maxu, minv, maxv, 0,
           trackMovePanCheckBox.isSelected(), trackMoveTiltCheckBox.isSelected(), 0)) {
               statusLabel.setText("starting tracking worked");
               //disable conflicting buttons
               grabButton.setEnabled(false);
               startTrackButton.setEnabled(false);
               detectMotionButton.setEnabled(false);
               stopStreamingButton.setEnabled(true);
               
               trackThread = new ShowTrackThread();
               trackThread.start();
           }else
               statusLabel.setText(ActionConstants.getErrorText(rover.state.getStatus()));
       }else
           statusLabel.setText("Connect first!");
   }//GEN-LAST:event_startTrackButtonMouseClicked
   
   private class ShowTrackThread extends Thread {
       
       boolean keepGoing = true; //tells the thread whether or not to keep going
       boolean lastFollowed = false; //whether or not it moved to follow the line on the last iteration
       
       public ShowTrackThread(){
       }
       
       public void run() {
           rover.receive.registerObject(this, ReceiveThread.TRACK_RECEIVE);
           waitForData();
           while(keepGoing){
               //System.out.println("got new data "+rover.receive.minY+" "+rover.receive.maxY+
               //" "+rover.receive.minX+" "+rover.receive.maxX);
               drawTrackedRegion();
               lineFollow();
               waitForData();
           }
           rover.receive.unregisterObject(this, ReceiveThread.TRACK_RECEIVE);
       }
       
       private void drawTrackedRegion() {
           System.arraycopy(rgb, 0, previewPix, 0, width*height);
           int trackedWidth = rover.receive.maxX - rover.receive.minX + 1;
           int trackedHeight = rover.receive.maxY - rover.receive.minY + 1;
           double conf = ((double) rover.receive.pixels) / (trackedWidth*trackedHeight);
           int fillColor;
           if(conf > .666)
               fillColor = Color.GREEN.getRGB();
           else if(conf > .333)
               fillColor = Color.YELLOW.getRGB();
           else
               fillColor = Color.RED.getRGB();
           
           for(int y=rover.receive.minY; y<=rover.receive.maxY; y++) {
               int pos = y*width + rover.receive.minX;
               for(int x=rover.receive.minX; x<=rover.receive.maxX; x++) {
                   previewPix[pos++] = fillColor;
               }
           }
           if(rover.receive.x > 0 && rover.receive.x<(width-1) &&
           rover.receive.y > 0 && rover.receive.y<(height-1))
               for(int y=rover.receive.y-1; y<=rover.receive.y+1; y++) {
                   int pos = y*width + rover.receive.x;
                   previewPix[pos-1] = previewPix[pos] = previewPix[pos+1] = Color.BLUE.getRGB();
               }
           previewSource.newPixels();
           pan = rover.state.getPan();
           tilt = rover.state.getTilt();
           repaint();
       }
       
       /** Code to control the rover so that it will follow the object being tracked.
        * It makes the assumption that the object is on the ground.  If it's not on
        * the ground, it won't follow the object.
        * The rover is stopped if there is nothing to follow.
        */
       private void lineFollow() {
           int newPan = pan+VisionUtil.objectPan(rover.receive.x)*3/4;
           int newTilt = tilt+VisionUtil.objectTilt(rover.receive.y)*3/4;
           //require following enabled, more than 30 pixels, and the object to be on the ground.
           if(lineFollowCheckBox.isSelected() && rover.receive.pixels > 30 && newTilt < -5) {
               lastFollowed = true;
               
               double pan = Math.toRadians(newPan);
               double tilt = Math.toRadians(-newTilt);
               double dist = VisionUtil.CAMERA_HEIGHT/Math.tan(tilt);  //distance of middle mass from rover
               //find the x and y relative to the rover, with the rover pointing along the x-axis
               double rovX = dist * Math.cos(pan);
               double rovY = dist * Math.sin(pan);
               int radius;
               if(rovX == 0)
                   radius = 0; //if the object is straight ahead, move the rover straight.
               else //turn about the radius to intersect the tracked point
                   radius = (int) ((rovX*rovX + rovY*rovY)/(2*rovY));
               rover.quadTurn(255, radius);
           }else if(lastFollowed == true) { //if the rover is moving, we must stop it
               lastFollowed = false;
               rover.setAll(3, 0, 0, 0, 0, 0, 0, 0, 0); //stop the rover
           }
       }
       
       private synchronized void waitForData() {
           try {
               wait();
           }catch(InterruptedException ie) {}
       }
       
       public synchronized void kill() {
           keepGoing = false;
           notify();
       }
       
   }
   
   private void imageLabelMouseClicked(java.awt.event.MouseEvent evt) {//GEN-FIRST:event_imageLabelMouseClicked
       int realX = evt.getX();
       int realY = evt.getY();
       trackFinder.addPoint(realX, realY);
       
       //find a color to draw with - I'm picking a color different from the one clicked on
       int clickedPixel = rgb[realY*width+realX];
       int clickedRed = (clickedPixel >> 16) & 255;
       int clickedGreen = (clickedPixel >> 8) & 255;
       int clickedBlue = clickedPixel & 255;
       int fillerColor, darkFiller;
       if(clickedRed > clickedGreen && clickedRed > clickedBlue){
           fillerColor = 0xff00ffff; //cyan
           darkFiller = clickedGreen > clickedBlue ? 0xff0000ff : 0xff00ff00;
       }else if(clickedGreen > clickedBlue){
           fillerColor = 0xffff00ff; //magenta
           darkFiller = clickedRed > clickedBlue ? 0xff0000ff : 0xffff0000;
       }else{
           fillerColor = 0xffffff00; //yellow
           darkFiller = clickedRed > clickedGreen ? 0xff00ff00 : 0xffff0000;
       }
       
       int i=0, inPixels=0, outPixels=0;
       boolean currMask[][] = trackFinder.currMask;
       for(int y=0; y<height; y++)
           for(int x=0; x<width; x++) {
               if(currMask[y][x]) {
                   previewPix[i] = fillerColor;
                   inPixels++;
               }else if(trackFinder.pixelInRange(x, y)){
                   previewPix[i] = darkFiller;
                   outPixels++;
               }else
                   previewPix[i] = rgb[i];
               i++;
           }
       previewSource.newPixels();
       minyTF.setText(Integer.toString(trackFinder.cminy));
       minuTF.setText(Integer.toString(trackFinder.cminu));
       minvTF.setText(Integer.toString(trackFinder.cminv));
       maxyTF.setText(Integer.toString(trackFinder.cmaxy));
       maxuTF.setText(Integer.toString(trackFinder.cmaxu));
       maxvTF.setText(Integer.toString(trackFinder.cmaxv));
       statusLabel.setText("There are "+inPixels+" pixels in your object and "+outPixels+" outside of it.");
   }//GEN-LAST:event_imageLabelMouseClicked
   
   //this function updates the YUV RGB displays as you move the mouse
   private void imageLabelMouseMoved(java.awt.event.MouseEvent evt) {//GEN-FIRST:event_imageLabelMouseMoved
       int realX = evt.getX();
       int realY = evt.getY();
       int offset = realY * width + realX;
       ivLabel.setText("V: "+((yuv[offset] >> 16) & 255));
       iyLabel.setText("Y: "+((yuv[offset] >> 8) & 255));
       iuLabel.setText("U: "+(yuv[offset] & 255));
       irLabel.setText("R: "+((rgb[offset] >> 16) & 255));
       igLabel.setText("G: "+((rgb[offset] >> 8) & 255));
       ibLabel.setText("B: "+(rgb[offset] & 255));
   }//GEN-LAST:event_imageLabelMouseMoved
   
   private void grabButtonMouseClicked(java.awt.event.MouseEvent evt) {//GEN-FIRST:event_grabButtonMouseClicked
       if(!grabButton.isEnabled())
           return;
       grabPicture(pan, tilt, width, height);
   }//GEN-LAST:event_grabButtonMouseClicked
   
   //private boolean gettingPicture = false;
   private boolean grabPicture(int pan, int tilt, int width, int height){
       /*synchronized(trackPanel) {
           if(gettingPicture)
               return false;
           gettingPicture = true;
       }*/
       if(rover.isConnected()) {
           byte [] yuv_in = rover.takeRawPicture(pan, tilt, width, height);
           if(yuv_in != null) {
               //convert into RGB for viewing
               VisionUtil.v4l_yuv420p2rgb(yuv_in, width, height, rgb);
               //convert into an easier format for me to read
               VisionUtil.v4l_yuv420p2yuv(yuv_in, width, height, yuv);
               System.arraycopy(rgb, 0, previewPix, 0, width*height);
               
               //refresh both images
               source.newPixels();
               previewSource.newPixels();
               //notify the track finder that the pixels have changed
               trackFinder.setYUV(yuv, width, height);
           }else {
               statusLabel.setText(ActionConstants.getErrorText(rover.state.getStatus()));
               //gettingPicture = false;
               return false;
           }
       }else {
           statusLabel.setText("Connect first!");
           //gettingPicture = false;
           return false;
       }
       //gettingPicture = false;
       return true;
   }
   
   private void ipTFActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_ipTFActionPerformed
       initCommunications();
   }//GEN-LAST:event_ipTFActionPerformed
   
   private void connectButtonMouseClicked(java.awt.event.MouseEvent evt) {//GEN-FIRST:event_connectButtonMouseClicked
       initCommunications();
   }//GEN-LAST:event_connectButtonMouseClicked
   
   /** Initializes communications and initializes the robot. Displays an error
    * message if there is an error.
    *
    *@returns true if successful, false if no connection can be established
    * or the rover does not respond.
    */
   private boolean initCommunications(){
       boolean success;
       
       //initialize communications
       String IP = ipTF.getText();
       success = rover.initComm(IP);
       rover.setCurrentIP(IP); //saves the current ip in case communication needs to be reestablished
       rover.saveIP(IP); //save the IP address so it can be loaded the next time the program is run
       
       if (success) {
           success = rover.initRobot();
           success = rover.refresh();
           if (success) {
               statusLabel.setText("Connected");
               return true;
           } else {
               statusLabel.setText("Error: rover is not responding!");
           }
       } else {
           statusLabel.setText("Error: Cannot resolve hostname.");
       }
       return false;
   }
   
   /** Exit the Application */
   private void exitForm(java.awt.event.WindowEvent evt) {//GEN-FIRST:event_exitForm
       //stop streaming on exit, but only if this program started the streaming
       if(rover.isConnected())
           if(trackThread != null || motionThread != null)
               rover.stopStreaming();
       
       System.exit(0);
   }//GEN-LAST:event_exitForm
   
   /**
    * @param args the command line arguments
    */
   public static void main(String args[]) {
       new Vision().show();
   }
   
   public void paint(Graphics g) {
       super.paint(g);
       g = headPanel.getGraphics();
       g.setColor(Color.BLACK);//draw the horizontal line at tilt=0
       g.drawLine(HEAD_STARTX, HEAD_STARTY+HEAD_HEIGHT*9/14, HEAD_STARTX+HEAD_WIDTH, HEAD_STARTY+HEAD_HEIGHT*9/14);
       //now draw the box around where it will be legal to joystick the rover in head move mode
       g.setColor(Color.BLUE);
       g.drawLine(HEAD_STARTX, HEAD_STARTY, HEAD_STARTX, HEAD_STARTY+HEAD_HEIGHT);
       g.drawLine(HEAD_STARTX+HEAD_WIDTH, HEAD_STARTY, HEAD_STARTX+HEAD_WIDTH, HEAD_STARTY+HEAD_HEIGHT);
       g.drawLine(HEAD_STARTX, HEAD_STARTY, HEAD_STARTX+HEAD_WIDTH, HEAD_STARTY);
       g.drawLine(HEAD_STARTX, HEAD_STARTY+HEAD_HEIGHT, HEAD_STARTX+HEAD_WIDTH, HEAD_STARTY+HEAD_HEIGHT);
       g.setColor(Color.BLACK); //draw the center line
       g.drawLine(HEAD_STARTX+HEAD_WIDTH/2, HEAD_STARTY, HEAD_STARTX+HEAD_WIDTH/2, HEAD_STARTY+HEAD_HEIGHT);
       
       //draw the current location of the head
       g.setColor(Color.RED);
       int x = panToX(pan);
       int y = tiltToY(tilt);
       g.drawLine(x-2, y-2, x+2, y+2);
       g.drawLine(x+2, y-2, x-2, y+2);
   }
   
   //translates a pan angle to a position in the headPanel
   private int panToX(int pan) {
       int x = 180 - pan;
       x = HEAD_WIDTH*x/360;
       return x + HEAD_STARTX;
   }
   
   //translates a tilt angle to a position in the headPanel
   private int tiltToY(int tilt) {
       int y = 90 - tilt;
       y = HEAD_HEIGHT*y/140;
       return y + HEAD_STARTY;
   }
   
    // Variables declaration - do not modify//GEN-BEGIN:variables
    private javax.swing.JPanel colorPanel;
    private javax.swing.JButton connectButton;
    private javax.swing.JPanel connectPanel;
    private javax.swing.JPanel controlPanel;
    private javax.swing.JButton detectMotionButton;
    private javax.swing.JLabel downLabel;
    private javax.swing.JButton grabButton;
    private javax.swing.JPanel headPanel;
    private javax.swing.JPanel headPanelA;
    private javax.swing.JLabel ibLabel;
    private javax.swing.JLabel igLabel;
    private javax.swing.JLabel imageLabel;
    private javax.swing.JLabel ipLabel;
    private javax.swing.JTextField ipTF;
    private javax.swing.JLabel irLabel;
    private javax.swing.JLabel iuLabel;
    private javax.swing.JLabel ivLabel;
    private javax.swing.JLabel iyLabel;
    private javax.swing.JLabel leftLabel;
    private javax.swing.JCheckBox lineFollowCheckBox;
    private javax.swing.JLabel maxuLabel;
    private javax.swing.JTextField maxuTF;
    private javax.swing.JLabel maxvLabel;
    private javax.swing.JTextField maxvTF;
    private javax.swing.JLabel maxyLabel;
    private javax.swing.JTextField maxyTF;
    private javax.swing.JLabel minuLabel;
    private javax.swing.JTextField minuTF;
    private javax.swing.JLabel minvLabel;
    private javax.swing.JTextField minvTF;
    private javax.swing.JLabel minyLabel;
    private javax.swing.JTextField minyTF;
    private javax.swing.JLabel panLabel;
    private javax.swing.JLabel previewLabel;
    private javax.swing.JLabel rightLabel;
    private javax.swing.JButton startTrackButton;
    private javax.swing.JLabel statusLabel;
    private javax.swing.JButton stopStreamingButton;
    private javax.swing.JLabel tiltLabel;
    private javax.swing.JCheckBox trackMovePanCheckBox;
    private javax.swing.JCheckBox trackMoveTiltCheckBox;
    private javax.swing.JPanel trackPanel;
    private javax.swing.JPanel trackParamsPanel;
    private javax.swing.JLabel upLabel;
    // End of variables declaration//GEN-END:variables
    
}
