package com.bcm.messenger.chats.components;

import android.animation.Animator;
import android.animation.AnimatorListenerAdapter;
import android.animation.ObjectAnimator;

import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.core.view.ViewCompat;

import android.content.Context;
import android.graphics.PorterDuff;
import android.graphics.PorterDuffColorFilter;
import android.graphics.drawable.Drawable;
import android.text.format.DateUtils;
import android.view.MotionEvent;
import android.view.View;
import android.view.animation.AlphaAnimation;
import android.view.animation.Animation;
import android.view.animation.AnimationSet;
import android.view.animation.AnticipateOvershootInterpolator;
import android.view.animation.DecelerateInterpolator;
import android.view.animation.OvershootInterpolator;
import android.view.animation.ScaleAnimation;
import android.view.animation.TranslateAnimation;
import android.widget.ImageView;
import android.widget.TextView;

import com.bcm.messenger.chats.R;
import com.bcm.messenger.common.ui.KeyboardAwareLinearLayout;
import com.bcm.messenger.common.utils.AppUtilKotlinKt;
import com.bcm.messenger.utility.ViewUtils;
import com.bcm.messenger.utility.concurrent.ListenableFuture;
import com.bcm.messenger.utility.concurrent.SettableFuture;

import java.util.concurrent.TimeUnit;

import static android.view.View.INVISIBLE;
import static android.view.View.VISIBLE;

/**
 * @author ling
 */
public class VoiceRecodingPanel implements KeyboardAwareLinearLayout.OnKeyboardShownListener, View.OnTouchListener {

    private static final String TAG = VoiceRecodingPanel.class.getSimpleName();

    private static final int FADE_TIME = 150;
    private static final int ANIMATION_DURATION = 200;

    private View slideBgView;
    private ImageView recordDotView;
    private SlideToCancel slideToCancel;
    private TextView recordTimeView;
    private @Nullable
    Listener listener;

    private boolean actionInProgress;
    private View recordButton;
    private View recordButtonFab;

    private float startPositionX;
    private float lastPositionX;
    private ObjectAnimator alphaIn;
    private ObjectAnimator alphaOut;

    public void onFinishInflate(View view) {

        this.slideBgView = view.findViewById(R.id.slide_bg_view);
        this.recordTimeView = view.findViewById(R.id.record_time);
        this.slideToCancel = new SlideToCancel(view.findViewById(R.id.slide_to_cancel));
        this.recordDotView = view.findViewById(R.id.record_dot_view);
        recordButton = view.findViewById(R.id.panel_audio_toggle);
        recordButton.setOnTouchListener(this);

        recordButtonFab = view.findViewById(R.id.quick_audio_fab);
    }

    public void setListener(final @NonNull Listener listener) {
        this.listener = listener;
    }

    @Override
    public boolean onTouch(View v, MotionEvent event) {
        lastPositionX = event.getX();

        switch (event.getAction()) {
            case MotionEvent.ACTION_DOWN:
                this.actionInProgress = true;
                if (null != listener) {
                    listener.onStartClicked();
                }
                break;
            case MotionEvent.ACTION_CANCEL:
                if (listener != null) {
                    listener.onCancelClicked();
                }
                break;
            case MotionEvent.ACTION_UP:
                if (this.actionInProgress) {
                    this.recordButton.setVisibility(VISIBLE);
                    if (listener != null) {
                        listener.onFinishClicked();
                    }
                }
                break;
            case MotionEvent.ACTION_MOVE:
                if (this.actionInProgress) {
                    moveTo(event.getX());
                    onRecordMoved(event.getX(), event.getRawX());
                }
                break;
            default:
                break;
        }

        return false;
    }


    private void playRedDot() {
        alphaIn = ObjectAnimator.ofFloat(recordDotView, "alpha", 1.0f, 0.3f).setDuration(500);
        alphaOut = ObjectAnimator.ofFloat(recordDotView, "alpha", 0.3f, 1.0f).setDuration(500);
        alphaIn.addListener(new AnimatorListenerAdapter() {
            @Override
            public void onAnimationEnd(Animator animation) {
                if (alphaOut != null) {
                    alphaOut.start();
                }
            }
        });
        alphaOut.addListener(new AnimatorListenerAdapter() {
            @Override
            public void onAnimationEnd(Animator animation) {
                if (alphaIn != null) {
                    alphaIn.start();
                }
            }
        });
        alphaIn.start();
    }

    private void stopRedDot() {
        if (alphaIn != null) {
            alphaIn.cancel();
        }
        if (alphaOut != null) {
            alphaOut.cancel();
        }
        alphaIn = null;
        alphaOut = null;
    }

    private void onRecordMoved(float x, float absoluteX) {
        slideToCancel.moveTo(x);
        float position = absoluteX / slideBgView.getWidth();

        if (position <= 0.7) {
            if (null != listener) {
                listener.onCancelClicked();
            }
            recordDotView.setImageResource(R.drawable.common_close_icon);
            recordDotView.getDrawable().setTint(AppUtilKotlinKt.getAttrColor(recordDotView.getContext(), R.attr.common_setting_item_warn_color));
        }
    }

    public void onPause() {
        if (actionInProgress && null != listener) {
            listener.onCancelClicked();
        }
    }


    @Override
    public void onKeyboardShown() {
    }

    private void display(float x) {
        this.startPositionX = x;
        this.lastPositionX = x;

        recordButtonFab.setVisibility(VISIBLE);
        recordButtonFab.setX(getWidthAdjustment() + getOffset(x));

        AnimationSet animation = new AnimationSet(true);

        ScaleAnimation scaleAnimation = new ScaleAnimation(0.5f, 1f, 0.5f, 1f,
                Animation.RELATIVE_TO_SELF, 0.5f,
                Animation.RELATIVE_TO_SELF, 0.5f);
        animation.addAnimation(scaleAnimation);
        animation.setFillBefore(true);
        animation.setFillAfter(true);
        animation.setDuration(ANIMATION_DURATION);
        animation.setInterpolator(new OvershootInterpolator());

        recordButtonFab.startAnimation(animation);
    }

    private void moveTo(float x) {
        this.lastPositionX = x;

        float offset = getOffset(x);
        int widthAdjustment = getWidthAdjustment();

        recordButtonFab.setX(widthAdjustment + offset);
    }

    private void hide(float x) {
        this.lastPositionX = x;

        float offset = getOffset(x);
        int widthAdjustment = getWidthAdjustment();

        AnimationSet animation = new AnimationSet(false);
        Animation scaleAnimation = new ScaleAnimation(1, 0.5f, 1, 0.5f,
                Animation.RELATIVE_TO_SELF, 0.5f,
                Animation.RELATIVE_TO_SELF, 0.5f);

        Animation translateAnimation = new TranslateAnimation(Animation.ABSOLUTE, offset + widthAdjustment,
                Animation.ABSOLUTE, widthAdjustment,
                Animation.RELATIVE_TO_SELF, -.25f,
                Animation.RELATIVE_TO_SELF, -.25f);

        scaleAnimation.setInterpolator(new AnticipateOvershootInterpolator(1.5f));
        translateAnimation.setInterpolator(new DecelerateInterpolator());
        animation.addAnimation(scaleAnimation);
        animation.addAnimation(translateAnimation);
        animation.setDuration(ANIMATION_DURATION);
        animation.setFillBefore(true);
        animation.setFillAfter(false);
        animation.setInterpolator(new AnticipateOvershootInterpolator(1.5f));

        recordButtonFab.setVisibility(View.GONE);
        recordButtonFab.clearAnimation();
        recordButtonFab.startAnimation(animation);
    }

    private float getOffset(float x) {
        return ViewCompat.getLayoutDirection(recordButtonFab) == ViewCompat.LAYOUT_DIRECTION_LTR ?
                -Math.max(0, this.startPositionX - x) : Math.max(0, x - this.startPositionX);
    }

    private int getWidthAdjustment() {
        int width = recordButtonFab.getWidth() / 4;
        return ViewCompat.getLayoutDirection(recordButtonFab) == ViewCompat.LAYOUT_DIRECTION_LTR ? -width : width;
    }

    public void showPlayTime(long playTime) {
        recordTimeView.setVisibility(VISIBLE);
        this.recordTimeView.setText(DateUtils.formatElapsedTime(TimeUnit.MILLISECONDS.toSeconds(playTime)));
        ViewUtils.INSTANCE.fadeIn(this.recordTimeView, FADE_TIME);
    }

    private void hidePlayTime() {
        ViewUtils.INSTANCE.fadeOut(this.recordTimeView, FADE_TIME, View.VISIBLE);
    }

    public void hideRecordState() {
        this.actionInProgress = false;

        hide(lastPositionX);
        this.recordButton.setVisibility(VISIBLE);

        slideToCancel.hide(lastPositionX);
        hidePlayTime();
        slideBgView.setVisibility(View.GONE);
        stopRedDot();
        recordDotView.setVisibility(View.GONE);
    }

    public void showRecordState() {
        showPlayTime(0);
        recordDotView.setImageResource(R.drawable.chats_record_dot);
        slideToCancel.display(startPositionX);
        slideBgView.setVisibility(VISIBLE);
        ViewUtils.INSTANCE.fadeIn(recordDotView, FADE_TIME);
        recordDotView.setVisibility(VISIBLE);
        playRedDot();

        this.actionInProgress = true;
        this.recordButton.setVisibility(INVISIBLE);
        display(lastPositionX);
    }

    private static class SlideToCancel {

        private final TextView slideToCancelView;

        private float startPositionX;

        public SlideToCancel(TextView slideToCancelView) {
            this.slideToCancelView = slideToCancelView;

            Context context = slideToCancelView.getContext();
            int clr = context.getResources().getColor(R.color.common_foreground_color);
            for (Drawable drawable : slideToCancelView.getCompoundDrawables()) {
                if (drawable != null) {
                    drawable.setColorFilter(new PorterDuffColorFilter(clr, PorterDuff.Mode.SRC_IN));
                }
            }
        }

        public void display(float startPositionX) {
            this.startPositionX = startPositionX;
            ViewUtils.INSTANCE.fadeIn(this.slideToCancelView, FADE_TIME);
        }

        public ListenableFuture<Void> hide(float x) {
            final SettableFuture<Void> future = new SettableFuture<>();
            float offset = getOffset(x);

            AnimationSet animation = new AnimationSet(true);
            animation.addAnimation(new TranslateAnimation(Animation.ABSOLUTE, offset,
                    Animation.ABSOLUTE, 0,
                    Animation.RELATIVE_TO_SELF, 0,
                    Animation.RELATIVE_TO_SELF, 0));
            animation.addAnimation(new AlphaAnimation(1, 0));

            animation.setDuration(ANIMATION_DURATION);
            animation.setFillBefore(true);
            animation.setFillAfter(false);
            animation.setAnimationListener(new Animation.AnimationListener() {
                @Override
                public void onAnimationStart(Animation animation) {
                }

                @Override
                public void onAnimationEnd(Animation animation) {
                    future.set(null);
                }

                @Override
                public void onAnimationRepeat(Animation animation) {
                }
            });

            slideToCancelView.setVisibility(View.GONE);
            slideToCancelView.startAnimation(animation);

            return future;
        }

        public void moveTo(float x) {
            float offset = getOffset(x);
            Animation animation = new TranslateAnimation(Animation.ABSOLUTE, offset,
                    Animation.ABSOLUTE, offset,
                    Animation.RELATIVE_TO_SELF, 0,
                    Animation.RELATIVE_TO_SELF, 0);

            animation.setDuration(0);
            animation.setFillAfter(true);
            animation.setFillBefore(true);

            slideToCancelView.startAnimation(animation);
        }

        private float getOffset(float x) {
            return ViewCompat.getLayoutDirection(slideToCancelView) == ViewCompat.LAYOUT_DIRECTION_LTR ?
                    -Math.max(0, this.startPositionX - x) : Math.max(0, x - this.startPositionX);
        }

    }

    public interface Listener {
        void onStartClicked();

        void onFinishClicked();

        void onCancelClicked();
    }
}