content: /*
   * Copyright (C) 2015 Actor LLC. <https://actor.im>
   */
  
  package im.actor.runtime.js.websocket;
  
  import com.google.gwt.core.client.JavaScriptObject;
  import com.google.gwt.typedarrays.shared.ArrayBuffer;
  import com.google.gwt.typedarrays.shared.TypedArrays;
  import com.google.gwt.typedarrays.shared.Uint8Array;
  
  import im.actor.runtime.Log;
  import im.actor.runtime.js.utils.Conversion;
  import im.actor.runtime.mtproto.AsyncConnection;
  import im.actor.runtime.mtproto.AsyncConnectionInterface;
  import im.actor.runtime.mtproto.ConnectionEndpoint;
  
  public class WebSocketConnection extends AsyncConnection {
  
      private static int connectionId = 0;
      private final String TAG = "WebSocketConnection#" + (connectionId++);
  
      private JavaScriptObject jsWebSocket;
      private boolean isClosed;
  
      public WebSocketConnection(ConnectionEndpoint endpoint, AsyncConnectionInterface connection) {
          super(endpoint, connection);
      }
  
      @Override
      public void doConnect() {
          // Log.d(TAG, "doConnect");
  
          isClosed = true;
  
          String url;
          if (getEndpoint().getType() == ConnectionEndpoint.TYPE_WS) {
              url = "ws://" + getEndpoint().getHost() + ":" + getEndpoint().getPort() + "/";
          } else if (getEndpoint().getType() == ConnectionEndpoint.TYPE_WS_TLS) {
              url = "wss://" + getEndpoint().getHost() + ":" + getEndpoint().getPort() + "/";
          } else {
              throw new RuntimeException();
          }
          this.jsWebSocket = createJSWebSocket(url, this);
      }
  
      @Override
      public void doSend(byte[] data) {
          // Log.d(TAG, "doSend");
          if (isClosed) {
              return;
          }
          Uint8Array push = TypedArrays.createUint8Array(data.length);
          for (int i = 0; i < data.length; i++) {
              push.set(i, data[i]);
          }
          send(push);
      }
  
      @Override
      public void doClose() {
          // Log.d(TAG, "doClose");
          isClosed = true;
          close();
      }
  
      private void onRawMessage(ArrayBuffer message) {
          // Log.d(TAG, "onRawMessage");
          if (isClosed) {
              return;
          }
          onReceived(Conversion.convertBytes(message));
      }
  
      private void onRawConnected() {
          // Log.d(TAG, "onRawConnected");
          isClosed = false;
          onConnected();
      }
  
      private void onRawClosed() {
          // Log.d(TAG, "onRawClosed");
          isClosed = true;
          onClosed();
      }
  
      // Native interfaces
  
      public native void send(Uint8Array message) /*-{
          if (message == null)
              return;
  
          this.@im.actor.runtime.js.websocket.WebSocketConnection::jsWebSocket.send(message);
      }-*/;
  
      public native void close() /*-{
          this.@im.actor.runtime.js.websocket.WebSocketConnection::jsWebSocket.close();
      }-*/;
  
      public native int getBufferedAmount() /*-{
          return this.@im.actor.runtime.js.websocket.WebSocketConnection::jsWebSocket.bufferedAmount;
      }-*/;
  
      public native int getReadyState() /*-{
          return this.@im.actor.runtime.js.websocket.WebSocketConnection::jsWebSocket.readyState;
      }-*/;
  
      public native String getURL() /*-{
          return this.@im.actor.runtime.js.websocket.WebSocketConnection::jsWebSocket.url;
      }-*/;
  
      /**
       * Creates the JavaScript WebSocket component and set's all callback handlers.
       *
       * @param url
       */
      private native JavaScriptObject createJSWebSocket(final String url, final WebSocketConnection webSocket) /*-{
          var jsWebSocket = new WebSocket(url);
          jsWebSocket.binaryType = "arraybuffer"
  
          jsWebSocket.onopen = function () {
              webSocket.@im.actor.runtime.js.websocket.WebSocketConnection::onRawConnected()();
          }
  
          jsWebSocket.onclose = function () {
              webSocket.@im.actor.runtime.js.websocket.WebSocketConnection::onRawClosed()();
          }
  
          jsWebSocket.onerror = function () {
              webSocket.@im.actor.runtime.js.websocket.WebSocketConnection::onRawClosed()();
          }
  
          jsWebSocket.onmessage = function (socketResponse) {
              if (socketResponse.data) {
                  webSocket.@im.actor.runtime.js.websocket.WebSocketConnection::onRawMessage(*)(socketResponse.data);
              }
          }
  
          return jsWebSocket;
      }-*/;
  }
  