content: /*
   * Licensed to the Apache Software Foundation (ASF) under one
   * or more contributor license agreements.  See the NOTICE file
   * distributed with this work for additional information
   * regarding copyright ownership.  The ASF licenses this file
   * to you under the Apache License, Version 2.0 (the
   * "License"); you may not use this file except in compliance
   * with the License.  You may obtain a copy of the License at
   * 
   *   http://www.apache.org/licenses/LICENSE-2.0
   * 
   * Unless required by applicable law or agreed to in writing,
   * software distributed under the License is distributed on an
   * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
   * KIND, either express or implied.  See the License for the
   * specific language governing permissions and limitations
   * under the License.
   */
  package org.apache.weex.appfram.websocket;
  
  import android.os.Looper;
  
  import org.apache.weex.WXSDKEngine;
  import org.apache.weex.annotation.JSMethod;
  import org.apache.weex.bridge.JSCallback;
  import org.apache.weex.bridge.WXBridgeManager;
  import org.apache.weex.utils.WXLogUtils;
  
  import java.util.HashMap;
  import java.util.Map;
  
  /**
   * Created by moxun on 16/12/27.
   */
  
  public class WebSocketModule extends WXSDKEngine.DestroyableModule {
  
      private static final String TAG = "WebSocketModule";
      private static final String KEY_DATA = "data";
      private static final String KEY_CODE = "code";
      private static final String KEY_REASON = "reason";
      private static final String KEY_WAS_CLEAN = "wasClean";
  
      private IWebSocketAdapter webSocketAdapter;
      private WebSocketEventListener eventListener;
  
      public WebSocketModule() {
          WXLogUtils.e(TAG, "create new instance");
      }
  
      @JSMethod(uiThread = false)
      public void WebSocket(String url, String protocol) {
          if (webSocketAdapter != null) {
              WXLogUtils.w(TAG, "close");
              webSocketAdapter.close(WebSocketCloseCodes.CLOSE_GOING_AWAY.getCode(), WebSocketCloseCodes.CLOSE_GOING_AWAY.name());
          }
          webSocketAdapter = mWXSDKInstance.getWXWebSocketAdapter();
          if (!reportErrorIfNoAdapter()) {
              eventListener = new WebSocketEventListener();
              webSocketAdapter.connect(url, protocol, eventListener);
          }
      }
  
      @JSMethod(uiThread = false)
      public void send(String data) {
          if (!reportErrorIfNoAdapter()) {
              webSocketAdapter.send(data);
          }
      }
  
      @JSMethod(uiThread = false)
      public void close(String code, String reason) {
          if (!reportErrorIfNoAdapter()) {
              int codeNumber = WebSocketCloseCodes.CLOSE_NORMAL.getCode();
              if (code != null) {
                  try {
                      codeNumber = Integer.parseInt(code);
                  } catch (NumberFormatException e) {
                      //ignore
                  }
              }
              webSocketAdapter.close(codeNumber, reason);
          }
      }
  
      @JSMethod(uiThread = false)
      public void onopen(JSCallback callback) {
          if (eventListener != null) {
              eventListener.onOpen = callback;
          }
      }
  
      @JSMethod(uiThread = false)
      public void onmessage(JSCallback callback) {
          if (eventListener != null) {
              eventListener.onMessage = callback;
          }
      }
  
      @JSMethod(uiThread = false)
      public void onclose(JSCallback callback) {
          if (eventListener != null) {
              eventListener.onClose = callback;
          }
      }
  
      @JSMethod(uiThread = false)
      public void onerror(JSCallback callback) {
          if (eventListener != null) {
              eventListener.onError = callback;
          }
      }
  
      @Override
      public void destroy() {
          Runnable destroyTask = new Runnable() {
              @Override
              public void run() {
                  WXLogUtils.w(TAG, "close session with instance id " + mWXSDKInstance.getInstanceId());
                  if (webSocketAdapter != null) {
                      webSocketAdapter.destroy();
                  }
                  webSocketAdapter = null;
                  eventListener = null;
              }
          };
  
          if (Looper.myLooper() == Looper.getMainLooper()) {
              WXBridgeManager.getInstance().post(destroyTask);
          } else {
              destroyTask.run();
          }
      }
  
      private boolean reportErrorIfNoAdapter() {
          if (webSocketAdapter == null) {
              if (eventListener != null) {
                  eventListener.onError("No implementation found for IWebSocketAdapter");
              }
              WXLogUtils.e(TAG, "No implementation found for IWebSocketAdapter");
              return true;
          }
          return false;
      }
  
      private class WebSocketEventListener implements IWebSocketAdapter.EventListener {
          private JSCallback onOpen;
          private JSCallback onClose;
          private JSCallback onError;
          private JSCallback onMessage;
  
          @Override
          public void onOpen() {
              if (onOpen != null) {
                  onOpen.invoke(new HashMap<>(0));
              }
          }
  
          @Override
          public void onMessage(String data) {
              if (onMessage != null) {
                  Map<String, String> msg = new HashMap<>(1);
                  msg.put(KEY_DATA, data);
                  onMessage.invokeAndKeepAlive(msg);
              }
          }
  
          @Override
          public void onClose(int code, String reason, boolean wasClean) {
              if (onClose != null) {
                  Map<String, Object> msg = new HashMap<>(3);
                  msg.put(KEY_CODE, code);
                  msg.put(KEY_REASON, reason);
                  msg.put(KEY_WAS_CLEAN, wasClean);
                  onClose.invoke(msg);
              }
          }
  
          @Override
          public void onError(String msg) {
              if (onError != null) {
                  Map<String, String> info = new HashMap<>(1);
                  info.put(KEY_DATA, msg);
                  onError.invokeAndKeepAlive(info);
              }
          }
      }
  }
  