LiteralPotatoDeveloper_BitcoinBubbles/BitcoinBubbles/WebsocketClient.pde
import java.lang.reflect.Method;
import java.net.URI;
import org.eclipse.jetty.client.HttpClient;
import org.eclipse.jetty.util.ssl.SslContextFactory;
import org.eclipse.jetty.websocket.client.ClientUpgradeRequest;
import org.eclipse.jetty.websocket.client.WebSocketClient;
import processing.core.PApplet;

/**
 * 
 * @author Lasse Steenbock Vestergaard
 *
 * Class for creating websocket client connections to any websocket server. Sub-protocols have not yet been implemented, and it's therefore only possible to connect to regular websocket servers. 
 *
 */
public class WebsocketClient {
  private Method webSocketEvent;
  private Method webSocketEventBinary;
  private WebsocketClientEvents socket;

  /**
   *
   * Initiating the client connection
   *
   * @param parent Processing's PApplet object
   * @param endpointURI The URI to connect to Ex. ws://localhost:8025/john
   */
  public WebsocketClient(PApplet parent, String endpointURI) {
    this(parent, parent, endpointURI, new StringList());
  }

  /**
   *
   * Initiating the client connection
   *
   * @param parent Processing's PApplet object
   * @param endpointURI The URI to connect to Ex. ws://localhost:8025/john
   * @param headers A list of custom headers to attach to the WS request, like "User-Agent:Processing"
   */
  public WebsocketClient(PApplet parent, String endpointURI, StringList headers) {
    this(parent, parent, endpointURI, headers);
  }

  /**
   * More flexible constructor in case you don't want callbacks called
   * in your PApplet but in a different class. Use this if you are
   * instantiating WebsocketClient in a class.
   *
   * @param parent Processing's PApplet object
   * @param callbacks The object implementing .webSocketEvent()
   * @param endpointURI The URI to connect to Ex. ws://localhost:8025/john
   * @param headers A list of custom headers to attach to the WS request, like "User-Agent:Processing"
   */
  public WebsocketClient(PApplet parent, Object callbacks,
               String endpointURI, StringList headers) {
    parent.registerMethod("dispose", this);

    try {
      webSocketEvent = callbacks.getClass().getMethod("webSocketEvent",
          String.class);
      webSocketEventBinary = callbacks.getClass().getMethod("webSocketEvent", byte[].class, int.class, int.class);
    } catch (Exception e) {
      // no such method, or an error.. which is fine, just ignore
    }

    WebSocketClient client = null;

    if(endpointURI.startsWith("wss")) {
      SslContextFactory ssl = new SslContextFactory();
      client = new WebSocketClient(ssl);
    } else {
      client = new WebSocketClient();
    }

    try {
      socket = new WebsocketClientEvents(callbacks, webSocketEvent,
          webSocketEventBinary);
      
      //Thomas Savine - Bandaid for message buffer limits.
      client.getPolicy().setMaxBinaryMessageBufferSize(1000000);
      client.getPolicy().setMaxTextMessageBufferSize(1000000);
      client.getPolicy().setMaxTextMessageSize(1000000);
      
      client.start();
      
      URI echoUri = new URI(endpointURI);
      ClientUpgradeRequest request = new ClientUpgradeRequest();

      // If headers are present, set them 
      for (String header : headers)
      {
        String[] h = header.split(":");
        if (h.length != 2) 
        {
        throw new Exception("Incorrectly formatted header \"" + header + 
          "\", please use \":\" to separate key and value, " +
          "as in \"User-Agent:Processing\"");
        }
        request.setHeader(h[0], h[1]);
      }

      
      client.connect(socket, echoUri, request);
      socket.getLatch().await();

    } catch (Throwable t) {
      t.printStackTrace();
    }
  }

  /**
   *
   * Send message to the websocket server. At a later stage it should be possible to send messages to specific clients connected to the same server
   *
   * @param message The message to send
   */
  public void sendMessage(String message){
    socket.sendMessage(message);
  }

  public void sendMessage(byte[] data){
    socket.sendMessage(data);
  }
  
  public void dispose(){
    // Anything in here will be called automatically when 
      // the parent sketch shuts down. For instance, this might
      // shut down a thread used by this library.
  }
}
