package nomadgui;

import javax.swing.*;
import javax.swing.event.*;
import java.awt.*;
import java.awt.event.*;
import java.beans.*;
import nomadgui.state.*;
import nomadgui.control.*;
import nomadgui.nddsJava.*;
import nomadgui.tools.*;

/** An extension of JFrame that forms the base container for the rest of the
  * GUI.  It contains a menubar, a status bar, and a desktop pane which holds
  * the three windows of the GUI (State, Control, and Science).
  * @see nomadgui.NomadDesktopPane
  * @see nomadgui.state.RSPFrame
  * @see nomadgui.control.RCPFrame
  */

public class NomadFrame extends JFrame {
  Container contentPane;
  NomadDesktopPane deskpane;
  nddsTelemetryThread ndds;
  
  JPanel statusArea = new JPanel();
  JLabel status = new JLabel("");
  statusListener listener = new statusListener(this);

  RSPFrame RSP;
  RCPFrame RCP;
  JInternalFrame SAP;

  /** Default Constructor.  Creates a NomadFrame with title "Nomad GUI." */

  public NomadFrame() { this("Nomad GUI"); }
  
  /** Creates an instance of NomadFrame with the specified title.
    * @param title The string to be displayed in the title bar of the GUI.
    */
  
  public NomadFrame(String title) {
    super(title);
    
    setBounds(100, 100, 1000, 740);
    
    contentPane = getContentPane();
    deskpane = new NomadDesktopPane();
    
    setJMenuBar(createMenuBar());

    statusArea.setBorder(BorderFactory.createRaisedBevelBorder());
    statusArea.setLayout(new FlowLayout(FlowLayout.LEFT,0,0));
    statusArea.add(status);
    status.setHorizontalAlignment(JLabel.LEFT);
    showStatus(" ");
    
    contentPane.add(deskpane, BorderLayout.CENTER);
    contentPane.add(statusArea, BorderLayout.SOUTH);

    RCP = new RCPFrame("Robot Control", false, true, true, true);
    RCP.setBounds(40, 40, 690, 300);
    deskpane.add(RCP);

    RSP = new RSPFrame("Robot State", false, true, true, true);
    RSP.setBounds(0, 0, 820, 575);
    deskpane.add(RSP);

    //SAP = new JInternalFrame("Science Autonomy", true, true, true, true);
    //SAP.setBounds(80, 80, 820, 575);
    //deskpane.add(SAP);
  }

  /** Sets the reference to the nddsTelemetryThread for the GUI.  This is
    * done outside of the constructor because of the way the thread was added
    * to the GUI, and since the thread is not always present (test mode).
    * @param t The reference to the GUI's nddsTelemetryThread.
    * @see nomad.nddsJava.nddsTelemetryThread
    */

  public void setNdds(nddsTelemetryThread t) { ndds = t; }

  private JMenuBar createMenuBar() {
    JMenuBar mb = new JMenuBar();

    JMenu fileMenu = new JMenu("File");
    JMenuItem exitItem = new JMenuItem("Exit", 'x');
    
    JMenu windowMenu = new JMenu("Window");
    JMenu newWinMenu = new JMenu("New...");
    JMenuItem rspItem = new JMenuItem("Robot State", 'S');
    JMenuItem rcpItem = new JMenuItem("Robot Control", 'C');
    JMenuItem sapItem = new JMenuItem("Science Autonomy", 'A');
    JMenuItem cascadeItem = new JMenuItem("Cascade", 'C');
    JMenuItem miniAllItem = new JMenuItem("Minimize All", 'M');
    JMenuItem openAllItem = new JMenuItem("Open All", 'O');

    JMenu testMenu = new JMenu("Test");
    JMenu testRobotMenu = new JMenu("Robot");
    JMenuItem linksItem = new JMenuItem("Links", 'L');
    JMenuItem armItem = new JMenuItem("Arm", 'A');
    JMenuItem bogieItem = new JMenuItem("Bogies", 'B');
    JMenuItem cameraItem = new JMenuItem("Camera", 'C');
    JMenu testPositionMenu = new JMenu("Positions");
    JMenuItem rpItem = new JMenuItem("Roll/Pitch", 'R');
    JMenuItem speedItem = new JMenuItem("Speed", 'S');
    JMenuItem headingItem = new JMenuItem("Heading", 'H');
    JMenuItem gpsItem = new JMenuItem("GPS", 'G');
    JMenu testVitalsMenu = new JMenu("Vitals");
    JMenuItem wheelsItem = new JMenuItem("Wheels", 'W');
    JMenu testWeatherMenu = new JMenu("Weather");
    JMenuItem TempItem = new JMenuItem("Temperature", 'T');
    JMenuItem PressItem = new JMenuItem("Pressure", 'P');

    fileMenu.add(exitItem);

    windowMenu.add(newWinMenu);
    newWinMenu.add(rspItem);
    newWinMenu.add(rcpItem);
    newWinMenu.add(sapItem);
    windowMenu.addSeparator();
    windowMenu.add(cascadeItem);
    windowMenu.add(miniAllItem);
    windowMenu.add(openAllItem);
    
    testMenu.add(testRobotMenu);
    testMenu.add(testPositionMenu);
    testMenu.add(testVitalsMenu);
    testMenu.add(testWeatherMenu);
    testRobotMenu.add(linksItem);
    testRobotMenu.add(armItem);
    testRobotMenu.add(bogieItem);
    testRobotMenu.add(cameraItem);
    testPositionMenu.add(rpItem);
    testPositionMenu.add(speedItem);
    testPositionMenu.add(headingItem);
    testPositionMenu.add(gpsItem);
    testVitalsMenu.add(wheelsItem);
    testWeatherMenu.add(TempItem);
    testWeatherMenu.add(PressItem);

    exitItem.setActionCommand("Exit the Nomad Interface");
    rspItem.setActionCommand("Open a new Robot State Window");
    rcpItem.setActionCommand("Open a new Robot Control Window");
    sapItem.setActionCommand("Open a new Science Autonomy Window");
    cascadeItem.setActionCommand("Cascade all Open Windows");
    miniAllItem.setActionCommand("Minimize all Open Windows");
    openAllItem.setActionCommand("Open all Minimzed Windows");
    linksItem.setActionCommand("Test Links (Deploy/Stow Once)");
    armItem.setActionCommand("Test Arm (Theta 0->120, Phi 0->180, Alpha 10->170)");
    bogieItem.setActionCommand("Test Bogie Angles (0 -> 20 -> -20 -> 0)");
    cameraItem.setActionCommand("Test Pan, Tilt, and Field of View of Hi-res camera");
    rpItem.setActionCommand("Test Roll/Pitch (Pitch, Roll, Pitch & Roll)");
    speedItem.setActionCommand("Test Speed (0 -> 50 -> -50 -> 0)");
    headingItem.setActionCommand("Test Heading (N, E, S, W, Circle)");
    gpsItem.setActionCommand("Test GPS (up, right, down, left)");
    wheelsItem.setActionCommand("Test Wheel Speeds and Amps");
    TempItem.setActionCommand("Test Internal Temperature");
    TempItem.setActionCommand("Test Pressure");

    exitItem.addChangeListener(listener);
    rspItem.addChangeListener(listener);
    rcpItem.addChangeListener(listener);
    sapItem.addChangeListener(listener);
    cascadeItem.addChangeListener(listener);
    miniAllItem.addChangeListener(listener);
    openAllItem.addChangeListener(listener);
    linksItem.addChangeListener(listener);
    armItem.addChangeListener(listener);
    bogieItem.addChangeListener(listener);
    cameraItem.addChangeListener(listener);
    rpItem.addChangeListener(listener);
    speedItem.addChangeListener(listener);
    headingItem.addChangeListener(listener);
    gpsItem.addChangeListener(listener);
    wheelsItem.addChangeListener(listener);
    TempItem.addChangeListener(listener);
    PressItem.addChangeListener(listener);
    
    fileMenu.setMnemonic('F');
    windowMenu.setMnemonic('W');
    newWinMenu.setMnemonic('N');
    testMenu.setMnemonic('T');
    testRobotMenu.setMnemonic('R');
    testPositionMenu.setMnemonic('P');
    testVitalsMenu.setMnemonic('V');
    testWeatherMenu.setMnemonic('W');
    
    mb.add(fileMenu);
    mb.add(windowMenu);
    mb.add(testMenu);

    exitItem.addActionListener(new ActionListener() {
      public void actionPerformed(ActionEvent e) {
	if (ndds != null)
	  ndds.disconnect();  // disconnect from telemetry, if connected
	System.exit(0);       // exit program
      }
    });
    
    rspItem.addActionListener(new ActionListener() {
      public void actionPerformed(ActionEvent e) {
 	if (RSP.isClosed()) { // RSP is closed
 	  RSP = new RSPFrame("Robot State", false, true, true, true);
 	  RSP.setBounds(0, 0, 820, 575);
 	  deskpane.add(RSP);
 	  return;
 	} else {              // already a RSP panel around
 	JOptionPane.showMessageDialog(NomadFrame.this,
 				      "A Robot State window already exists.",
 				      "Window Exists",
 				      JOptionPane.INFORMATION_MESSAGE);
	}
      }
    });
    
    rcpItem.addActionListener(new ActionListener() {
      public void actionPerformed(ActionEvent e) {
	if (RCP.isClosed()) { // no existing RCP
	  RCP = new RCPFrame("Robot Control", false, true, true, true);
	  RCP.setBounds(0, 0, 820, 575);
	  deskpane.add(RCP);
	  return;
	} else {              // already a RCP panel around
	JOptionPane.showMessageDialog(NomadFrame.this,
				      "A Robot Control window already exists.",
				      "Window Exists",
				      JOptionPane.INFORMATION_MESSAGE);
        }
      }
    });
    
//     sapItem.addActionListener(new ActionListener() {
//       public void actionPerformed(ActionEvent e) {
// 	if (SAP.isClosed()) { // no existing SAP
// 	  //SAP = new SAPFrame("Science Autonomy", false, true, true, true);
// 	  //SAP.setBounds(0, 0, 820, 575);
// 	  //deskpane.add(SAP);
// 	  return;
// 	} else {              // already a SAP panel around
// 	JOptionPane.showMessageDialog(NomadFrame.this,
// 				      "A Science Autonomy window "+
// 				      "already exists.",
// 				      "Window Exists",
// 				      JOptionPane.INFORMATION_MESSAGE);
//         }
//       }
//     });
    
    cascadeItem.addActionListener(new ActionListener() {
      public void actionPerformed(ActionEvent e) {
	deskpane.cascade();
      }
    });
    
    miniAllItem.addActionListener(new ActionListener() {
      public void actionPerformed(ActionEvent e) {
	deskpane.minimizeAll();
      }
    });
    
    openAllItem.addActionListener(new ActionListener() {
      public void actionPerformed(ActionEvent e) {
	deskpane.openAll();
      }
    });

    // Test functions
    
    linksItem.addActionListener(new ActionListener() {
      public void actionPerformed(ActionEvent e) {
	synchronized(RSP) {
	  linkTestThread t = new linkTestThread(RSP);
	  t.start();
	}
      }
    });

    armItem.addActionListener(new ActionListener() {
      public void actionPerformed(ActionEvent e) {
	synchronized(RSP) {
	  armTestThread t = new armTestThread(RSP);
	  t.start();
	}
      }
    });

    bogieItem.addActionListener(new ActionListener() {
      public void actionPerformed(ActionEvent e) {
	synchronized(RSP) {
	  bogieTestThread t = new bogieTestThread(RSP);
	  t.start();
	}
      }
    });

    cameraItem.addActionListener(new ActionListener() {
      public void actionPerformed(ActionEvent e) {
	synchronized(RSP) {
	  fovTestThread t = new fovTestThread(RSP);
	  t.start();
	}
      }
    });

    rpItem.addActionListener(new ActionListener() {
      public void actionPerformed(ActionEvent e) {
	synchronized(RSP) {
	  rpTestThread t = new rpTestThread(RSP);
	  t.start();
	}
      }
    });

    speedItem.addActionListener(new ActionListener() {
      public void actionPerformed(ActionEvent e) {
	synchronized(RSP) {
	  speedTestThread t = new speedTestThread(RSP);
	  t.start();
	}
      }
    });

    headingItem.addActionListener(new ActionListener() {
      public void actionPerformed(ActionEvent e) {
	synchronized(RSP) {
	  headingTestThread t = new headingTestThread(RSP);
	  t.start();
	}
      }
    });

    gpsItem.addActionListener(new ActionListener() {
      public void actionPerformed(ActionEvent e) {
	synchronized(RSP) {
	  gpsTestThread t = new gpsTestThread(RSP);
	  t.start();
	}
      }
    });

    wheelsItem.addActionListener(new ActionListener() {
      public void actionPerformed(ActionEvent e) {
	synchronized(RSP) {
	  wheelTestThread t = new wheelTestThread(RSP);
	  t.start();
	}
      }
    });
    
    TempItem.addActionListener(new ActionListener() {
      public void actionPerformed(ActionEvent e) {
	synchronized(RSP) {
	  TempTestThread t = new TempTestThread(RSP);
	  t.start();
	}
      }
    });
    
    PressItem.addActionListener(new ActionListener() {
      public void actionPerformed(ActionEvent e) {
	synchronized(RSP) {
	  pressureTestThread t = new pressureTestThread(RSP);
	  t.start();
	}
      }
    });
    
    return mb;
  }

  /** Sets the text in the status bar of the GUI.
    * @param s The string to be displayed in the status bar.
    */
  
  public void showStatus(String s) {
    runnableText text = new runnableText(s, status);
    SwingUtilities.invokeLater(text);
    //status.setText(s);
  }

  // ------------------------------
  // update functions for telemetry
  // ------------------------------
  
  /** Gives the GUI the current roll of the robot.
    * @param r The roll of the robot. */
  synchronized public void setRoll(float r) { RSP.setRoll(r); }
  
  /** Gives the GUI the current pitch of the robot.
    * @param p The pitch of the robot. */
  synchronized public void setPitch(float p) { RSP.setPitch(p); }
  
  /** Gives the GUI the current speed of the robot.  The speed in this case
    * is determined by the change in GPS position, and represents the actual
    * movement of the robot rather than the speed of the wheels, which may be
    * inaccurate due to wheel slippage, etc.
    * @param speed The speed of the robot. */
  synchronized public void setRobotSpeed(int speed)
    { RSP.setRobotSpeed(speed); }
  
  /** Gives the GUI the current turn radius of the robot.
    * @param rad The turn radius of the robot. */
  synchronized public void setRadius(int rad) { RSP.setRadius(rad); }
  
  /** Gives the GUI the current heading of the robot.
    * @param h The heading of the robot. */
  synchronized public void setHeading(int h) { RSP.setHeading(h); }
  
  /** Gives the GUI the current angle of the first arm joint.
    * @param t angle of the first arm joint. */
  synchronized public void setTheta(float t) { RSP.setTheta(t); }
  
  /** Gives the GUI the current angle of the second arm joint.
    * @param p The angle of the second arm joint. */
  synchronized public void setPhi(float p) { RSP.setPhi(p); }
  
  /** Gives the GUI the current angle of the wrist joint.
    * @param a The angle of the wrist joint. */
  synchronized public void setAlpha(float a) { RSP.setAlpha(a); }
  
  /** Gives the GUI the current bogie angle of the robot.
    * @param b The bogie angle of the robot. */
  synchronized public void setBogie(float b) { RSP.setBogie(b); }
  
  /** Gives the GUI the current link positions of the robot's steering racks.
    * @param gr The right rack position.
    * @param gl The left rack position. */
  synchronized public void setLinks(float gr, float gl)
    { RSP.setLinks(gr, gl); }
  
  /** Gives the GUI the current wheel speeds of the robot.
    * @param fr The front right wheel speed.
    * @param fl The front left wheel speed.
    * @param br The back right wheel speed.
    * @param bl The back left wheel speed. */
  synchronized public void setWheelSpeed(int fr, int fl, int br, int bl)
    { RSP.setWheelSpeed(fr, fl, br, bl); }
  
  /** Gives the GUI the current amp states.
    * @param fre "Enable" status of the front right wheel amp.
    * @param fle "Enable" status of the front left wheel amp.
    * @param bre "Enable" status of the back right wheel amp.
    * @param ble "Enable" status of the back left wheel amp.
    * @param sre "Enable" status of the right steering amp.
    * @param sle "Enable" status of the left steering amp.
    * @param frf "Fault" status of the front right wheel amp.
    * @param flf "Fault" status of the front left wheel amp.
    * @param brf "Fault" status of the back right wheel amp.
    * @param blf "Fault" status of the back left wheel amp.
    * @param srf "Fault" status of the right steering amp.
    * @param slf "Fault" status of the left steering amp. */
  synchronized public void setAmps(boolean fre, boolean fle, boolean bre,
				   boolean ble, boolean sre, boolean sle,
				   boolean frf, boolean flf, boolean brf,
				   boolean blf, boolean srf, boolean slf) {
    RSP.setAmps(fre, fle, bre, ble, sre, sle, frf, flf, brf, blf, srf, slf); }
  
  /** Gives the GUI the current inside and outside temperatures.
    * @param i The inside temperature of the robot.
    * @param o The outside temperature. */
  synchronized public void setTemp(int i, int o) { RSP.setTemp(i,o); }
  
  /** Gives the GUI the current GPS position of the robot.
    * @param x The x (north) position of the robot.
    * @param y The y (east) position of the robot.
    * @param z The z (up) position of the robot. */
  synchronized public void setGPS(float x, float y, float z)
    { RSP.setGPS(x,y,z); }
  
  /** Gives the GUI the current pressure (weather).
    * @param p The current pressure. */
  synchronized public void setPressure(int p) { RSP.setPressure(p); }
  
  /** Gives the GUI the current wind conditions.
    * @param s The current wind speed.
    * @param g The current wind gust.
    * @param c The current wind chill.
    * @param d The current wind direction. */
  synchronized public void setWind(short s, short g, int c, short d)
    { RSP.setWind(s,g,c,d); }
  
  /** Gives the GUI the most recent odomoter update (new distance travelled).
    * @param dist The amount travelled since the last odometer update. */
  synchronized public void setOdometer(long dist) { RSP.setOdometer(dist); }
  
  /** Gives the GUI the change in time (for time ON) since the last update.
    * @param t The amount of time passed since the last time update. */
  synchronized public void setTime(long t) { RSP.setTime(t); }
  
  /** Gives the GUI the current driving mode of the robot.  Currently, the
    * value 0 gives a driving mode of "Autonomous", the value 1 gives a
    * driving mode of "Manual", and the value 2 gives a driving mode of 
    * "Independent."
    * @param m The driving mode of the robot. */
  synchronized public void setDriveMode(int m) { RSP.setDriveMode(m); }
  
  /** Gives the GUI the value of the currents for each motor.  The motors are
    * numbered as follows:
    * <ul>
    * <li> <b>0</b> The back right wheel motor.</li>
    * <li> <b>1</b> The back left wheel motor.</li>
    * <li> <b>2</b> The front right wheel motor.</li>
    * <li> <b>3</b> The front left wheel motor.</li>
    * <li> <b>4</b> The right steering motor.</li>
    * <li> <b>5</b> The left steering motor.</li>
    * </ul>
    * @param c The commanded currents of each motor.
    * @param m The monitored currents of each motor.
    * @param s The sensed currents of each motor.*/
  synchronized public void setCurrents(float c[], float m[], float s[])
    { RSP.setCurrents(c, m, s); }
  
  /** Gives the GUI the current motor speeds and power consumption. The motors
    * are numbered as follows:
    * <ul>
    * <li> <b>0</b> The back right wheel motor.</li>
    * <li> <b>1</b> The back left wheel motor.</li>
    * <li> <b>2</b> The front right wheel motor.</li>
    * <li> <b>3</b> The front left wheel motor.</li>
    * <li> <b>4</b> The right steering motor.</li>
    * <li> <b>5</b> The left steering motor.</li>
    * </ul>
    * @param p The current power consumption of each motor.
    * @param s The current speed of each motor.*/
  synchronized public void setMotors(float p[], float s[])
    { RSP.setMotors(p, s); }
  
  /** Gives the GUI the current field of view of the hi-res camera.
    * @param p The pan of the hi-res camera.
    * @param t The tilt of the hi-res camera.
    * @param f The field of view of the hi-res camera, in degrees.*/
  synchronized public void setFOV(float p, float t, float f)
    { RSP.setFOV(p,t,f); }
}

class statusListener implements ChangeListener {
  NomadFrame nframe;

  public statusListener(NomadFrame n) {
    nframe = n;
  }

  public void stateChanged(ChangeEvent e) {
    JMenuItem b = (JMenuItem)e.getSource();
    if (b.isArmed())
      nframe.showStatus(b.getActionCommand());
  }
}
