content: /*
   * Copyright (C) 2015 Actor LLC. <https://actor.im>
   */
  
  package im.actor.core.modules.typing;
  
  import java.util.HashMap;
  import java.util.HashSet;
  
  import im.actor.core.api.ApiTypingType;
  import im.actor.core.modules.ModuleContext;
  import im.actor.core.modules.ModuleActor;
  import im.actor.runtime.actors.ActorCreator;
  import im.actor.runtime.actors.ActorRef;
  import im.actor.runtime.actors.ActorSystem;
  import im.actor.runtime.actors.ActorCancellable;
  import im.actor.runtime.actors.Props;
  import im.actor.runtime.annotations.Verified;
  
  @Verified
  public class TypingActor extends ModuleActor {
  
      public static ActorRef get(final ModuleContext messenger) {
          return ActorSystem.system().actorOf("actor/typing", () -> new TypingActor(messenger));
      }
  
      private static final int TYPING_TEXT_TIMEOUT = 7000;
  
      private HashMap<Integer, ActorCancellable> typingsCancellables = new HashMap<>();
      private HashSet<Integer> typings = new HashSet<>();
      private HashMap<Integer, HashSet<Integer>> groupTypings = new HashMap<>();
      private HashMap<Integer, HashMap<Integer, ActorCancellable>> groupCancellables = new HashMap<>();
  
      public TypingActor(ModuleContext messenger) {
          super(messenger);
      }
  
      @Verified
      private void privateTyping(int uid, ApiTypingType type) {
          // Support only text typings
          if (type != ApiTypingType.TEXT) {
              return;
          }
  
          if (getUser(uid) == null) {
              return;
          }
  
          if (!typings.contains(uid)) {
              typings.add(uid);
  
              context().getTypingModule().getTyping(uid).getTyping().change(true);
          }
  
          if (typingsCancellables.containsKey(uid)) {
              typingsCancellables.remove(uid).cancel();
          }
          typingsCancellables.put(uid, schedule(new StopTyping(uid), TYPING_TEXT_TIMEOUT));
      }
  
      @Verified
      private void stopPrivateTyping(int uid) {
          if (typings.contains(uid)) {
              typings.remove(uid);
  
              if (typingsCancellables.containsKey(uid)) {
                  typingsCancellables.remove(uid).cancel();
              }
  
              context().getTypingModule().getTyping(uid).getTyping().change(false);
          }
      }
  
      @Verified
      private void groupTyping(int gid, int uid, ApiTypingType type) {
          // Support only text typings
          if (type != ApiTypingType.TEXT) {
              return;
          }
  
          if (getGroup(gid) == null) {
              return;
          }
  
          if (getUser(uid) == null) {
              return;
          }
  
          if (!groupTypings.containsKey(gid)) {
              HashSet<Integer> set = new HashSet<Integer>();
              set.add(uid);
              groupTypings.put(gid, set);
  
              context().getTypingModule()
                      .getGroupTyping(gid)
                      .getActive()
                      .change(new int[]{uid});
          } else {
              HashSet<Integer> src = groupTypings.get(gid);
              if (!src.contains(uid)) {
                  src.add(uid);
                  Integer[] ids = src.toArray(new Integer[src.size()]);
                  int[] ids2 = new int[ids.length];
                  for (int i = 0; i < ids.length; i++) {
                      ids2[i] = ids[i];
                  }
  
                  context().getTypingModule()
                          .getGroupTyping(gid)
                          .getActive()
                          .change(ids2);
              }
          }
  
          if (!groupCancellables.containsKey(gid)) {
              groupCancellables.put(gid, new HashMap<>());
          }
  
          HashMap<Integer, ActorCancellable> cancellables = groupCancellables.get(gid);
          if (cancellables.containsKey(uid)) {
              cancellables.remove(uid).cancel();
          }
          cancellables.put(uid, schedule(new StopGroupTyping(gid, uid), TYPING_TEXT_TIMEOUT));
      }
  
      @Verified
      private void stopGroupTyping(int gid, int uid) {
          if (!groupTypings.containsKey(gid)) {
              return;
          }
          HashSet<Integer> set = groupTypings.get(gid);
          if (set.contains(uid)) {
              set.remove(uid);
              Integer[] ids = set.toArray(new Integer[set.size()]);
              int[] ids2 = new int[ids.length];
              for (int i = 0; i < ids.length; i++) {
                  ids2[i] = ids[i];
              }
  
              context().getTypingModule()
                      .getGroupTyping(gid)
                      .getActive()
                      .change(ids2);
          }
      }
  
      // Messages
  
      @Override
      public void onReceive(Object message) {
          if (message instanceof PrivateTyping) {
              PrivateTyping typing = (PrivateTyping) message;
              privateTyping(typing.getUid(), typing.getType());
          } else if (message instanceof GroupTyping) {
              GroupTyping typing = (GroupTyping) message;
              groupTyping(typing.getGid(), typing.getUid(), typing.getType());
          } else if (message instanceof StopTyping) {
              StopTyping typing = (StopTyping) message;
              stopPrivateTyping(typing.getUid());
          } else if (message instanceof StopGroupTyping) {
              StopGroupTyping typing = (StopGroupTyping) message;
              stopGroupTyping(typing.getGid(), typing.getUid());
          } else {
              super.onReceive(message);
          }
      }
  
      public static class StopTyping {
          private int uid;
  
          public StopTyping(int uid) {
              this.uid = uid;
          }
  
          public int getUid() {
              return uid;
          }
  
          @Override
          public boolean equals(Object o) {
              if (this == o) return true;
              if (o == null || getClass() != o.getClass()) return false;
  
              StopTyping that = (StopTyping) o;
  
              if (uid != that.uid) return false;
  
              return true;
          }
  
          @Override
          public int hashCode() {
              return uid;
          }
      }
  
      public static class StopGroupTyping {
          private int gid;
          private int uid;
  
          public StopGroupTyping(int gid, int uid) {
              this.gid = gid;
              this.uid = uid;
          }
  
          public int getGid() {
              return gid;
          }
  
          public int getUid() {
              return uid;
          }
  
          @Override
          public boolean equals(Object o) {
              if (this == o) return true;
              if (o == null || getClass() != o.getClass()) return false;
  
              StopGroupTyping that = (StopGroupTyping) o;
  
              if (gid != that.gid) return false;
              if (uid != that.uid) return false;
  
              return true;
          }
  
          @Override
          public int hashCode() {
              int result = gid;
              result = 31 * result + uid;
              return result;
          }
      }
  
      public static class PrivateTyping {
          private int uid;
          private ApiTypingType type;
  
          public PrivateTyping(int uid, ApiTypingType type) {
              this.uid = uid;
              this.type = type;
          }
  
          public int getUid() {
              return uid;
          }
  
          public ApiTypingType getType() {
              return type;
          }
  
          @Override
          public boolean equals(Object o) {
              if (this == o) return true;
              if (o == null || getClass() != o.getClass()) return false;
  
              PrivateTyping that = (PrivateTyping) o;
  
              if (type != that.type) return false;
              if (uid != that.uid) return false;
  
              return true;
          }
  
          @Override
          public int hashCode() {
              int result = uid;
              result = 31 * result + type.getValue();
              return result;
          }
      }
  
      public static class GroupTyping {
          private int gid;
          private int uid;
          private ApiTypingType type;
  
          public GroupTyping(int gid, int uid, ApiTypingType type) {
              this.gid = gid;
              this.uid = uid;
              this.type = type;
          }
  
          public int getGid() {
              return gid;
          }
  
          public int getUid() {
              return uid;
          }
  
          public ApiTypingType getType() {
              return type;
          }
  
          @Override
          public boolean equals(Object o) {
              if (this == o) return true;
              if (o == null || getClass() != o.getClass()) return false;
  
              GroupTyping that = (GroupTyping) o;
  
              if (gid != that.gid) return false;
              if (type != that.type) return false;
              if (uid != that.uid) return false;
  
              return true;
          }
  
          @Override
          public int hashCode() {
              int result = gid;
              result = 31 * result + uid;
              result = 31 * result + type.getValue();
              return result;
          }
      }
  }