
package com.reactnative.im.client;

import android.util.Log;

import com.facebook.react.bridge.Arguments;
import com.facebook.react.bridge.Promise;
import com.facebook.react.bridge.ReactApplicationContext;
import com.facebook.react.bridge.ReactContext;
import com.facebook.react.bridge.ReactContextBaseJavaModule;
import com.facebook.react.bridge.ReactMethod;
import com.facebook.react.bridge.Callback;
import com.facebook.react.bridge.ReadableMap;
import com.facebook.react.bridge.WritableMap;
import com.facebook.react.bridge.WritableNativeMap;
import com.facebook.react.modules.core.DeviceEventManagerModule;


import org.json.JSONObject;

import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.HashMap;
import java.util.Iterator;

import javax.annotation.Nullable;

import export.Client;
import export.Handler;
import export.ReadyStateCallback;
import export.RequestStatusCallback;

public class RNReactNativeImClientModule extends ReactContextBaseJavaModule {

  private final ReactApplicationContext reactContext;
  private  Client client ;
  private final String  listener = "/v1/message/listener";
  private final String  eventListener="onMessage";
  private final String  eventPing="ping";
  public RNReactNativeImClientModule(ReactApplicationContext reactContext) {
    super(reactContext);

    this.reactContext = reactContext;


  }


  @ReactMethod
  public void setRequestProperty(String key,String value){
    Log.i("sessionId:::",value);
    client.setRequestProperty(key,value);
    client.addMessageListener(listener, new Handler() {
      @Override
      public void handle(byte[] bytes, byte[] bytes1) {
        String operator="no operator" ;
        if(bytes!=null){
          operator = new String(bytes);
        }

        String params="no params" ;
        if(bytes1!=null){
          params= new String(bytes1);
        }

        Log.i("receive msg::",params);
        try {
          WritableMap resp = Arguments.createMap();
          resp.putString("operator", operator);
          resp.putString("params", params);
          sendEvent(eventListener, resp);
        } catch (Exception e) {
          Log.e("error message receive:", operator + params);
        }

      }
    });

  }
  @ReactMethod
  public String getRequestProperty(String key){
    return client.getRequestProperty(key);
  }
  @ReactMethod
  public void setResponseProperty(String key,String value){
    client.setResponseProperty(key,value);
  }

  @ReactMethod
  public String getResponseProperty(String key){
    return client.getResponseProperty(key);
  }

  @ReactMethod
  public void removeMessageListener (String listener){
    client.removeMessageListener(listener);
  }
  @ReactMethod
  public String getReadyState(){
    return String.valueOf(client.getReadyState());
  }

  @ReactMethod
  public void connect(String server, String port, final Promise promise){
    try {
      Long portNum = Long.valueOf(port);
      if(client==null||client.getReadyState()!=1L) {
        client = new Client(server, portNum, new ReadyStateCallback() {
          @Override
          public void onClose() {
            promise.resolve("close");
          }

          @Override
          public void onError(String s) {
            promise.reject(s, "connect error!");
          }

          @Override
          public void onOpen() {
            new Runnable() {
              SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-DD HH:mm:ss");

              @Override
              public void run() {
                try {
                  client.ping(3, null, new RequestStatusCallback() {
                    @Override
                    public void onEnd() {
                      Log.i("ping end !", sdf.format(new Date()));
                      WritableMap wm = Arguments.createMap();
                      wm.putString("status", "end");
                      sendEvent(eventPing, wm);
                    }

                    @Override
                    public void onError(long l, String s) {
                      Log.i("ping error !", sdf.format(new Date()));
                      WritableMap wm = Arguments.createMap();
                      wm.putString("status", "error");
                      sendEvent(eventPing, wm);
                    }

                    @Override
                    public void onStart() {
                      Log.i("ping start !", sdf.format(new Date()));
                      WritableMap wm = Arguments.createMap();
                      wm.putString("status", "start");
                      sendEvent(eventPing, wm);

                    }

                    @Override
                    public void onSuccess(byte[] bytes, byte[] bytes1) {
                      Log.i("ping success !", sdf.format(new Date()));
                      WritableMap wm = Arguments.createMap();
                      wm.putString("status", "success");
                      sendEvent(eventPing, wm);
                    }
                  });
                } catch (Exception e) {
                  Log.e("im client ping error!", e.getMessage());
                }
              }
            }.run();



          }
        });
      }
      Log.i("open", "11111");
      client.setRetryInterval(3000L);

      promise.resolve("open");
    }catch (Exception e){
      promise.reject("port should be number string",e.getMessage());
    }

  }

  /*
  * params:payload:{operator:{},params:{}}
  * */
  @ReactMethod
  public void syncSend (ReadableMap payload, final Promise promise){
    String operator = payload.getString("operator");
    final String params =payload.getString("params");
    Log.i("async send :",operator+" "+params);

    try {

      client.syncSend(operator, params.getBytes(), new RequestStatusCallback() {
        WritableMap resp = Arguments.createMap();
        @Override
        public void onEnd() {
          resp.putString("status","end");
          // promise.resolve(resp);
        }

        @Override
        public void onError(long l, String s) {
          promise.reject(String.valueOf(l),s);
        }

        @Override
        public void onStart() {
          resp.putString("status","start");
          //promise.resolve(resp);
        }

        @Override
        public void onSuccess(byte[] bytes, byte[] bytes1) {
          String operator = new String(bytes);
          String params = new String (bytes1);

          resp.putString("operator",operator);
          resp.putString("params",params);
          resp.putString("status","success");
          promise.resolve(resp);
        }
      });
    }catch (Exception e){
      promise.reject("send error:",e.getMessage());
    }


  }

  /*
  * params:payload:{operator:{},params:{}}
  * */
  @ReactMethod
  public void asyncSend (ReadableMap payload, final Promise promise){
    String sid  = client.getRequestProperty("sid");
    Log.i("sid:：：：",sid);
    String operator = payload.getString("operator");
    final String params =payload.getString("params");
    Log.i("async send :",operator+" "+params);

    try {
      Log.i("connection status :",String.valueOf(client.getReadyState()));
      client.asyncSend(operator, params.getBytes(), new RequestStatusCallback() {
        WritableMap resp = Arguments.createMap();
        @Override
        public void onEnd() {
//            resp.putString("status","end");
          //promise.resolve(resp);
          Log.i("send","end");

        }

        @Override
        public void onError(long l, String s) {
//            Log.i("send","error");
//            Log.e("error",operator+"  "+s);
          promise.reject(String.valueOf(l),s);
        }

        @Override
        public void onStart() {
//            resp.putString("status","start");
          //promise.resolve(resp);
          Log.i("send","start");
        }

        @Override
        public void onSuccess(byte[] bytes, byte[] bytes1) {

          // String operator = new String(bytes);
          try {
            String params = new String (bytes1);

            JSONObject jobject = new JSONObject(params);
            WritableMap paramsMap= Arguments.createMap();
            Iterator<String> it = jobject.keys();
            while(it.hasNext()){
              String key = it.next();
              String value = jobject.getString(key);
              paramsMap.putString(key,value);
            }

            Log.i("send result:",params);
            resp.putString("operator","");
            resp.putMap("params", paramsMap);

            Log.i("resp:::",resp.toString());

          }catch (Exception e){

          }
          resp.putString("status","success");
          promise.resolve(resp);
        }
      });
    }catch (Exception e){
      Log.i("send","error");
      Log.e("error",e.getMessage());
      promise.reject("send error:",e.getMessage());
    }


  }


  void sendEvent ( String eventName, @Nullable WritableMap params){
    this.reactContext.getJSModule(DeviceEventManagerModule.RCTDeviceEventEmitter.class)
            .emit(eventName,params);
  }

  @Override
  public String getName() {
    return "RNReactNativeImClient";
  }
}