content: /*
   * Copyright (C) 2015 Actor LLC. <https://actor.im>
   */
  
  package im.actor.core.viewmodel;
  
  import com.google.j2objc.annotations.ObjectiveCName;
  import com.google.j2objc.annotations.Property;
  
  import org.jetbrains.annotations.NotNull;
  
  import java.util.ArrayList;
  import java.util.Collection;
  import java.util.HashSet;
  
  import im.actor.core.api.ApiMapValue;
  import im.actor.core.entity.Group;
  import im.actor.core.entity.GroupMember;
  import im.actor.core.entity.GroupType;
  import im.actor.core.viewmodel.generics.AvatarValueModel;
  import im.actor.core.viewmodel.generics.BooleanValueModel;
  import im.actor.core.viewmodel.generics.IntValueModel;
  import im.actor.core.viewmodel.generics.StringValueModel;
  import im.actor.runtime.annotations.MainThread;
  import im.actor.runtime.mvvm.BaseValueModel;
  import im.actor.runtime.mvvm.ModelChangedListener;
  import im.actor.runtime.mvvm.ValueModel;
  import im.actor.runtime.mvvm.ValueModelCreator;
  
  /**
   * Group View Model
   */
  public class GroupVM extends BaseValueModel<Group> {
  
      public static ValueModelCreator<Group, GroupVM> CREATOR = GroupVM::new;
  
      @Property("nonatomic, readonly")
      private int groupId;
      @NotNull
      @Property("nonatomic, readonly")
      private GroupType groupType;
      @NotNull
      @Property("nonatomic, readonly")
      private StringValueModel name;
      @NotNull
      @Property("nonatomic, readonly")
      private AvatarValueModel avatar;
      @NotNull
      @Property("nonatomic, readonly")
      private BooleanValueModel isMember;
      @NotNull
      @Property("nonatomic, readonly")
      private IntValueModel membersCount;
      @NotNull
      @Property("nonatomic, readonly")
      private BooleanValueModel isCanWriteMessage;
      @NotNull
      @Property("nonatomic, readonly")
      private BooleanValueModel isCanCall;
      @NotNull
      @Property("nonatomic, readonly")
      private BooleanValueModel isCanClear;
      @NotNull
      @Property("nonatomic, readonly")
      private BooleanValueModel isCanJoin;
      @NotNull
      @Property("nonatomic, readonly")
      private BooleanValueModel isCanViewInfo;
  
      @NotNull
      @Property("nonatomic, readonly")
      private ValueModel<HashSet<GroupMember>> members;
      @NotNull
      @Property("nonatomic, readonly")
      private BooleanValueModel isAsyncMembers;
      @NotNull
      @Property("nonatomic, readonly")
      private BooleanValueModel isCanViewMembers;
      @NotNull
      @Property("nonatomic, readonly")
      private BooleanValueModel isCanInviteMembers;
      @NotNull
      @Property("nonatomic, readonly")
      private BooleanValueModel isCanEditInfo;
      @NotNull
      @Property("nonatomic, readonly")
      private BooleanValueModel isHistoryShared;
      @NotNull
      @Property("nonatomic, readonly")
      private BooleanValueModel isCanEditAdministration;
      @NotNull
      @Property("nonatomic, readonly")
      private BooleanValueModel isCanEditAdmins;
      @NotNull
      @Property("nonatomic, readonly")
      private BooleanValueModel isCanViewAdmins;
      @NotNull
      @Property("nonatomic, readonly")
      private BooleanValueModel isCanLeave;
      @NotNull
      @Property("nonatomic, readonly")
      private BooleanValueModel isCanDelete;
      @NotNull
      @Property("nonatomic, readonly")
      private BooleanValueModel isCanInviteViaLink;
      @NotNull
      @Property("nonatomic, readonly")
      private BooleanValueModel isCanKickInvited;
      @NotNull
      @Property("nonatomic, readonly")
      private BooleanValueModel isCanKickAnyone;
      @NotNull
      @Property("nonatomic, readonly")
      private BooleanValueModel isCanEditForeign;
      @NotNull
      @Property("nonatomic, readonly")
      private BooleanValueModel isCanDeleteForeign;
      @NotNull
      @Property("nonatomic, readonly")
      private BooleanValueModel isDeleted;
  
      @NotNull
      @Property("nonatomic, readonly")
      private StringValueModel theme;
      @NotNull
      @Property("nonatomic, readonly")
      private StringValueModel about;
      @NotNull
      @Property("nonatomic, readonly")
      private StringValueModel shortName;
      @Property("nonatomic, readonly")
      private IntValueModel ownerId;
      @NotNull
      @Property("nonatomic, readonly")
      private ValueModel<Integer> presence;
      @NotNull
      @Property("nonatomic, readonly")
      private ValueModel<ApiMapValue> ext;
  
      @NotNull
      private ArrayList<ModelChangedListener<GroupVM>> listeners = new ArrayList<>();
  
      /**
       * <p>INTERNAL API</p>
       * Create Group View Model
       *
       * @param rawObj initial value of Group
       */
      public GroupVM(@NotNull Group rawObj) {
          super(rawObj);
          this.groupId = rawObj.getGroupId();
          this.groupType = rawObj.getGroupType();
          this.name = new StringValueModel("group." + groupId + ".title", rawObj.getTitle());
          this.avatar = new AvatarValueModel("group." + groupId + ".avatar", rawObj.getAvatar());
          this.isMember = new BooleanValueModel("group." + groupId + ".isMember", rawObj.isMember());
          this.membersCount = new IntValueModel("group." + groupId + ".membersCount", rawObj.getMembersCount());
          this.isCanWriteMessage = new BooleanValueModel("group." + groupId + ".can_write", rawObj.isCanSendMessage());
          this.isCanCall = new BooleanValueModel("group." + groupId + ".can_call", rawObj.isCanCall());
          this.isCanViewMembers = new BooleanValueModel("group." + groupId + ".can_view_members", rawObj.isCanViewMembers());
          this.isCanInviteMembers = new BooleanValueModel("group." + groupId + ".can_invite_members", rawObj.isCanInviteMembers());
          this.isCanEditInfo = new BooleanValueModel("group." + groupId + ".can_edit_info", rawObj.isCanEditInfo());
          this.isAsyncMembers = new BooleanValueModel("group." + groupId + ".isAsyncMembers", rawObj.isAsyncMembers());
          this.isCanEditAdministration = new BooleanValueModel("group." + groupId + ".isCanEditAdministration", rawObj.isCanEditAdministration());
          this.isHistoryShared = new BooleanValueModel("group." + groupId + ".isHistoryShared", rawObj.isSharedHistory());
          this.isCanEditAdmins = new BooleanValueModel("group." + groupId + ".isCanEditAdmins", rawObj.isCanEditAdmins());
          this.isCanViewAdmins = new BooleanValueModel("group." + groupId + ".isCanViewAdmins", rawObj.isCanViewAdmins());
          this.isCanLeave = new BooleanValueModel("group." + groupId + ".isCanLeave", rawObj.isCanLeave());
          this.isCanDelete = new BooleanValueModel("group." + groupId + ".isCanDelete", rawObj.isCanDelete());
          this.isCanInviteViaLink = new BooleanValueModel("group." + groupId + ".isCanInviteViaLink", rawObj.isCanInviteViaLink());
          this.isCanKickInvited = new BooleanValueModel("group." + groupId + ".isCanKickInvited", rawObj.isCanKickInvited());
          this.isCanKickAnyone = new BooleanValueModel("group." + groupId + ".isCanKickAnyone", rawObj.isCanKickAnyone());
          this.isCanEditForeign = new BooleanValueModel("group." + groupId + ".isCanEditForeign", rawObj.isCanEditForeign());
          this.isCanDeleteForeign = new BooleanValueModel("group." + groupId + ".isCanDeleteForeign", rawObj.isCanDeleteForeign());
          this.isDeleted = new BooleanValueModel("group." + groupId + ".isDeleted", rawObj.isDeleted());
          this.isCanClear = new BooleanValueModel("group." + groupId + ".isCanClear", rawObj.isCanClear());
          this.isCanJoin = new BooleanValueModel("group." + groupId + ".isCanJoin", rawObj.isCanJoin());
          this.isCanViewInfo = new BooleanValueModel("group." + groupId + ".isCanViewInfo", rawObj.isCanViewInfo());
  
          this.ownerId = new IntValueModel("group." + groupId + ".membersCount", rawObj.getOwnerId());
          this.members = new ValueModel<>("group." + groupId + ".members", new HashSet<>(rawObj.getMembers()));
          this.presence = new ValueModel<>("group." + groupId + ".presence", 0);
          this.theme = new StringValueModel("group." + groupId + ".theme", rawObj.getTopic());
          this.about = new StringValueModel("group." + groupId + ".about", rawObj.getAbout());
          this.shortName = new StringValueModel("group." + groupId + ".shortname", rawObj.getShortName());
          this.ext = new ValueModel<>("group." + groupId + ".ext", rawObj.getExt());
      }
  
      /**
       * Get Group Id
       *
       * @return Group Id
       */
      @ObjectiveCName("getId")
      public int getId() {
          return groupId;
      }
  
      /**
       * Get Group Type
       *
       * @return Group Type
       */
      @NotNull
      @ObjectiveCName("getGroupType")
      public GroupType getGroupType() {
          return groupType;
      }
  
      /**
       * Get Name Value Model
       *
       * @return Value Model of String
       */
      @NotNull
      @ObjectiveCName("getNameModel")
      public StringValueModel getName() {
          return name;
      }
  
      /**
       * Get Avatar Value Model
       *
       * @return Value Model of Avatar
       */
      @NotNull
      @ObjectiveCName("getAvatarModel")
      public AvatarValueModel getAvatar() {
          return avatar;
      }
  
      /**
       * Get About Value Model
       *
       * @return Value Model of String
       */
      @NotNull
      @ObjectiveCName("getAboutModel")
      public StringValueModel getAbout() {
          return about;
      }
  
      /**
       * Get Theme Value Model
       *
       * @return Value Model of String
       */
      @NotNull
      @ObjectiveCName("getThemeModel")
      public StringValueModel getTheme() {
          return theme;
      }
  
      /**
       * Get Short Name Model
       *
       * @return Value Model of String
       */
      @NotNull
      @ObjectiveCName("getShortNameModel")
      public StringValueModel getShortName() {
          return shortName;
      }
  
      /**
       * Get membership Value Model
       *
       * @return Value Model of Boolean
       */
      @NotNull
      @ObjectiveCName("isMemberModel")
      public BooleanValueModel isMember() {
          return isMember;
      }
  
      /**
       * Get Group members count
       *
       * @return members count
       */
      @NotNull
      @ObjectiveCName("getMembersCountModel")
      public IntValueModel getMembersCount() {
          return membersCount;
      }
  
      /**
       * Can current user write message to a group
       *
       * @return can write message model
       */
      @NotNull
      @ObjectiveCName("isCanWriteMessageModel")
      public BooleanValueModel getIsCanWriteMessage() {
          return isCanWriteMessage;
      }
  
      /**
       * Can current user view members of a group
       *
       * @return can view members model
       */
      @NotNull
      @ObjectiveCName("getIsCanViewMembersModel")
      public BooleanValueModel getIsCanViewMembers() {
          return isCanViewMembers;
      }
  
      /**
       * Can current user edit group info
       *
       * @return can edit group info
       */
      @NotNull
      @ObjectiveCName("isCanEditInfoModel")
      public BooleanValueModel getIsCanEditInfo() {
          return isCanEditInfo;
      }
  
      /**
       * Can current user invite members to a group
       *
       * @return can invite members model
       */
      @NotNull
      @ObjectiveCName("getIsCanInviteMembersModel")
      public BooleanValueModel getIsCanInviteMembers() {
          return isCanInviteMembers;
      }
  
  
      /**
       * Is members should be fetched async
       *
       * @return is members async model
       */
      @NotNull
      @ObjectiveCName("getIsAsyncMembersModel")
      public BooleanValueModel getIsAsyncMembers() {
          return isAsyncMembers;
      }
  
      /**
       * Is history shared in this group
       *
       * @return is history shared model
       */
      @NotNull
      @ObjectiveCName("getIsHistorySharedModel")
      public BooleanValueModel getIsHistoryShared() {
          return isHistoryShared;
      }
  
      /**
       * Is current user can edit administration settings
       *
       * @return is can edit administration model
       */
      @NotNull
      @ObjectiveCName("getIsCanEditAdministrationModel")
      public BooleanValueModel getIsCanEditAdministration() {
          return isCanEditAdministration;
      }
  
      /**
       * Is current user can leave group
       *
       * @return is current user can leave model
       */
      @NotNull
      @ObjectiveCName("getIsCanLeaveModel")
      public BooleanValueModel getIsCanLeave() {
          return isCanLeave;
      }
  
      /**
       * Is current user can delete group
       *
       * @return is current user can delete model
       */
      @NotNull
      @ObjectiveCName("getIsCanDeleteModel")
      public BooleanValueModel getIsCanDelete() {
          return isCanDelete;
      }
  
      /**
       * Is current user can call in this group
       *
       * @return is current user can call model
       */
      @NotNull
      @ObjectiveCName("getIsCanCallModel")
      public BooleanValueModel getIsCanCall() {
          return isCanCall;
      }
  
      /**
       * Is current user can invite via link
       *
       * @return is current user can invite via link model
       */
      @NotNull
      @ObjectiveCName("getIsCanInviteViaLinkModel")
      public BooleanValueModel getIsCanInviteViaLink() {
          return isCanInviteViaLink;
      }
  
      /**
       * Is current user can kick invited members
       *
       * @return is current user can kick invited model
       */
      @NotNull
      @ObjectiveCName("getIsCanKickInvitedModel")
      public BooleanValueModel getIsCanKickInvited() {
          return isCanKickInvited;
      }
  
      /**
       * Is current user can kick anyone
       *
       * @return is current user can kick anyone model
       */
      @NotNull
      @ObjectiveCName("getIsCanKickAnyoneModel")
      public BooleanValueModel getIsCanKickAnyone() {
          return isCanKickAnyone;
      }
  
      /**
       * Is current user can edit foreign messages
       *
       * @return is current user can edit foreign messages model
       */
      @NotNull
      @ObjectiveCName("getIsCanEditForeignModel")
      public BooleanValueModel getIsCanEditForeign() {
          return isCanEditForeign;
      }
  
      /**
       * Is current user can delete foreign messages
       *
       * @return is current user can delete foreign messages model
       */
      @NotNull
      @ObjectiveCName("getIsCanDeleteForeignModel")
      public BooleanValueModel getIsCanDeleteForeign() {
          return isCanDeleteForeign;
      }
  
      /**
       * Is current user can clear messages
       *
       * @return is current user can clear messages model
       */
      @NotNull
      @ObjectiveCName("getIsCanClearModel")
      public BooleanValueModel getIsCanClear() {
          return isCanClear;
      }
  
      /**
       * Is current user can view info
       *
       * @return is current user can view info model
       */
      @NotNull
      @ObjectiveCName("getIsCanViewInfoModel")
      public BooleanValueModel getIsCanViewInfo() {
          return isCanViewInfo;
      }
  
      /**
       * Is current user can join
       *
       * @return is current user can join model
       */
      @NotNull
      public BooleanValueModel getIsCanJoin() {
          return isCanJoin;
      }
  
      /**
       * Is current user can edit admins
       *
       * @return is current user can edit admins
       */
      @NotNull
      public BooleanValueModel getIsCanEditAdmins() {
          return isCanEditAdmins;
      }
  
      /**
       * Is group deleted
       *
       * @return is this group deleted model
       */
      @NotNull
      @ObjectiveCName("getIsDeletedModel")
      public BooleanValueModel getIsDeleted() {
          return isDeleted;
      }
  
      /**
       * Get Group owner user id model
       *
       * @return creator owner id model
       */
      @ObjectiveCName("getCreatorIdModel")
      public IntValueModel getOwnerId() {
          return ownerId;
      }
  
      /**
       * Get members Value Model
       *
       * @return Value Model of HashSet of GroupMember
       */
      @NotNull
      @ObjectiveCName("getMembersModel")
      public ValueModel<HashSet<GroupMember>> getMembers() {
          return members;
      }
  
      /**
       * Get Online Value Model
       *
       * @return Value Model of Integer
       */
      @NotNull
      @ObjectiveCName("getPresenceModel")
      public ValueModel<Integer> getPresence() {
          return presence;
      }
  
      /**
       * Get ext Value Model
       *
       * @return Value Model of ext
       */
      @NotNull
      public ValueModel<ApiMapValue> getExt() {
          return ext;
      }
  
      /**
       * Subscribe for GroupVM updates
       *
       * @param listener Listener for updates
       */
      @MainThread
      @ObjectiveCName("subscribeWithListener:")
      public synchronized void subscribe(@NotNull ModelChangedListener<GroupVM> listener) {
          if (listeners.contains(listener)) {
              return;
          }
          listeners.add(listener);
          listener.onChanged(this);
      }
  
      /**
       * Subscribe for GroupVM updates
       *
       * @param listener Listener for updates
       */
      @MainThread
      @ObjectiveCName("subscribeWithListener:withNotify:")
      public synchronized void subscribe(@NotNull ModelChangedListener<GroupVM> listener, boolean notify) {
          if (listeners.contains(listener)) {
              return;
          }
          listeners.add(listener);
          if (notify) {
              listener.onChanged(this);
          }
      }
  
      /**
       * Unsubscribe from GroupVM updates
       *
       * @param listener Listener for updates
       */
      @MainThread
      @ObjectiveCName("unsubscribeWithListener:")
      public synchronized void unsubscribe(@NotNull ModelChangedListener<GroupVM> listener) {
          listeners.remove(listener);
      }
  
      //
      // Update handling
      //
  
      @Override
      protected void updateValues(@NotNull Group rawObj) {
          boolean isChanged = name.change(rawObj.getTitle());
  
          isChanged |= avatar.change(rawObj.getAvatar());
          isChanged |= membersCount.change(rawObj.getMembersCount());
          isChanged |= isMember.change(rawObj.isMember());
          isChanged |= isCanWriteMessage.change(rawObj.isCanSendMessage());
  
          isChanged |= theme.change(rawObj.getTopic());
          isChanged |= about.change(rawObj.getAbout());
          isChanged |= members.change(new HashSet<>(rawObj.getMembers()));
          isChanged |= ownerId.change(rawObj.getOwnerId());
          isChanged |= isCanViewMembers.change(rawObj.isCanViewMembers());
          isChanged |= isCanInviteMembers.change(rawObj.isCanInviteMembers());
          isChanged |= isCanEditInfo.change(rawObj.isCanEditInfo());
          isChanged |= shortName.change(rawObj.getShortName());
          isChanged |= isAsyncMembers.change(rawObj.isAsyncMembers());
          isChanged |= isHistoryShared.change(rawObj.isSharedHistory());
          isChanged |= isCanEditAdministration.change(rawObj.isCanEditAdministration());
          isChanged |= isCanEditAdmins.change(rawObj.isCanEditAdmins());
          isChanged |= isCanViewAdmins.change(rawObj.isCanViewAdmins());
          isChanged |= isCanLeave.change(rawObj.isCanLeave());
          isChanged |= isCanDelete.change(rawObj.isCanDelete());
          isChanged |= isCanInviteViaLink.change(rawObj.isCanInviteViaLink());
          isChanged |= isCanKickInvited.change(rawObj.isCanKickInvited());
          isChanged |= isCanKickAnyone.change(rawObj.isCanKickAnyone());
          isChanged |= isCanEditForeign.change(rawObj.isCanEditForeign());
          isChanged |= isCanDeleteForeign.change(rawObj.isCanDeleteForeign());
          isChanged |= isDeleted.change(rawObj.isDeleted());
          isChanged |= isCanClear.change(rawObj.isCanClear());
          isChanged |= isCanViewInfo.change(rawObj.isCanViewInfo());
          isChanged |= isCanJoin.change(rawObj.isCanJoin());
          isChanged |= isCanCall.change(rawObj.isCanCall());
          isChanged |= ext.change(rawObj.getExt());
  
          if (isChanged) {
              notifyIfNeeded();
          }
      }
  
      private synchronized void notifyIfNeeded() {
          if (listeners.size() > 0) {
              notifyChange();
          }
      }
  
      private void notifyChange() {
          im.actor.runtime.Runtime.postToMainThread(() -> {
              for (ModelChangedListener<GroupVM> l : listeners) {
                  l.onChanged(GroupVM.this);
              }
          });
      }
  }