package awais.instagrabber.adapters.viewholder.directmessages;

import android.annotation.SuppressLint;
import android.content.res.ColorStateList;
import android.content.res.Resources;
import android.graphics.Point;
import android.graphics.drawable.Drawable;
import android.view.Gravity;
import android.view.View;
import android.view.ViewConfiguration;
import android.view.ViewPropertyAnimator;
import android.view.animation.AccelerateDecelerateInterpolator;
import android.widget.FrameLayout;

import androidx.annotation.CallSuper;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.constraintlayout.widget.ConstraintLayout;
import androidx.core.widget.ImageViewCompat;
import androidx.recyclerview.widget.ItemTouchHelper;
import androidx.recyclerview.widget.RecyclerView;
import androidx.transition.TransitionManager;

import com.google.android.material.transition.MaterialFade;
import com.google.common.collect.ImmutableList;

import java.time.format.DateTimeFormatter;
import java.time.format.FormatStyle;
import java.util.LinkedList;
import java.util.List;
import java.util.stream.Collectors;

import awais.instagrabber.R;
import awais.instagrabber.adapters.DirectItemsAdapter.DirectItemCallback;
import awais.instagrabber.adapters.DirectItemsAdapter.DirectItemInternalLongClickListener;
import awais.instagrabber.customviews.DirectItemContextMenu;
import awais.instagrabber.customviews.DirectItemFrameLayout;
import awais.instagrabber.customviews.RamboTextViewV2;
import awais.instagrabber.customviews.helpers.SwipeAndRestoreItemTouchHelperCallback.SwipeableViewHolder;
import awais.instagrabber.databinding.LayoutDmBaseBinding;
import awais.instagrabber.models.enums.DirectItemType;
import awais.instagrabber.models.enums.MediaItemType;
import awais.instagrabber.repositories.responses.Media;
import awais.instagrabber.repositories.responses.User;
import awais.instagrabber.repositories.responses.directmessages.DirectItem;
import awais.instagrabber.repositories.responses.directmessages.DirectItemEmojiReaction;
import awais.instagrabber.repositories.responses.directmessages.DirectItemReactions;
import awais.instagrabber.repositories.responses.directmessages.DirectItemStoryShare;
import awais.instagrabber.repositories.responses.directmessages.DirectThread;
import awais.instagrabber.utils.DMUtils;
import awais.instagrabber.utils.DeepLinkParser;
import awais.instagrabber.utils.ResponseBodyUtils;

public abstract class DirectItemViewHolder extends RecyclerView.ViewHolder implements SwipeableViewHolder {
    private static final String TAG = DirectItemViewHolder.class.getSimpleName();
    // private static final List<Integer> THREAD_CHANGING_OPTIONS = ImmutableList.of(R.id.unsend);

    private final LayoutDmBaseBinding binding;
    private final User currentUser;
    private final DirectThread thread;
    private final int groupMessageWidth;
    private final List<Long> userIds;
    private final DirectItemCallback callback;
    private final int reactionAdjustMargin;
    private final AccelerateDecelerateInterpolator accelerateDecelerateInterpolator = new AccelerateDecelerateInterpolator();

    protected final int margin;
    protected final int dmRadius;
    protected final int dmRadiusSmall;
    protected final int messageInfoPaddingSmall;
    protected final int mediaImageMaxHeight;
    protected final int windowWidth;
    protected final int mediaImageMaxWidth;
    protected final int reactionTranslationYType1;
    protected final int reactionTranslationYType2;

    private boolean selected = false;
    private DirectItemInternalLongClickListener longClickListener;
    private DirectItem item;
    private ViewPropertyAnimator shrinkGrowAnimator;
    private MessageDirection messageDirection;
    // private View.OnLayoutChangeListener layoutChangeListener;

    public DirectItemViewHolder(@NonNull final LayoutDmBaseBinding binding,
                                @NonNull final User currentUser,
                                @NonNull final DirectThread thread,
                                @NonNull final DirectItemCallback callback) {
        super(binding.getRoot());
        this.binding = binding;
        this.currentUser = currentUser;
        this.thread = thread;
        this.callback = callback;
        userIds = thread.getUsers()
                        .stream()
                        .map(User::getPk)
                        .collect(Collectors.toList());
        binding.ivProfilePic.setVisibility(thread.isGroup() ? View.VISIBLE : View.GONE);
        binding.ivProfilePic.setOnClickListener(null);
        final Resources resources = itemView.getResources();
        margin = resources.getDimensionPixelSize(R.dimen.dm_message_item_margin);
        final int avatarSize = resources.getDimensionPixelSize(R.dimen.dm_message_item_avatar_size);
        dmRadius = resources.getDimensionPixelSize(R.dimen.dm_message_card_radius);
        dmRadiusSmall = resources.getDimensionPixelSize(R.dimen.dm_message_card_radius_small);
        messageInfoPaddingSmall = resources.getDimensionPixelSize(R.dimen.dm_message_info_padding_small);
        windowWidth = resources.getDisplayMetrics().widthPixels;
        mediaImageMaxHeight = resources.getDimensionPixelSize(R.dimen.dm_media_img_max_height);
        reactionAdjustMargin = resources.getDimensionPixelSize(R.dimen.dm_reaction_adjust_margin);
        final int groupWidthCorrection = avatarSize + messageInfoPaddingSmall * 3;
        mediaImageMaxWidth = windowWidth - margin - (thread.isGroup() ? groupWidthCorrection : messageInfoPaddingSmall * 2);
        // messageInfoPaddingSmall is used cuz it's also 4dp, 1 avatar margin + 2 paddings = 3
        groupMessageWidth = windowWidth - margin - groupWidthCorrection;
        reactionTranslationYType1 = resources.getDimensionPixelSize(R.dimen.dm_reaction_translation_y_type_1);
        reactionTranslationYType2 = resources.getDimensionPixelSize(R.dimen.dm_reaction_translation_y_type_2);
    }

    public void bind(final int position, final DirectItem item) {
        if (item == null) return;
        this.item = item;
        messageDirection = isSelf(item) ? MessageDirection.OUTGOING : MessageDirection.INCOMING;
        // Asynchronous binding causes some weird behaviour
        // itemView.post(() -> bindBase(item, messageDirection, position));
        // itemView.post(() -> bindItem(item, messageDirection));
        // itemView.post(() -> setupLongClickListener(position, messageDirection));
        bindBase(item, messageDirection, position);
        bindItem(item, messageDirection);
        setupLongClickListener(position, messageDirection);
    }

    private void bindBase(@NonNull final DirectItem item, final MessageDirection messageDirection, final int position) {
        final FrameLayout.LayoutParams containerLayoutParams = (FrameLayout.LayoutParams) binding.container.getLayoutParams();
        final DirectItemType itemType = item.getItemType() == null ? DirectItemType.UNKNOWN : item.getItemType();
        setMessageDirectionGravity(messageDirection, containerLayoutParams);
        setGroupUserDetails(item, messageDirection);
        setBackground(messageDirection);
        setMessageInfo(item, messageDirection);
        switch (itemType) {
            case REEL_SHARE:
            case STORY_SHARE: // i think they could have texts?
//                containerLayoutParams.setMarginStart(0);
//                containerLayoutParams.setMarginEnd(0);
            case TEXT:
            case LINK:
            case UNKNOWN:
                binding.messageInfo.setPadding(0, 0, dmRadius, dmRadiusSmall);
                break;
            default:
                if (showMessageInfo()) {
                    binding.messageInfo.setPadding(0, 0, messageInfoPaddingSmall, dmRadiusSmall);
                }
        }
        setupReply(item, messageDirection);
        setReactions(item, position);
        if (item.getRepliedToMessage() == null && item.getShowForwardAttribution()) {
            setForwardInfo(messageDirection);
        }
    }

    private void setBackground(final MessageDirection messageDirection) {
        if (showBackground()) {
            binding.background.setBackgroundResource(messageDirection == MessageDirection.INCOMING ? R.drawable.bg_speech_bubble_incoming
                                                                                                   : R.drawable.bg_speech_bubble_outgoing);
            return;
        }
        binding.background.setBackgroundResource(0);
    }

    private void setGroupUserDetails(final DirectItem item, final MessageDirection messageDirection) {
        if (showUserDetailsInGroup()) {
            binding.ivProfilePic.setVisibility(messageDirection == MessageDirection.INCOMING && thread.isGroup() ? View.VISIBLE : View.GONE);
            binding.tvUsername.setVisibility(messageDirection == MessageDirection.INCOMING && thread.isGroup() ? View.VISIBLE : View.GONE);
            if (messageDirection == MessageDirection.INCOMING && thread.isGroup()) {
                final List<User> allUsers = new LinkedList(thread.getUsers());
                allUsers.addAll(thread.getLeftUsers());
                final User user = getUser(item.getUserId(), allUsers);
                if (user != null) {
                    binding.tvUsername.setText(user.getUsername());
                    binding.ivProfilePic.setImageURI(user.getProfilePicUrl());
                }
                ConstraintLayout.LayoutParams layoutParams = (ConstraintLayout.LayoutParams) binding.chatMessageLayout.getLayoutParams();
                layoutParams.matchConstraintMaxWidth = groupMessageWidth;
                binding.chatMessageLayout.setLayoutParams(layoutParams);
            }
            return;
        }
        binding.ivProfilePic.setVisibility(View.GONE);
        binding.tvUsername.setVisibility(View.GONE);
    }

    private void setMessageDirectionGravity(final MessageDirection messageDirection,
                                            final FrameLayout.LayoutParams containerLayoutParams) {
        if (allowMessageDirectionGravity()) {
            containerLayoutParams.setMarginStart(messageDirection == MessageDirection.OUTGOING ? margin : 0);
            containerLayoutParams.setMarginEnd(messageDirection == MessageDirection.INCOMING ? margin : 0);
            containerLayoutParams.gravity = messageDirection == MessageDirection.INCOMING ? Gravity.START : Gravity.END;
            return;
        }
        containerLayoutParams.gravity = Gravity.CENTER;
    }

    private void setMessageInfo(@NonNull final DirectItem item, final MessageDirection messageDirection) {
        if (showMessageInfo()) {
            binding.messageInfo.setVisibility(View.VISIBLE);
            binding.deliveryStatus.setVisibility(messageDirection == MessageDirection.OUTGOING ? View.VISIBLE : View.GONE);
            if (item.getDate() != null) {
                final DateTimeFormatter dateFormatter = DateTimeFormatter.ofLocalizedTime(FormatStyle.SHORT);
                binding.messageTime.setText(dateFormatter.format(item.getDate()));
            }
            if (messageDirection == MessageDirection.OUTGOING) {
                if (item.isPending()) {
                    binding.deliveryStatus.setImageResource(R.drawable.ic_check_24);
                } else {
                    final boolean read = DMUtils.isRead(item,
                                                        thread.getLastSeenAt(),
                                                        userIds
                    );
                    binding.deliveryStatus.setImageResource(R.drawable.ic_check_all_24);
                    ImageViewCompat.setImageTintList(
                            binding.deliveryStatus,
                            ColorStateList.valueOf(itemView.getResources().getColor(read ? R.color.blue_500 : R.color.grey_500))
                    );
                }
            }
            return;
        }
        binding.messageInfo.setVisibility(View.GONE);
    }

    private void setupReply(final DirectItem item, final MessageDirection messageDirection) {
        if (item.getRepliedToMessage() != null) {
            final List<User> allUsers = new LinkedList(thread.getUsers());
            allUsers.addAll(thread.getLeftUsers());
            setReply(item, messageDirection, allUsers);
        } else {
            binding.quoteLine.setVisibility(View.GONE);
            binding.replyContainer.setVisibility(View.GONE);
            binding.replyInfo.setVisibility(View.GONE);
        }
    }

    private void setReply(final DirectItem item,
                          final MessageDirection messageDirection,
                          final List<User> users) {
        final DirectItem replied = item.getRepliedToMessage();
        final DirectItemType itemType = replied.getItemType();
        final Resources resources = itemView.getResources();
        String text = null;
        String url = null;
        switch (itemType) {
            case TEXT:
                text = replied.getText();
                break;
            case LINK:
                text = replied.getLink().getText();
                break;
            case PLACEHOLDER:
                text = replied.getPlaceholder().getMessage();
                break;
            case MEDIA:
                url = ResponseBodyUtils.getThumbUrl(replied.getMedia());
                break;
            case RAVEN_MEDIA:
                url = ResponseBodyUtils.getThumbUrl(replied.getVisualMedia().getMedia());
                break;
            case VOICE_MEDIA:
                text = resources.getString(R.string.voice_message);
                break;
            case MEDIA_SHARE:
                Media mediaShare = replied.getMediaShare();
                if (mediaShare.getType() == MediaItemType.MEDIA_TYPE_SLIDER) {
                    mediaShare = mediaShare.getCarouselMedia().get(0);
                }
                url = ResponseBodyUtils.getThumbUrl(mediaShare);
                break;
            case REEL_SHARE:
                text = replied.getReelShare().getText();
                break;
            // Below types cannot be replied to
            // case LIKE:
            //     text = "❤️";
            //     break;
            // case PROFILE:
            //     text = "@" + replied.getProfile().getUsername();
            //     break;
            // case CLIP:
            //     url = ResponseBodyUtils.getThumbUrl(replied.getClip().getClip().getImageVersions2());
            //     break;
            // case FELIX_SHARE:
            //     url = ResponseBodyUtils.getThumbUrl(replied.getFelixShare().getVideo().getImageVersions2());
            //     break;
            // case STORY_SHARE:
            //     final DirectItemMedia media = replied.getStoryShare().getMedia();
            //     if (media == null) break;
            //     url = ResponseBodyUtils.getThumbUrl(media.getImageVersions2());
            //     break;
            // case LOCATION
        }
        if (text == null && url == null) {
            binding.quoteLine.setVisibility(View.GONE);
            binding.replyContainer.setVisibility(View.GONE);
            binding.replyInfo.setVisibility(View.GONE);
            return;
        }
        setReplyGravity(messageDirection);
        final String info = setReplyInfo(item, replied, users, resources);
        binding.replyInfo.setVisibility(View.VISIBLE);
        binding.replyInfo.setText(info);
        binding.quoteLine.setVisibility(View.VISIBLE);
        binding.replyContainer.setVisibility(View.VISIBLE);
        if (url != null) {
            binding.replyText.setVisibility(View.GONE);
            binding.replyImage.setVisibility(View.VISIBLE);
            binding.replyImage.setImageURI(url);
            return;
        }
        binding.replyImage.setVisibility(View.GONE);
        final Drawable background = binding.replyText.getBackground().mutate();
        background.setTint(replied.getUserId() != currentUser.getPk()
                           ? resources.getColor(R.color.grey_600)
                           : resources.getColor(R.color.deep_purple_400));
        binding.replyText.setBackgroundDrawable(background);
        binding.replyText.setVisibility(View.VISIBLE);
        binding.replyText.setText(text);
    }

    private String setReplyInfo(final DirectItem item,
                                final DirectItem replied,
                                final List<User> users,
                                final Resources resources) {
        final long repliedToUserId = replied.getUserId();
        if (repliedToUserId == item.getUserId() && item.getUserId() == currentUser.getPk()) {
            // User replied to own message
            return resources.getString(R.string.replied_to_yourself);
        }
        if (repliedToUserId == item.getUserId()) {
            // opposite user replied to their own message
            return resources.getString(R.string.replied_to_themself);
        }
        final User user = getUser(repliedToUserId, users);
        final String repliedToUsername = user != null ? user.getUsername() : "";
        if (item.getUserId() == currentUser.getPk()) {
            return thread.isGroup()
                   ? resources.getString(R.string.replied_you_group, repliedToUsername)
                   : resources.getString(R.string.replied_you);
        }
        if (repliedToUserId == currentUser.getPk()) {
            return resources.getString(R.string.replied_to_you);
        }
        return resources.getString(R.string.replied_group, repliedToUsername);
    }

    private void setForwardInfo(final MessageDirection direction) {
        binding.replyInfo.setVisibility(View.VISIBLE);
        binding.replyInfo.setText(direction == MessageDirection.OUTGOING ? R.string.forward_outgoing : R.string.forward_incoming);
    }

    private void setReplyGravity(final MessageDirection messageDirection) {
        final boolean isIncoming = messageDirection == MessageDirection.INCOMING;
        final ConstraintLayout.LayoutParams quoteLineLayoutParams = (ConstraintLayout.LayoutParams) binding.quoteLine.getLayoutParams();
        final ConstraintLayout.LayoutParams replyContainerLayoutParams = (ConstraintLayout.LayoutParams) binding.replyContainer.getLayoutParams();
        final ConstraintLayout.LayoutParams replyInfoLayoutParams = (ConstraintLayout.LayoutParams) binding.replyInfo.getLayoutParams();
        final int profilePicId = binding.ivProfilePic.getId();
        final int replyContainerId = binding.replyContainer.getId();
        final int quoteLineId = binding.quoteLine.getId();
        quoteLineLayoutParams.startToEnd = isIncoming ? profilePicId : replyContainerId;
        quoteLineLayoutParams.endToStart = isIncoming ? replyContainerId : ConstraintLayout.LayoutParams.UNSET;
        quoteLineLayoutParams.endToEnd = isIncoming ? ConstraintLayout.LayoutParams.UNSET : ConstraintLayout.LayoutParams.PARENT_ID;
        replyContainerLayoutParams.startToEnd = isIncoming ? quoteLineId : profilePicId;
        replyContainerLayoutParams.endToEnd = isIncoming ? ConstraintLayout.LayoutParams.PARENT_ID : ConstraintLayout.LayoutParams.UNSET;
        replyContainerLayoutParams.endToStart = isIncoming ? ConstraintLayout.LayoutParams.UNSET : quoteLineId;
        replyInfoLayoutParams.startToEnd = isIncoming ? quoteLineId : ConstraintLayout.LayoutParams.UNSET;
        replyInfoLayoutParams.endToStart = isIncoming ? ConstraintLayout.LayoutParams.UNSET : quoteLineId;
    }

    private void setReactions(final DirectItem item, final int position) {
        binding.getRoot().post(() -> {
            MaterialFade materialFade = new MaterialFade();
            materialFade.addTarget(binding.emojis);
            TransitionManager.beginDelayedTransition(binding.getRoot(), materialFade);
            final DirectItemReactions reactions = item.getReactions();
            final List<DirectItemEmojiReaction> emojis = reactions != null ? reactions.getEmojis() : null;
            if (emojis == null || emojis.isEmpty()) {
                binding.container.setPadding(messageInfoPaddingSmall, messageInfoPaddingSmall, messageInfoPaddingSmall, 0);
                binding.reactionsWrapper.setVisibility(View.GONE);
                return;
            }
            binding.reactionsWrapper.setVisibility(View.VISIBLE);
            binding.reactionsWrapper.setTranslationY(getReactionsTranslationY());
            binding.container.setPadding(messageInfoPaddingSmall, messageInfoPaddingSmall, messageInfoPaddingSmall, reactionAdjustMargin);
            binding.emojis.setEmojis(emojis.stream()
                                           .map(DirectItemEmojiReaction::getEmoji)
                                           .collect(Collectors.toList()));
            // binding.emojis.setEmojis(ImmutableList.of("😣",
            //                                           "😖",
            //                                           "😫",
            //                                           "😩",
            //                                           "🥺",
            //                                           "😢",
            //                                           "😭",
            //                                           "😤",
            //                                           "😠",
            //                                           "😡",
            //                                           "🤬"));
            binding.emojis.setOnClickListener(v -> callback.onReactionClick(item, position));
            // final List<DirectUser> reactedUsers = emojis.stream()
            //                                             .map(DirectItemEmojiReaction::getSenderId)
            //                                             .distinct()
            //                                             .map(userId -> getUser(userId, users))
            //                                             .collect(Collectors.toList());
            // for (final DirectUser user : reactedUsers) {
            //     if (user == null) continue;
            //     final ProfilePicView profilePicView = new ProfilePicView(itemView.getContext());
            //     profilePicView.setSize(ProfilePicView.Size.TINY);
            //     profilePicView.setImageURI(user.getProfilePicUrl());
            //     binding.reactions.addView(profilePicView);
            // }
        });
    }

    protected boolean isSelf(final DirectItem directItem) {
        return directItem.getUserId() == currentUser.getPk();
    }

    public void setItemView(final View view) {
        this.binding.message.addView(view);
    }

    public abstract void bindItem(final DirectItem directItemModel, final MessageDirection messageDirection);

    @Nullable
    protected User getUser(final long userId, final List<User> users) {
        if (userId == currentUser.getPk()) {
            return currentUser;
        }
        if (users == null) return null;
        for (final User user : users) {
            if (userId != user.getPk()) continue;
            return user;
        }
        return null;
    }

    protected boolean allowMessageDirectionGravity() {
        return true;
    }

    protected boolean showUserDetailsInGroup() {
        return true;
    }

    protected boolean showBackground() {
        return false;
    }

    protected boolean showMessageInfo() {
        return true;
    }

    protected boolean allowLongClick() {
        return true;
    }

    protected boolean allowReaction() {
        return true;
    }

    protected boolean canForward() {
        return true;
    }

    protected List<DirectItemContextMenu.MenuItem> getLongClickOptions() {
        return null;
    }

    protected int getReactionsTranslationY() {
        return reactionTranslationYType1;
    }

    @CallSuper
    public void cleanup() {
        // if (layoutChangeListener != null) {
        //     binding.container.removeOnLayoutChangeListener(layoutChangeListener);
        // }
    }

    protected void setupRamboTextListeners(@NonNull final RamboTextViewV2 textView) {
        textView.addOnHashtagListener(autoLinkItem -> callback.onHashtagClick(autoLinkItem.getOriginalText().trim()));
        textView.addOnMentionClickListener(autoLinkItem -> openProfile(autoLinkItem.getOriginalText().trim()));
        textView.addOnEmailClickListener(autoLinkItem -> callback.onEmailClick(autoLinkItem.getOriginalText().trim()));
        textView.addOnURLClickListener(autoLinkItem -> openURL(autoLinkItem.getOriginalText().trim()));
    }

    protected void openProfile(final String username) {
        callback.onMentionClick(username);
    }

    protected void openLocation(final long locationId) {
        callback.onLocationClick(locationId);
    }

    protected void openURL(final String url) {
        callback.onURLClick(url);
    }

    protected void openMedia(final Media media, final int index) {
        callback.onMediaClick(media, index);
    }

    protected void openStory(final DirectItemStoryShare storyShare) {
        callback.onStoryClick(storyShare);
    }

    protected void handleDeepLink(final String deepLinkText) {
        if (deepLinkText == null) return;
        final DeepLinkParser.DeepLink deepLink = DeepLinkParser.parse(deepLinkText);
        if (deepLink == null) return;
        switch (deepLink.getType()) {
            case USER:
                callback.onMentionClick(deepLink.getValue());
                break;
        }
    }

    @SuppressLint("ClickableViewAccessibility")
    private void setupLongClickListener(final int position, final MessageDirection messageDirection) {
        if (!allowLongClick()) return;
        binding.getRoot().setOnItemLongClickListener(new DirectItemFrameLayout.OnItemLongClickListener() {
            @Override
            public void onLongClickStart(final View view) {
                itemView.post(() -> shrink());
            }

            @Override
            public void onLongClickCancel(final View view) {
                itemView.post(() -> grow());
            }

            @Override
            public void onLongClick(final View view, final float x, final float y) {
                // if (longClickListener == null) return false;
                // longClickListener.onLongClick(position, this);
                itemView.post(() -> grow());
                setSelected(true);
                showLongClickOptions(new Point((int) x, (int) y), messageDirection);
            }
        });
    }

    private void showLongClickOptions(final Point location, final MessageDirection messageDirection) {
        final List<DirectItemContextMenu.MenuItem> longClickOptions = getLongClickOptions();
        final ImmutableList.Builder<DirectItemContextMenu.MenuItem> builder = ImmutableList.builder();
        if (longClickOptions != null) {
            builder.addAll(longClickOptions);
        }
        if (canForward()) {
            builder.add(new DirectItemContextMenu.MenuItem(R.id.forward, R.string.forward));
        }
        if (thread.getInputMode() != 1 && messageDirection == MessageDirection.OUTGOING) {
            builder.add(new DirectItemContextMenu.MenuItem(R.id.unsend, R.string.dms_inbox_unsend));
        }
        final boolean showReactions = thread.getInputMode() != 1 && allowReaction();
        final ImmutableList<DirectItemContextMenu.MenuItem> menuItems = builder.build();
        if (!showReactions && menuItems.isEmpty()) return;
        final DirectItemContextMenu menu = new DirectItemContextMenu(itemView.getContext(), showReactions, menuItems);
        menu.setOnDismissListener(() -> setSelected(false));
        menu.setOnReactionClickListener(emoji -> callback.onReaction(item, emoji));
        menu.setOnOptionSelectListener((itemId, cb) -> callback.onOptionSelect(item, itemId, cb));
        menu.setOnAddReactionListener(() -> {
            menu.dismiss();
            itemView.postDelayed(() -> callback.onAddReactionListener(item), 300);
        });
        menu.show(itemView, location);
    }

    public void setLongClickListener(final DirectItemInternalLongClickListener longClickListener) {
        this.longClickListener = longClickListener;
    }

    public void setSelected(final boolean selected) {
        this.selected = selected;
    }

    private void shrink() {
        if (shrinkGrowAnimator != null) {
            shrinkGrowAnimator.cancel();
        }
        shrinkGrowAnimator = itemView.animate()
                                     .scaleX(0.8f)
                                     .scaleY(0.8f)
                                     .setInterpolator(accelerateDecelerateInterpolator)
                                     .setDuration(ViewConfiguration.getLongPressTimeout() - ViewConfiguration.getTapTimeout());
        shrinkGrowAnimator.start();
    }

    private void grow() {
        if (shrinkGrowAnimator != null) {
            shrinkGrowAnimator.cancel();
        }
        shrinkGrowAnimator = itemView.animate()
                                     .scaleX(1f)
                                     .scaleY(1f)
                                     .setInterpolator(accelerateDecelerateInterpolator)
                                     .setDuration(200)
                                     .withEndAction(() -> shrinkGrowAnimator = null);
        shrinkGrowAnimator.start();
    }

    @Override
    public int getSwipeDirection() {
        if (item == null || messageDirection == null) return ItemTouchHelper.ACTION_STATE_IDLE;
        return messageDirection == MessageDirection.OUTGOING ? ItemTouchHelper.START : ItemTouchHelper.END;
    }

    public enum MessageDirection {
        INCOMING,
        OUTGOING
    }
}
