content: /*
   * Copyright (C) 2015 Actor LLC. <https://actor.im>
   */
  
  package im.actor.core.modules.sequence;
  
  import org.jetbrains.annotations.NotNull;
  
  import java.util.ArrayList;
  import java.util.List;
  
  import im.actor.core.api.ApiGroup;
  import im.actor.core.api.ApiGroupOutPeer;
  import im.actor.core.api.ApiUser;
  import im.actor.core.api.ApiUserOutPeer;
  import im.actor.core.api.base.FatSeqUpdate;
  import im.actor.core.api.base.SeqUpdate;
  import im.actor.core.api.rpc.RequestGetReferencedEntitites;
  import im.actor.core.events.NewSessionCreated;
  import im.actor.core.modules.AbsModule;
  import im.actor.core.modules.ModuleContext;
  import im.actor.core.modules.sequence.internal.ExecuteAfter;
  import im.actor.core.network.parser.Update;
  import im.actor.runtime.actors.ActorRef;
  import im.actor.runtime.actors.messages.Void;
  import im.actor.runtime.eventbus.BusSubscriber;
  import im.actor.runtime.eventbus.Event;
  import im.actor.runtime.promise.Promise;
  import im.actor.runtime.promise.PromiseFunc;
  import im.actor.runtime.promise.PromiseResolver;
  import im.actor.runtime.promise.Promises;
  import im.actor.runtime.promise.PromisesArray;
  
  import static im.actor.runtime.actors.ActorSystem.system;
  
  public class Updates extends AbsModule implements BusSubscriber {
  
      // j2objc workaround
      private static final Void DUMB = null;
  
      private ActorRef updateActor;
      private ActorRef updateHandler;
      private SequenceHandlerInt updateHandlerInt;
  
      public Updates(ModuleContext messenger) {
          super(messenger);
      }
  
      public void run() {
          this.updateHandler = system().actorOf("actor/updates/handler", SequenceHandlerActor.CONSTRUCTOR(context()));
          this.updateHandlerInt = new SequenceHandlerInt(this.updateHandler);
          this.updateActor = system().actorOf("actor/updates", SequenceActor.CONSTRUCTOR(context()));
  
  
          context().getEvents().subscribe(this, NewSessionCreated.EVENT);
      }
  
      public SequenceHandlerInt getUpdateHandler() {
          return updateHandlerInt;
      }
  
      public void onPushReceived(int seq, long authId) {
          if (updateActor != null) {
              updateActor.send(new SequenceActor.PushSeq(seq, authId));
          }
      }
  
      public Promise<Void> applyUpdate(int seq, byte[] state, Update update) {
          return new Promise<>((PromiseFunc<Void>) resolver -> {
              updateActor.send(new SeqUpdate(seq, state, update.getHeaderKey(), update.toByteArray()));
              executeAfter(seq, () -> resolver.result(null));
          });
      }
  
      public Promise<Void> applyUpdate(int seq, byte[] state, Update update,
                                       List<ApiUser> users, ApiGroup group) {
          ArrayList<ApiGroup> groups = new ArrayList<>();
          groups.add(group);
          return applyUpdate(seq, state, update, users, groups);
      }
  
      public Promise<Void> applyUpdate(int seq, byte[] state, Update update,
                                       List<ApiUser> users, List<ApiGroup> groups) {
          return new Promise<>((PromiseFunc<Void>) resolver -> {
              updateActor.send(new FatSeqUpdate(seq, state, update.getHeaderKey(), update.toByteArray(),
                      users, groups));
              executeAfter(seq, () -> resolver.result(null));
          });
      }
  
      public Promise<Void> waitForUpdate(int seq) {
          return new Promise<>((PromiseFunc<Void>) resolver -> {
              executeAfter(seq, () -> resolver.result(null));
          });
      }
  
      public Promise<Void> applyRelatedData(final List<ApiUser> users) {
          return applyRelatedData(users, new ArrayList<>());
      }
  
      public Promise<Void> applyRelatedData(final List<ApiUser> users, final ApiGroup group) {
          ArrayList<ApiGroup> groups = new ArrayList<>();
          groups.add(group);
          return applyRelatedData(users, groups);
      }
  
      public Promise<Void> applyRelatedData(final List<ApiUser> users, final List<ApiGroup> groups) {
          Promise<Void> res = Promise.success(null);
          if (users.size() > 0) {
              res = res.chain(v -> context().getUsersModule().getUserRouter().applyUsers(users));
          }
          if (groups.size() > 0) {
              res = res.chain(v -> context().getGroupsModule().getRouter().applyGroups(groups));
          }
          return res;
      }
  
      public Promise<Void> loadRequiredPeers(List<ApiUserOutPeer> users, List<ApiGroupOutPeer> groups) {
  
          Promise<List<ApiUserOutPeer>> usersMissingPeers = context().getUsersModule().getUserRouter()
                  .fetchMissingUsers(users);
  
          Promise<List<ApiGroupOutPeer>> groupMissingPeers = context().getGroupsModule().getRouter()
                  .fetchPendingGroups(groups);
  
          return Promises.tuple(usersMissingPeers, groupMissingPeers)
                  .flatMap(missing -> {
                      if (missing.getT1().size() > 0 || missing.getT2().size() > 0) {
                          return api(new RequestGetReferencedEntitites(missing.getT1(), missing.getT2()))
                                  .flatMap(r -> applyRelatedData(r.getUsers(), r.getGroups()));
                      } else {
                          return Promise.success(null);
                      }
                  });
      }
  
  
      @Deprecated
      public void onUpdateReceived(Object update) {
          updateActor.send(update);
      }
  
      @Deprecated
      public void executeAfter(int seq, Runnable runnable) {
          updateActor.send(new ExecuteAfter(seq, runnable));
      }
  
  
      public void resetModule() {
          // TODO: Implement
      }
  
      @Override
      public void onBusEvent(Event event) {
          if (event instanceof NewSessionCreated) {
              updateActor.send(new SequenceActor.Invalidate());
          }
      }
  }