package chat.rocket.android.service;

import android.app.Service;
import android.content.Context;
import android.content.Intent;
import android.os.IBinder;
import android.support.annotation.Nullable;
import bolts.Task;
import chat.rocket.android.helper.LogcatIfError;
import chat.rocket.android.model.ServerConfig;
import chat.rocket.android.realm_helper.RealmHelper;
import chat.rocket.android.realm_helper.RealmListObserver;
import chat.rocket.android.realm_helper.RealmStore;
import io.realm.RealmResults;
import java.util.HashMap;
import java.util.List;

/**
 * Background service for Rocket.Chat.Application class.
 */
public class RocketChatService extends Service {

  private RealmHelper realmHelper;
  private HashMap<String, RocketChatWebSocketThread> webSocketThreads;
  private RealmListObserver<ServerConfig> connectionRequiredServerConfigObserver;

  /**
   * ensure RocketChatService alive.
   */
  public static void keepalive(Context context) {
    context.startService(new Intent(context, RocketChatService.class));
  }

  @Override public void onCreate() {
    super.onCreate();
    webSocketThreads = new HashMap<>();
    realmHelper = RealmStore.getDefault();
    connectionRequiredServerConfigObserver = realmHelper
            .createListObserver(realm -> realm.where(ServerConfig.class)
                .isNotNull("hostname")
                .equalTo("state", ServerConfig.STATE_READY)
                .findAll())
            .setOnUpdateListener(this::connectToServerWithServerConfig);

    refreshServerConfigState();
  }

  private void refreshServerConfigState() {
    realmHelper.executeTransaction(realm -> {
      RealmResults<ServerConfig> configs = realm.where(ServerConfig.class).findAll();
      for (ServerConfig config: configs) {
        if (config.getState() != ServerConfig.STATE_READY) {
          config.setState(ServerConfig.STATE_READY);
        }
      }
      return null;
    }).continueWith(new LogcatIfError());;
  }

  @Override public int onStartCommand(Intent intent, int flags, int startId) {
    List<ServerConfig> configs = realmHelper.executeTransactionForReadResults(realm ->
        realm.where(ServerConfig.class)
            .equalTo("state", ServerConfig.STATE_CONNECTED)
            .findAll());
    for (ServerConfig config: configs) {
      String serverConfigId = config.getServerConfigId();
      if (webSocketThreads.containsKey(serverConfigId)) {
        RocketChatWebSocketThread thread = webSocketThreads.get(serverConfigId);
        if (thread != null) {
          thread.keepalive();
        }
      }
    }

    realmHelper.executeTransaction(realm -> {
      RealmResults<ServerConfig> targetConfigs = realm
          .where(ServerConfig.class)
          .beginGroup()
          .equalTo("state", ServerConfig.STATE_CONNECTION_ERROR)
          .or()
          .isNotNull("error")
          .endGroup()
          .isNotNull("session")
          .findAll();
      for (ServerConfig config : targetConfigs) {
        config.setState(ServerConfig.STATE_READY);
        config.setError(null);
      }
      return null;
    }).onSuccessTask(task -> {
      connectionRequiredServerConfigObserver.sub();
      return null;
    });
    return START_STICKY;
  }

  private void connectToServerWithServerConfig(List<ServerConfig> configList) {
    if (configList.isEmpty()) {
      return;
    }

    ServerConfig config = configList.get(0);
    createWebSocketThread(config).onSuccess(task -> {
      RocketChatWebSocketThread thread = task.getResult();
      if (thread != null) {
        thread.keepalive();
      }
      return null;
    });
  }

  private Task<RocketChatWebSocketThread> createWebSocketThread(final ServerConfig config) {
    final String serverConfigId = config.getServerConfigId();
    webSocketThreads.put(serverConfigId, null);
    return ServerConfig.updateState(serverConfigId, ServerConfig.STATE_CONNECTING)
        .onSuccessTask(_task ->
            RocketChatWebSocketThread.getStarted(getApplicationContext(), config))
        .onSuccessTask(task ->
            ServerConfig.updateState(serverConfigId, ServerConfig.STATE_CONNECTED)
                .onSuccessTask(_task -> task))
        .onSuccessTask(task -> {
          webSocketThreads.put(serverConfigId, task.getResult());
          return task;
        });
  }

  private Task<RocketChatWebSocketThread> findOrCreateWebSocketThread(final ServerConfig config) {
    final String serverConfigId = config.getServerConfigId();
    if (webSocketThreads.containsKey(serverConfigId)) {
      return Task.forResult(webSocketThreads.get(serverConfigId));
    } else {
      return createWebSocketThread(config);
    }
  }

  @Override public void onDestroy() {
    if (connectionRequiredServerConfigObserver != null) {
      connectionRequiredServerConfigObserver.unsub();
    }
    super.onDestroy();
  }

  @Nullable
  @Override
  public IBinder onBind(Intent intent) {
    return null;
  }
}

@@ -1710,126 +1710,131 @@
 ass)
-.findAll();%0A      for (ServerConfig config: configs) %7B%0A        if (config.getState() != ServerConfig.STATE_READY) %7B%0A  
+%0A          .notEqualTo(%22state%22, ServerConfig.STATE_READY)%0A          .findAll();%0A      for (ServerConfig config: configs) %7B%0A
     
@@ -1880,26 +1880,16 @@
 READY);%0A
-        %7D%0A
       %7D%0A
@@ -3399,310 +3399,8 @@
 0);%0A
-    createWebSocketThread(config).onSuccess(task -%3E %7B%0A      RocketChatWebSocketThread thread = task.getResult();%0A      if (thread != null) %7B%0A        thread.keepalive();%0A      %7D%0A      return null;%0A    %7D);%0A  %7D%0A%0A  private Task%3CRocketChatWebSocketThread%3E createWebSocketThread(final ServerConfig config) %7B%0A
     
@@ -3465,63 +3465,8 @@
     
-webSocketThreads.put(serverConfigId, null);%0A    return 
 Serv
@@ -3560,130 +3560,218 @@
 ask(
-_
 task -%3E
-%0A            RocketChatWebSocketThread.getStarted(getApplicationContext(), config))%0A        .onSuccessTask(task -%3E
+ createWebSocketThread(config))%0A        .onSuccessTask(task -%3E %7B%0A          RocketChatWebSocketThread thread = task.getResult();%0A          if (thread != null) %7B%0A            thread.keepalive();%0A          %7D
 %0A   
@@ -3777,17 +3777,22 @@
         
- 
+return
  ServerC
@@ -3854,31 +3854,26 @@
 NNECTED)
+;
 %0A
-      
         
   .onSuc
@@ -3868,172 +3868,43 @@
     
-  .onSuccessTask(_task -%3E task))%0A        .onSuccessTask(task -%3E %7B%0A          webSocketThreads.put(serverConfigId, task.getResult());%0A          return task;%0A        %7D
+%7D).continueWith(new LogcatIfError()
 );%0A 
@@ -3953,15 +3953,9 @@
 ad%3E 
-findOrC
+c
 reat
@@ -4062,28 +4062,24 @@
 igId();%0A    
-if (
 webSocketThr
@@ -4079,35 +4079,27 @@
 cketThreads.
-containsKey
+put
 (serverConfi
@@ -4097,31 +4097,33 @@
 rverConfigId
-)) %7B%0A  
+, null);%0A
     return T
@@ -4125,24 +4125,19 @@
 urn 
-Task.forResult(w
+RocketChatW
 ebSo
@@ -4150,42 +4150,83 @@
 read
-s
 .get
-(serverConfigId));%0A    %7D else
+Started(getApplicationContext(), config)%0A        .onSuccessTask(task -%3E
  %7B%0A 
@@ -4234,51 +4234,102 @@
     
+ 
-return createWebSocketThread(config);%0A
+   webSocketThreads.put(serverConfigId, task.getResult());%0A          return task;%0A    
     %7D
+);
 %0A  %7D

set state CONNECTED after thread.keepalive()