package edu.harvard.econcs.turkserver.client;
import edu.harvard.econcs.turkserver.QuizMaterials;
import edu.harvard.econcs.turkserver.api.ClientLobbyController;
import java.awt.Container;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.ItemEvent;
import java.awt.event.ItemListener;
import java.util.concurrent.BlockingQueue;
import java.util.concurrent.LinkedBlockingQueue;
import javax.swing.BoxLayout;
import javax.swing.JLabel;
import javax.swing.JOptionPane;
import javax.swing.JPanel;
import javax.swing.SwingUtilities;
public abstract class GUIController implements ItemListener, ActionListener {
private enum Activity { ACTIVE, INACTIVE };
protected final ClientLobbyController clientCont;
private final Container content;
private final Container host;
protected LobbyPanel lobby;
// This should always be at the bottom of the screen
protected StatusBar statusBar;
// Blank screen messages
protected JPanel blankPanel;
protected JLabel blankLabel;
private volatile Activity activeStatus;
/**
*
* @param client
* @param content
* @param host the top level container (JFrame or Applet)
*/
protected GUIController(ClientLobbyController client, Container content, Container host) {
this.clientCont = client;
this.content = content;
this.host = host;
content.setLayout(new BoxLayout(content, BoxLayout.PAGE_AXIS));
// Create lobby
lobby = new LobbyPanel(this);
// Create status bar
statusBar = new StatusBar("Initializing...");
// Set to active by default, timers will start on inactivity
activeStatus = Activity.ACTIVE;
// Create blank screen stuff
blankPanel = new JPanel();
blankLabel = new JLabel();
blankPanel.add(blankLabel);
}
/**
* Dummy constructor for gc that does nothing
* @param client
*/
public GUIController(ClientLobbyController client) {
this.clientCont = client;
content = null;
host = null;
}
@Override
public void itemStateChanged(ItemEvent e) {
// From the checkbox
if( e.getStateChange() == ItemEvent.SELECTED ) {
clientCont.updateLobbyReadiness(true);
}
else {
clientCont.updateLobbyReadiness(false);
}
}
@Override
public void actionPerformed(ActionEvent e) {
if( LobbyPanel.updateStatusCmd.equals(e.getActionCommand()) ) {
clientCont.updateLobbyStatus(lobby.getStatusMsg());
}
else {
System.out.println("Unrecognized command: " + e.toString());
}
}
public LobbyPanel getLobby() {
return lobby;
}
public Container getContent() {
return content;
}
/**
* Gets the top level container
* @return
*/
public Container getHost() {
return host;
}
/**
* These two methods should be called from the same thread
* @param millis
*/
public void recordInactivity(long millis) {
clientCont.recordInactivity(millis);
if( activeStatus == Activity.ACTIVE ) {
blankRedraw(
"You seem to be inactive. Move the mouse to return to the game.");
activeStatus = Activity.INACTIVE;
setStatus("Inactive - move mouse or press any key");
}
}
/**
* These two methods should be called from the same thread
*/
public void clearInactivity() {
if( activeStatus == Activity.INACTIVE ) {
experimentRedraw();
activeStatus = Activity.ACTIVE;
setStatus("Resumed");
}
}
/**
* Pops a modal dialog that waits for input.
* @param prompt
* @return
*/
public String questionMsg(final String prompt) {
/* TODO fix this really lame semantics that
* breaks when multiple windows pop up
*/
final BlockingQueue<String> sem = new LinkedBlockingQueue<String>(1);
SwingUtilities.invokeLater(new Runnable() {
@Override
public void run() {
String resp = JOptionPane.showInternalInputDialog(content, prompt);
// Have to pass something
sem.offer(resp == null ? "" : resp);
System.out.println("Got " + resp);
}
});
String ret = null;
while( ret == null ) {
// Wait for response
try {
ret = sem.take();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
return ret;
}
public void popMsg(final String str) {
SwingUtilities.invokeLater(new Runnable() {
@Override
public void run() {
JOptionPane.showInternalMessageDialog(content, str);
}
});
}
public void popMsg(final String str, final String title, final int messageType) {
SwingUtilities.invokeLater(new Runnable() {
@Override
public void run() {
JOptionPane.showInternalMessageDialog(content, str, title, messageType);
}
});
}
public void setStatus(final String status) {
SwingUtilities.invokeLater(new Runnable() {
@Override
public void run() {
statusBar.setMessage(status);
}
});
}
protected abstract void addExperimentComponents();
/**
* Draws a blank screen with the status bar
*/
public void blankRedraw(final String message) {
SwingUtilities.invokeLater(new Runnable() {
@Override
public void run() {
content.removeAll();
content.setLayout(new BoxLayout(content, BoxLayout.PAGE_AXIS));
blankLabel.setText(message);
content.add(blankPanel);
content.add(statusBar);
// Redraw everything including the label
content.validate();
}
});
}
/**
* Draws the lobby
*/
public void lobbyRedraw() {
lobby.clearModel();
SwingUtilities.invokeLater(new Runnable() {
@Override
public void run() {
content.removeAll();
content.setLayout(new BoxLayout(content, BoxLayout.PAGE_AXIS));
content.add(lobby);
content.add(statusBar);
content.validate();
}
});
}
/**
* Draws the experiment
*/
public void experimentRedraw() {
SwingUtilities.invokeLater(new Runnable() {
@Override
public void run() {
content.removeAll();
content.setLayout(new BoxLayout(content, BoxLayout.PAGE_AXIS));
addExperimentComponents();
content.add(statusBar);
content.repaint();
}
});
}
/**
* Applies the quiz to the user and gets the results
* Blocks until quiz is finished
* @param qm
* @return
*/
public abstract QuizResults doQuiz(QuizMaterials qm);
}