content: package im.actor.sdk.controllers.conversation;
  
  import android.app.Activity;
  import android.content.res.Configuration;
  import android.os.Bundle;
  import android.support.annotation.Nullable;
  import android.support.v4.app.Fragment;
  import android.view.LayoutInflater;
  import android.view.View;
  import android.view.ViewGroup;
  import android.widget.TextView;
  import android.widget.Toast;
  
  import im.actor.core.entity.GroupType;
  import im.actor.core.entity.Peer;
  import im.actor.core.entity.PeerType;
  import im.actor.core.entity.Sticker;
  import im.actor.core.network.RpcException;
  import im.actor.core.viewmodel.ConversationVM;
  import im.actor.core.viewmodel.GroupVM;
  import im.actor.core.viewmodel.UserVM;
  import im.actor.runtime.mvvm.Value;
  import im.actor.runtime.mvvm.ValueChangedListener;
  import im.actor.runtime.mvvm.ValueDoubleChangedListener;
  import im.actor.runtime.mvvm.ValueListener;
  import im.actor.sdk.ActorSDK;
  import im.actor.sdk.ActorSDKLauncher;
  import im.actor.sdk.R;
  import im.actor.sdk.controllers.BaseFragment;
  import im.actor.sdk.controllers.conversation.attach.AbsAttachFragment;
  import im.actor.sdk.controllers.conversation.inputbar.InputBarCallback;
  import im.actor.sdk.controllers.conversation.inputbar.InputBarFragment;
  import im.actor.sdk.controllers.conversation.mentions.AutocompleteCallback;
  import im.actor.sdk.controllers.conversation.mentions.AutocompleteFragment;
  import im.actor.sdk.controllers.conversation.messages.MessagesDefaultFragment;
  import im.actor.sdk.controllers.conversation.messages.MessagesFragmentCallback;
  import im.actor.sdk.controllers.conversation.placeholder.EmptyChatPlaceholder;
  import im.actor.sdk.controllers.conversation.quote.QuoteCallback;
  import im.actor.sdk.controllers.conversation.quote.QuoteFragment;
  import im.actor.sdk.controllers.conversation.attach.AttachFragment;
  import im.actor.sdk.controllers.conversation.toolbar.ChatToolbarFragment;
  
  import static im.actor.sdk.util.ActorSDKMessenger.groups;
  import static im.actor.sdk.util.ActorSDKMessenger.messenger;
  import static im.actor.sdk.util.ActorSDKMessenger.users;
  
  public class ChatFragment extends BaseFragment implements InputBarCallback, MessagesFragmentCallback, QuoteCallback,
          AutocompleteCallback {
  
      public static ChatFragment create(Peer peer) {
          ChatFragment res = new ChatFragment();
          Bundle bundle = new Bundle();
          bundle.putLong("peer", peer.getUnuqueId());
          res.setArguments(bundle);
          return res;
      }
  
      private View quoteContainer;
      private View inputContainer;
      private View inputOverlayContainer;
      private View emptyContainer;
      private TextView inputOverlayText;
      private Peer peer;
      private long editRid;
      private String currentQuote;
  
      public ChatFragment() {
          setUnbindOnPause(true);
      }
  
      @Override
      public void onCreate(Bundle saveInstance) {
          super.onCreate(saveInstance);
  
          peer = Peer.fromUniqueId(getArguments().getLong("peer"));
      }
  
      @Nullable
      @Override
      public View onCreateView(LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) {
  
          View res = inflater.inflate(R.layout.fragment_chat, container, false);
  
          quoteContainer = res.findViewById(R.id.quoteFragment);
  
          res.findViewById(R.id.sendContainer).setBackgroundColor(style.getMainBackgroundColor());
          inputContainer = res.findViewById(R.id.sendFragment);
          inputOverlayContainer = res.findViewById(R.id.inputOverlay);
          inputOverlayContainer.setBackgroundColor(style.getMainBackgroundColor());
          inputOverlayText = (TextView) res.findViewById(R.id.overlayText);
          inputOverlayText.setOnClickListener(view -> onOverlayPressed());
          inputOverlayContainer.setVisibility(View.GONE);
          emptyContainer = res.findViewById(R.id.emptyPlaceholder);
          emptyContainer.setVisibility(View.GONE);
  
          if (savedInstanceState == null) {
              Fragment toolbarFragment = ActorSDK.sharedActor().getDelegate().fragmentForToolbar(peer);
              if (toolbarFragment == null) {
                  toolbarFragment = new ChatToolbarFragment(peer);
              }
              InputBarFragment inputBarFragment = ActorSDK.sharedActor().getDelegate().fragmentForChatInput();
              if (inputBarFragment == null) {
                  inputBarFragment = new InputBarFragment();
              }
  
              AutocompleteFragment autocompleteFragment = ActorSDK.sharedActor().getDelegate().fragmentForAutocomplete(peer);
              if (autocompleteFragment == null) {
                  autocompleteFragment = AutocompleteFragment.create(peer);
                  autocompleteFragment.setUnderlyingView(res.findViewById(R.id.messagesFragment));
              }
  
              QuoteFragment quoteFragment = ActorSDK.sharedActor().getDelegate().fragmentForQuote();
              if (quoteFragment == null) {
                  quoteFragment = new QuoteFragment();
              }
              MessagesDefaultFragment messagesDefaultFragment = MessagesDefaultFragment.create(peer);
              messagesDefaultFragment.setNewMessageListener(inputBarFragment);
              getChildFragmentManager().beginTransaction()
                      .add(toolbarFragment, "toolbar")
                      .add(R.id.messagesFragment, messagesDefaultFragment)
                      .add(R.id.sendFragment, inputBarFragment)
                      .add(R.id.quoteFragment, quoteFragment)
                      .add(R.id.emptyPlaceholder, new EmptyChatPlaceholder())
                      .add(R.id.autocompleteContainer, autocompleteFragment)
                      .commitNow();
  
              AbsAttachFragment fragment = ActorSDK.sharedActor().getDelegate().fragmentForAttachMenu(peer);
              if (fragment == null) {
                  fragment = new AttachFragment(peer);
              }
              getFragmentManager().beginTransaction()
                      .add(R.id.overlay, fragment)
                      .commit();
          }
  
          return res;
      }
  
      @Override
      public void onResume() {
          super.onResume();
          findInputBar().setText(messenger().loadDraft(peer), true);
  
          if (peer.getPeerType() == PeerType.PRIVATE) {
              UserVM userVM = users().get(peer.getPeerId());
  
              if (userVM.isBot()) {
                  ConversationVM conversationVM = messenger().getConversationVM(peer);
                  bind(conversationVM.getIsEmpty(), conversationVM.getIsLoaded(), (isEmpty, valueModel, isLoaded, valueModel2) -> {
                      if (isEmpty) {
                          inputOverlayText.setText(R.string.chat_empty_bot);
                          inputOverlayText.setTextColor(style.getListActionColor());
                          inputOverlayText.setClickable(true);
                          showView(inputOverlayContainer, false);
                          goneView(inputContainer, false);
                      } else {
                          goneView(inputOverlayContainer, false);
                          showView(inputContainer, false);
                      }
  
                      if (isEmpty && isLoaded) {
                          showView(emptyContainer, false);
                      } else {
                          hideView(emptyContainer, false);
                      }
                  });
                  bind(userVM.getAbout(), (val, valueModel) -> {
                      if (val == null) {
                          findEmptyPlaceholder().setText(getString(R.string.chat_empty_bot_about));
                      } else {
                          findEmptyPlaceholder().setText(val);
                      }
                  });
              } else {
                  bind(userVM.getIsBlocked(), (val, valueModel) -> {
                      if (val) {
                          inputOverlayText.setText(R.string.blocked_unblock);
                          inputOverlayText.setTextColor(style.getListActionColor());
                          inputOverlayText.setClickable(true);
                          showView(inputOverlayContainer, false);
                          goneView(inputContainer, false);
                      } else {
                          goneView(inputOverlayContainer, false);
                          showView(inputContainer, false);
                      }
                  });
              }
          } else if (peer.getPeerType() == PeerType.GROUP) {
              GroupVM groupVM = groups().get(peer.getPeerId());
              bind(groupVM.isMember(), groupVM.getIsCanWriteMessage(), (isMember, valueModel, canWriteMessage, valueModel2) -> {
                  if (canWriteMessage) {
                      goneView(inputOverlayContainer, false);
                      showView(inputContainer, false);
                  } else if (isMember) {
                      if (messenger().isNotificationsEnabled(peer)) {
                          inputOverlayText.setText(getString(R.string.chat_mute));
                      } else {
                          inputOverlayText.setText(getString(R.string.chat_unmute));
                      }
                      inputOverlayText.setTextColor(style.getListActionColor());
                      inputOverlayText.setClickable(true);
                      inputOverlayText.setEnabled(true);
                      showView(inputOverlayContainer, false);
                      goneView(inputContainer, false);
                  } else if (groupVM.getIsCanJoin().get()) {
                      inputOverlayText.setText(getString(R.string.join));
                      inputOverlayText.setTextColor(style.getListActionColor());
                      inputOverlayText.setClickable(true);
                      inputOverlayText.setEnabled(true);
                      showView(inputOverlayContainer, false);
                      goneView(inputContainer, false);
                  } else if (groupVM.getIsDeleted().get()) {
                      inputOverlayText.setText(groupVM.getGroupType() == GroupType.CHANNEL ? R.string.channel_deleted : R.string.group_deleted);
                      inputOverlayText.setTextColor(style.getListActionColor());
                      inputOverlayText.setClickable(false);
                      inputOverlayText.setEnabled(false);
                      showView(inputOverlayContainer, false);
                      goneView(inputContainer, false);
                  } else {
                      inputOverlayText.setText(R.string.chat_not_member);
                      inputOverlayText.setTextColor(style.getListActionColor());
                      inputOverlayText.setClickable(false);
                      inputOverlayText.setEnabled(false);
                      showView(inputOverlayContainer, false);
                      goneView(inputContainer, false);
                  }
              });
          }
      }
  
      public void onOverlayPressed() {
          if (peer.getPeerType() == PeerType.PRIVATE) {
              UserVM userVM = users().get(peer.getPeerId());
              if (userVM.isBot()) {
                  messenger().sendMessage(peer, "/start");
              } else {
                  if (userVM.getIsBlocked().get()) {
                      execute(messenger().unblockUser(userVM.getId()));
                  }
              }
          } else if (peer.getPeerType() == PeerType.GROUP) {
              GroupVM groupVM = groups().get(peer.getPeerId());
              if (groupVM.isMember().get()) {
                  if (messenger().isNotificationsEnabled(peer)) {
                      messenger().changeNotificationsEnabled(peer, false);
                      inputOverlayText.setText(getString(R.string.chat_unmute));
                  } else {
                      messenger().changeNotificationsEnabled(peer, true);
                      inputOverlayText.setText(getString(R.string.chat_mute));
                  }
              } else if (groupVM.getIsCanJoin().get()) {
                  messenger().joinGroup(groupVM.getId());
              } else {
                  // TODO: Rejoin
              }
          }
      }
  
      public boolean onBackPressed() {
  
          // Share Menu
          AbsAttachFragment attachFragment = findShareFragment();
          if (attachFragment != null) {
              if (attachFragment.onBackPressed()) {
                  return true;
              }
          }
  
          if (findInputBar().onBackPressed()) {
              return true;
          }
  
          // Message Edit
          if (editRid != 0) {
              editRid = 0;
              findInputBar().setText("");
              hideQuote();
              return true;
          }
  
          // Message Quoting
          if (currentQuote != null) {
              currentQuote = null;
              hideQuote();
              return true;
          }
  
          return false;
      }
  
      @Override
      public void onTextFocusChanged(boolean isFocused) {
  
      }
  
      @Override
      public void onTyping() {
          messenger().onTyping(peer);
      }
  
      @Override
      public void onTextChanged(String text) {
  
      }
  
      @Override
      public void onAutoCompleteWordChanged(String text) {
          findAutocomplete().onCurrentWordChanged(text);
      }
  
      @Override
      public void onTextSent(String sequence) {
          if (sequence.trim().length() > 0) {
              if (editRid == 0) {
                  if (currentQuote != null) {
                      messenger().sendMessage(peer, currentQuote + sequence);
                      currentQuote = null;
                      hideQuote();
                  } else {
                      messenger().sendMessage(peer, sequence);
                  }
              } else {
                  messenger().updateMessage(peer, sequence, editRid).failure(e -> {
                      Activity activity = getActivity();
                      if (activity != null) {
                          RpcException re = (RpcException) e;
                          String error;
                          if (re.getTag().equals("NOT_IN_TIME_WINDOW")) {
                              error = getString(R.string.edit_message_error_slowpoke);
                          } else if (re.getTag().equals("NOT_LAST_MESSAGE")) {
                              error = getString(R.string.edit_message_error_not_last);
                          } else {
                              error = re.getMessage();
                          }
                          Toast.makeText(activity, error, Toast.LENGTH_LONG).show();
                      }
                  });
                  editRid = 0;
                  hideQuote();
              }
              findInputBar().setText("");
          }
      }
  
      @Override
      public void onAudioSent(int duration, String descriptor) {
          messenger().sendVoice(peer, duration, descriptor);
      }
  
      @Override
      public void onStickerSent(Sticker sticker) {
          messenger().sendSticker(peer, sticker);
      }
  
      @Override
      public void onAttachPressed() {
          findInputBar().clearFocus();
  
          AbsAttachFragment attachFragment = findShareFragment();
          if (attachFragment != null) {
              quoteContainer.postDelayed(() -> attachFragment.show(), 200);
          }
      }
  
      @Override
      public void onAvatarClick(int uid) {
          ActorSDKLauncher.startProfileActivity(getActivity(), uid);
      }
  
      @Override
      public void onAvatarLongClick(int uid) {
          insertMention(uid);
      }
  
      @Override
      public void onMentionPicked(String name) {
          findInputBar().replaceCurrentWord(name);
      }
  
      @Override
      public void onCommandPicked(String command) {
          messenger().sendMessage(peer, "/" + command);
          findInputBar().setText("");
          findAutocomplete().onCurrentWordChanged("");
      }
  
      public void insertMention(int uid) {
          UserVM user = users().get(uid);
          String name = user.getName().get();
          String nick = user.getNick().get();
  
          String text = findInputBar().getText();
          if (text.length() > 0 && !text.endsWith(" ")) {
              text = text + " ";
          }
  
          String mentionString;
          if (nick != null) {
              mentionString = "@" + nick;
          } else {
              mentionString = name;
          }
          if (text.length() == 0) {
              mentionString += ": ";
          } else {
              mentionString += " ";
          }
  
          findInputBar().setText(text + mentionString);
          findInputBar().requestFocus();
      }
  
      @Override
      public void onMessageEdit(long rid, String text) {
          editRid = rid;
          findInputBar().setText(text);
          showQuote(text, false);
      }
  
      @Override
      public void onMessageQuote(String text) {
          showQuote(text, true);
          currentQuote = text;
      }
  
      @Override
      public void onQuoteCancelled() {
          if (editRid != 0) {
              editRid = 0;
              findInputBar().setText("");
          }
          currentQuote = null;
          hideQuote();
      }
  
      @Override
      public void onPause() {
          super.onPause();
          messenger().saveDraft(peer, findInputBar().getText());
      }
  
      @Override
      public void onDestroyView() {
          super.onDestroyView();
          quoteContainer = null;
      }
  
  
      //
      // Tools
      //
      private InputBarFragment findInputBar() {
          return ((InputBarFragment) getChildFragmentManager().findFragmentById(R.id.sendFragment));
      }
  
      private QuoteFragment findQuoteFragment() {
          return ((QuoteFragment) getChildFragmentManager().findFragmentById(R.id.quoteFragment));
      }
  
      private AbsAttachFragment findShareFragment() {
          return ((AbsAttachFragment) getFragmentManager().findFragmentById(R.id.overlay));
      }
  
      private EmptyChatPlaceholder findEmptyPlaceholder() {
          return ((EmptyChatPlaceholder) getChildFragmentManager().findFragmentById(R.id.emptyPlaceholder));
      }
  
      private AutocompleteFragment findAutocomplete() {
          return ((AutocompleteFragment) getChildFragmentManager().findFragmentById(R.id.autocompleteContainer));
      }
  
      private void hideQuote() {
          hideView(quoteContainer);
          findInputBar().setDisableOnEmptyText(true);
          findInputBar().setAudioEnabled(true);
          findInputBar().setAttachEnabled(true);
      }
  
      private void showQuote(String text, boolean isQuote) {
          showView(quoteContainer);
          findQuoteFragment().setText(text, isQuote);
          findInputBar().setDisableOnEmptyText(false);
          findInputBar().setAudioEnabled(false);
          findInputBar().setAttachEnabled(false);
      }
  }
  