content: package im.actor.sdk.controllers;
  
  import android.text.SpannableStringBuilder;
  import android.text.Spanned;
  import android.text.style.ForegroundColorSpan;
  import android.view.View;
  import android.widget.TextView;
  
  import java.util.ArrayList;
  import java.util.HashSet;
  
  import im.actor.core.entity.Avatar;
  import im.actor.core.entity.GroupMember;
  import im.actor.core.viewmodel.GroupVM;
  import im.actor.core.viewmodel.UserPresence;
  import im.actor.core.viewmodel.UserVM;
  import im.actor.runtime.mvvm.ValueDoubleListener;
  import im.actor.runtime.mvvm.ValueListener;
  import im.actor.runtime.mvvm.ValueModel;
  import im.actor.sdk.ActorSDK;
  import im.actor.sdk.R;
  import im.actor.sdk.view.avatar.AvatarView;
  import im.actor.sdk.view.avatar.CoverAvatarView;
  import im.actor.runtime.mvvm.Value;
  import im.actor.runtime.mvvm.ValueChangedListener;
  import im.actor.runtime.mvvm.ValueDoubleChangedListener;
  import im.actor.runtime.mvvm.ValueTripleChangedListener;
  
  import static im.actor.sdk.util.ActorSDKMessenger.messenger;
  import static im.actor.sdk.util.ActorSDKMessenger.users;
  
  public class ActorBinder {
  
      private ArrayList<Binding> bindings = new ArrayList<Binding>();
  
      public void bind(final TextView textView, Value<String> value) {
          bind(value, new ValueChangedListener<String>() {
              @Override
              public void onChanged(String val, Value<String> Value) {
                  textView.setText(val);
              }
          });
  
      }
  
      public void bindGlobalCounter(ValueChangedListener<Integer> callback) {
          callback.onChanged(messenger().getGlobalState().getGlobalCounter().get(), messenger().getGlobalState().getGlobalCounter());
          bind(messenger().getGlobalState().getGlobalCounter(), callback);
      }
  
      public void bindGroupTyping(final TextView textView, final View container, final View titleContainer, final Value<int[]> typing) {
          bind(typing, new ValueChangedListener<int[]>() {
              @Override
              public void onChanged(int[] val, Value<int[]> Value) {
                  if (val.length == 0) {
                      container.setVisibility(View.INVISIBLE);
                      titleContainer.setVisibility(View.VISIBLE);
                  } else {
                      if (val.length == 1) {
                          textView.setText(messenger().getFormatter().formatTyping(users().get(val[0]).getName().get()));
                      } else {
                          textView.setText(messenger().getFormatter().formatTyping(val.length));
                      }
                      container.setVisibility(View.VISIBLE);
                      titleContainer.setVisibility(View.INVISIBLE);
                  }
              }
          });
      }
  
      public void bindPrivateTyping(final TextView textView, final View container, final View titleContainer,
                                    final Value<Boolean> typing) {
          bind(typing, new ValueChangedListener<Boolean>() {
              @Override
              public void onChanged(Boolean val, Value<Boolean> Value) {
                  if (val) {
                      textView.setText(messenger().getFormatter().formatTyping());
                      container.setVisibility(View.VISIBLE);
                      titleContainer.setVisibility(View.INVISIBLE);
                  } else {
                      container.setVisibility(View.INVISIBLE);
                      titleContainer.setVisibility(View.VISIBLE);
                  }
              }
          });
      }
  
      public void bind(final TextView textView, final View container, final Value<String> value) {
          bind(textView, container, value, null, true, "");
      }
  
      public void bind(final TextView textView, final View container, final Value<String> value, final OnChangedListener callback, final boolean hide, final String defaultValue) {
          bind(value, new ValueChangedListener<String>() {
              @Override
              public void onChanged(String val, Value<String> Value) {
  
                  if (val != null) {
                      if (hide) {
                          container.setVisibility(View.VISIBLE);
                      }
                      textView.setText(val);
                  } else {
                      if (hide) {
                          container.setVisibility(View.GONE);
                      }
                      textView.setText(defaultValue);
                  }
                  if (callback != null) {
                      callback.onChanged(val);
                  }
              }
          });
      }
  
      public Binding bind(final TextView textView, final UserVM user) {
          return bind(user.getPresence(), new ValueChangedListener<UserPresence>() {
              @Override
              public void onChanged(UserPresence val, Value<UserPresence> Value) {
                  String s = messenger().getFormatter().formatPresence(val, user.getSex());
                  if (s == null) {
                      s = "";
                  }
  
                  if (user.isBot()) {
                      s = textView.getContext().getString(R.string.members_adapter_bot_online_status);
                  }
                  textView.setText(s);
              }
          });
      }
  
      public Binding bindOnline(final TextView textView, final UserVM user) {
          return bind(user.getPresence(), new ValueChangedListener<UserPresence>() {
              @Override
              public void onChanged(UserPresence val, Value<UserPresence> Value) {
                  String s = messenger().getFormatter().formatPresence(val, user.getSex());
                  if (s == null) {
                      s = "";
                  }
  
                  if (val.getState().equals(UserPresence.State.ONLINE)) {
                      textView.setTextColor(ActorSDK.sharedActor().style.getGroupOnlineColor());
                      s = "\u25CF".concat(s);
                  } else {
                      textView.setTextColor(ActorSDK.sharedActor().style.getTextSecondaryColor());
                  }
                  if (user.isBot()) {
                      s = textView.getContext().getString(R.string.members_adapter_bot_online_status);
                  }
                  textView.setText(s);
              }
          });
      }
  
  
      public Binding bind(final View v, ValueModel<UserPresence> up) {
          return bind(up, new ValueChangedListener<UserPresence>() {
  
              @Override
              public void onChanged(UserPresence val, Value<UserPresence> valueModel) {
                  if (val.getState().equals(UserPresence.State.ONLINE)) {
                      v.setVisibility(View.VISIBLE);
                  } else {
                      v.setVisibility(View.GONE);
                  }
              }
          });
      }
  
      public Binding bind(final OnChangedListener<Boolean> callback, ValueModel<UserPresence> up) {
          return bind(up, new ValueChangedListener<UserPresence>() {
              @Override
              public void onChanged(UserPresence val, Value<UserPresence> valueModel) {
                  callback.onChanged(val.getState().equals(UserPresence.State.ONLINE));
              }
          });
      }
  
      public void bind(final TextView textView, final View titleContainer, final GroupVM value) {
          bind(value.getPresence(), value.getMembersCount(), value.isMember(), (online, onlineModel, membersCount, membersModel, isMember, isMemberModel) -> {
              if (isMember) {
                  titleContainer.setVisibility(View.VISIBLE);
                  if (online <= 0) {
                      SpannableStringBuilder builder = new SpannableStringBuilder(
                              messenger().getFormatter().formatGroupMembers(membersCount));
                      builder.setSpan(new ForegroundColorSpan(0xB7ffffff), 0, builder.length(),
                              Spanned.SPAN_INCLUSIVE_EXCLUSIVE);
                      textView.setText(builder);
                  } else {
                      SpannableStringBuilder builder = new SpannableStringBuilder(
                              messenger().getFormatter().formatGroupMembers(membersCount) + ", ");
                      builder.setSpan(new ForegroundColorSpan(0xB7ffffff), 0, builder.length(),
                              Spanned.SPAN_INCLUSIVE_EXCLUSIVE);
                      builder.append(messenger().getFormatter().formatGroupOnline(online));
                      textView.setText(builder);
                  }
              } else {
                  titleContainer.setVisibility(View.GONE);
              }
          });
      }
  
      public void bind(final AvatarView avatarView, final int id,
                       final Value<Avatar> avatar, final Value<String> name) {
          bind(avatar, name, new ValueDoubleChangedListener<Avatar, String>() {
              @Override
              public void onChanged(Avatar val, Value<Avatar> Value, String val2, Value<String> Value2) {
                  avatarView.bind(val, val2, id);
              }
          });
      }
  
      public void bind(final CoverAvatarView avatarView, final Value<Avatar> avatar) {
          bind(avatar, new ValueChangedListener<Avatar>() {
              @Override
              public void onChanged(Avatar val, Value<Avatar> Value) {
                  if (val != null) {
                      avatarView.bind(val);
                  } else {
                      avatarView.unbind();
                      avatarView.setImageURI(null);
                  }
              }
          });
      }
  
      public <T> Binding bind(Value<T> value, ValueChangedListener<T> listener) {
          value.subscribe(listener);
          Binding b = new Binding(value, listener);
          bindings.add(b);
          return b;
      }
  
      public <T> Binding bind(Value<T> value, ValueListener<T> listener) {
          return bind(value, (val, valueModel) -> {
              listener.onChanged(val);
          });
      }
  
      public <T1, T2> void bind(Value<T1> value1, Value<T2> value2, ValueDoubleListener<T1, T2> listener) {
          bind(value1, value2, (val, valueModel, val2, valueModel2) -> {
              listener.onChanged(val, val2);
          });
      }
  
      public <T> Binding bind(Value<T> value, ValueChangedListener<T> listener, boolean notify) {
          value.subscribe(listener, notify);
          Binding b = new Binding(value, listener);
          bindings.add(b);
          return b;
      }
  
      public <T> Binding bind(Value<T> value, boolean notify, ValueChangedListener<T> listener) {
          value.subscribe(listener, notify);
          Binding binding = new Binding(value, listener);
          bindings.add(binding);
          return binding;
      }
  
      public <T, V> Binding[] bind(final Value<T> value1, final Value<V> value2,
                              final ValueDoubleChangedListener<T, V> listener) {
  
          Binding[] bindings = new Binding[2];
          bindings[0] = bind(value1, false, new ValueChangedListener<T>() {
              @Override
              public void onChanged(T val, Value<T> Value) {
                  listener.onChanged(val, Value, value2.get(), value2);
              }
          });
          bindings[1] = bind(value2, false, new ValueChangedListener<V>() {
              @Override
              public void onChanged(V val, Value<V> Value) {
                  listener.onChanged(value1.get(), value1, val, Value);
              }
          });
          listener.onChanged(value1.get(), value1, value2.get(), value2);
          return bindings;
      }
  
      public <T, V, S> void bind(final Value<T> value1, final Value<V> value2, final Value<S> value3,
                                 final ValueTripleChangedListener<T, V, S> listener) {
          bind(value1, false, new ValueChangedListener<T>() {
              @Override
              public void onChanged(T val, Value<T> Value) {
                  listener.onChanged(val, Value, value2.get(), value2, value3.get(), value3);
              }
          });
          bind(value2, false, new ValueChangedListener<V>() {
              @Override
              public void onChanged(V val, Value<V> Value) {
                  listener.onChanged(value1.get(), value1, val, Value, value3.get(), value3);
              }
          });
          bind(value3, false, new ValueChangedListener<S>() {
              @Override
              public void onChanged(S val, Value<S> Value) {
                  listener.onChanged(value1.get(), value1, value2.get(), value2, val, Value);
              }
          });
          listener.onChanged(value1.get(), value1, value2.get(), value2, value3.get(), value3);
      }
  
      public void unbindAll() {
          for (Binding b : bindings) {
              b.unbind();
          }
          bindings.clear();
      }
  
      public interface OnChangedListener<T> {
          void onChanged(T val);
      }
  
      public void unbind(Binding b) {
          b.unbind();
          bindings.remove(b);
      }
  
      public class Binding {
          private Value model;
          private ValueChangedListener listener;
  
          private Binding(Value model, ValueChangedListener listener) {
              this.model = model;
              this.listener = listener;
          }
  
          public void unbind() {
              model.unsubscribe(listener);
          }
      }
  }
  