package nodebox.client.devicehandler;
import com.google.common.collect.ImmutableList;
import nodebox.node.Device;
import oscP5.OscEventListener;
import oscP5.OscMessage;
import oscP5.OscP5;
import oscP5.OscStatus;
import javax.swing.*;
import java.awt.*;
import java.awt.event.*;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
public class OSCDeviceHandler implements DeviceHandler {
private String name;
private OscP5 oscP5;
private int oscPort;
private boolean syncWithTimeline;
private Map<String, List<Object>> oscMessages = new HashMap<String, List<Object>>();
private boolean paused;
public OSCDeviceHandler(String name) {
this(name, -1, false);
}
public OSCDeviceHandler(String name, int oscPort, boolean syncWithTimeline) {
this.name = name;
this.oscPort = oscPort;
this.syncWithTimeline = syncWithTimeline;
oscP5 = null;
oscMessages.clear();
paused = false;
}
@Override
public String getName() {
return name;
}
public int getPort() {
return oscPort;
}
@Override
public boolean isSyncedWithTimeline() {
return syncWithTimeline;
}
public boolean isPaused() {
return paused;
}
public boolean isRunning() {
return oscP5 != null;
}
public Map<String, List<Object>> getOscMessages() {
return oscMessages;
}
private static int randomOSCPort() {
return 1024 + (int) Math.round(Math.random() * 10000);
}
@Override
public void start() {
if (oscP5 != null)
stop();
if (oscPort == -1) return;
oscMessages.clear();
oscP5 = new OscP5(new Object(), oscPort);
oscP5.addListener(new OscEventListener() {
@Override
public void oscEvent(OscMessage m) {
if (! isPaused()) {
ImmutableList<Object> arguments = ImmutableList.copyOf(m.arguments());
oscMessages.put(m.addrPattern(), arguments);
}
}
@Override
public void oscStatus(OscStatus ignored) {
}
});
}
@Override
public void pause() {
paused = true;
}
@Override
public void resume() {
paused = false;
if (oscP5 == null)
start();
}
@Override
public void stop() {
if (oscP5 != null)
oscP5.stop();
oscP5 = null;
paused = false;
}
@Override
public void addData(Map<String, Object> map) {
map.put(getName() + ".messages", getOscMessages());
}
@Override
public AbstractDeviceControl createControl() {
return new OSCDeviceControl(this);
}
private class OSCDeviceControl extends AbstractDeviceControl {
private JLabel deviceNameLabel;
private JTextField portNumberField;
private JCheckBox syncWithTimelineCheck;
private JButton startButton;
private JButton stopButton;
private JButton clearButton;
public OSCDeviceControl(OSCDeviceHandler deviceHandler) {
super(deviceHandler);
setLayout(new BoxLayout(this, BoxLayout.X_AXIS));
Dimension d = new Dimension(550, 30);
setPreferredSize(d);
setMaximumSize(d);
setSize(d);
deviceNameLabel = new JLabel(deviceHandler.getName());
portNumberField = new JTextField();
portNumberField.setText(String.valueOf(getPort()));
portNumberField.addActionListener(new ActionListener() {
@Override
public void actionPerformed(ActionEvent actionEvent) {
changePortNumber();
}
}
);
portNumberField.addFocusListener(new FocusAdapter() {
@Override
public void focusLost(FocusEvent focusEvent) {
changePortNumber();
}
});
portNumberField.setPreferredSize(new Dimension(70, portNumberField.getHeight()));
portNumberField.setMinimumSize(new Dimension(70, portNumberField.getHeight()));
syncWithTimelineCheck = new JCheckBox("Sync with Timeline");
syncWithTimelineCheck.setSelected(isSyncedWithTimeline());
syncWithTimelineCheck.addItemListener(new ItemListener() {
@Override
public void itemStateChanged(ItemEvent itemEvent) {
syncWithTimeline = syncWithTimelineCheck.isSelected();
setPropertyValue(Device.TIMELINE_SYNC, String.valueOf(syncWithTimeline));
}
});
startButton = new JButton();
if (isRunning()) {
startButton.setText(isPaused() ? "Start" : "Pause");
} else {
startButton.setText("Start");
}
startButton.addActionListener(new ActionListener() {
@Override
public void actionPerformed(ActionEvent actionEvent) {
if (! isRunning()) {
startOSC();
} else if (isPaused()) {
resumeOSC();
} else {
pauseOSC();
}
}
});
stopButton = new JButton("Stop");
stopButton.addActionListener(new ActionListener() {
@Override
public void actionPerformed(ActionEvent actionEvent) {
stopOSC();
}
});
stopButton.setEnabled(oscP5 != null);
clearButton = new JButton("Clear");
clearButton.addActionListener(new ActionListener() {
@Override
public void actionPerformed(ActionEvent actionEvent) {
oscMessages.clear();
}
});
add(Box.createHorizontalStrut(10));
add(deviceNameLabel);
add(Box.createHorizontalStrut(5));
add(portNumberField);
add(Box.createHorizontalStrut(5));
add(syncWithTimelineCheck);
add(Box.createHorizontalStrut(5));
add(startButton);
add(Box.createHorizontalStrut(5));
add(stopButton);
add(Box.createHorizontalStrut(5));
add(clearButton);
add(Box.createHorizontalGlue());
}
private void startOSC() {
start();
if (isRunning())
startButton.setText(isRunning() ? "Pause" : "Start");
stopButton.setEnabled(oscP5 != null);
}
private void resumeOSC() {
resume();
startButton.setText(isRunning() ? "Pause" : "Start");
stopButton.setEnabled(true);
}
private void pauseOSC() {
pause();
startButton.setText(isRunning() ? "Resume" : "Start");
stopButton.setEnabled(true);
}
private void stopOSC() {
stop();
startButton.setText("Start");
stopButton.setEnabled(false);
}
private void changePortNumber() {
try {
int newPort = Integer.parseInt(portNumberField.getText());
stopOSC();
oscPort = newPort;
setPropertyValue("port", String.valueOf(newPort));
} catch (Exception e) {
// todo: better error handling of invalid port values
portNumberField.setText(String.valueOf(getPort()));
return;
}
}
}
}