package ru.shmakinv.android.widget.material.searchview;

import android.animation.Animator;
import android.app.DialogFragment;
import android.app.Fragment;
import android.app.FragmentManager;
import android.app.FragmentTransaction;
import android.content.Context;
import android.content.DialogInterface;
import android.content.Intent;
import android.content.pm.PackageManager;
import android.content.pm.ResolveInfo;
import android.graphics.Point;
import android.os.Build;
import android.os.Bundle;
import android.speech.RecognizerIntent;
import android.support.annotation.NonNull;
import android.text.Editable;
import android.view.Display;
import android.view.MotionEvent;
import android.view.View;
import android.view.ViewAnimationUtils;
import android.view.ViewGroup;
import android.view.WindowManager;
import android.view.animation.LinearInterpolator;
import android.view.inputmethod.InputMethodManager;

import com.devspark.robototextview.RobotoTypefaces;

import java.util.List;

/**
 * BaseRestoreInstanceFragment
 *
 * @author Vyacheslav Shmakin
 * @version 19.04.2017
 */
abstract class BaseRestoreInstanceFragment extends DialogFragment {

    private static final String KEY_CLOSE_REQUESTED = "is-close-requested";
    private static final String KEY_VISIBLE = "is-visible";
    private static final String KEY_QUERY = "last-query";
    private static final String KEY_HINT = "hint";
    private static final String KEY_CURSOR_POSITION = "last-cursor-position";
    private static final String KEY_TYPEFACE = "last-typeface";
    private static final long ANIMATOR_DURATION_SHOW = 300L;
    protected static final String DIALOG_TAG = "BaseRestoreInstanceFragment";
    protected static final long ANIMATOR_MIN_SUGGESTION_DURATION = 50L;
    protected static final long ANIMATOR_MAX_SUGGESTION_DURATION = 200L;

    protected Integer mMenuItemId = null;
    @SuppressWarnings("FieldCanBeLocal")
    private float mAnimProportionX = -1;

    protected boolean mVisible = false;
    protected boolean mCloseRequested = false;
    protected boolean mCloseAnimationRunning = false;

    protected String mQuery = null;
    protected String mHint = null;
    protected int mSelection = -1;
    protected int mTypefaceValue = RobotoTypefaces.TYPEFACE_ROBOTO_REGULAR;

    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setRetainInstance(true);
        if (savedInstanceState != null) {
            this.mVisible = savedInstanceState.getBoolean(KEY_VISIBLE);
            this.mCloseRequested = savedInstanceState.getBoolean(KEY_CLOSE_REQUESTED);
            this.mQuery = savedInstanceState.getString(KEY_QUERY, null);
            this.mHint = savedInstanceState.getString(KEY_HINT, null);
            this.mSelection = savedInstanceState.getInt(KEY_CURSOR_POSITION, mSelection);
            this.mTypefaceValue = savedInstanceState.getInt(KEY_TYPEFACE, mTypefaceValue);
        }
    }

    public void show(@NonNull final FragmentManager manager) {
        FragmentTransaction transaction = manager.beginTransaction();
        Fragment prev = manager.findFragmentByTag(DIALOG_TAG);
        if (prev != null) {
            transaction.remove(prev);
        }

        transaction.add(this, DIALOG_TAG);
        transaction.commitAllowingStateLoss();
        manager.executePendingTransactions();
    }

    protected void setupTypeface(int typefaceValue) {
        this.mTypefaceValue = typefaceValue;
    }

    @Override
    public void onSaveInstanceState(Bundle outState) {
        super.onSaveInstanceState(outState);
        outState.putBoolean(KEY_VISIBLE, this.mVisible);
        outState.putBoolean(KEY_CLOSE_REQUESTED, this.mCloseRequested);
        outState.putString(KEY_QUERY, this.mQuery);
        outState.putString(KEY_HINT, this.mHint);
        outState.putInt(KEY_CURSOR_POSITION, this.mSelection);
        outState.putInt(KEY_TYPEFACE, this.mTypefaceValue);
    }

    @Override
    public void onDestroyView() {
        // removes the dismiss intent to avoid shown dialogs being dismissed.
        if (getDialog() != null && getRetainInstance()) {
            getDialog().setDismissMessage(null);
        }
        super.onDestroyView();
    }

    @Override
    public void onDismiss(DialogInterface dialog) {
        super.onDismiss(dialog);
        this.mVisible = false;
        this.mCloseRequested = false;
        this.mQuery = null;
        this.mHint = null;
        this.mSelection = -1;
        this.mTypefaceValue = RobotoTypefaces.TYPEFACE_ROBOTO_REGULAR;
    }

    public boolean isShown() {
        return mVisible;
    }

    protected void animateShow(@NonNull final View view, @NonNull final View metricsView, final int itemId) {
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
            ViewGroup.MarginLayoutParams lp = (ViewGroup.MarginLayoutParams) view.getLayoutParams();
            int centerX = calculateCenterX(itemId);
            int centerY = (metricsView.getHeight() / 2) - lp.topMargin;
            int endRadius = metricsView.getWidth();

            final Animator animatorShow = ViewAnimationUtils.createCircularReveal(
                    view,
                    centerX,
                    centerY,
                    0.0F,
                    endRadius);

            animatorShow.addListener(new Animator.AnimatorListener() {
                @Override
                public void onAnimationStart(Animator animation) {
                    view.setVisibility(View.VISIBLE);
                    showKeyboard();
                }

                @Override
                public void onAnimationEnd(Animator animation) {
                    //noinspection ConstantConditions
                    getDialog().getWindow().addFlags(WindowManager.LayoutParams.FLAG_DIM_BEHIND);
                    onShowSearchAnimationEnd();
                }

                @Override
                public void onAnimationCancel(Animator animation) {
                }

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

            animatorShow.setDuration(ANIMATOR_DURATION_SHOW);
            animatorShow.setInterpolator(new LinearInterpolator());
            animatorShow.start();
        } else {
            view.setVisibility(View.VISIBLE);
            showKeyboard();
            //noinspection ConstantConditions
            getDialog().getWindow().addFlags(WindowManager.LayoutParams.FLAG_DIM_BEHIND);
            onShowSearchAnimationEnd();
        }
    }

    protected void animateDismiss(@NonNull final View view, @NonNull final View metricsView, final int itemId) {
        if (!mCloseAnimationRunning) {
            mCloseAnimationRunning = true;
        } else {
            return;
        }

        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
            ViewGroup.MarginLayoutParams lp = (ViewGroup.MarginLayoutParams) view.getLayoutParams();
            int centerX = calculateCenterX(itemId);
            int centerY = (metricsView.getHeight() / 2) - lp.topMargin;
            int startRadius = metricsView.getWidth();

            final Animator animatorHide = ViewAnimationUtils.createCircularReveal(
                    view,
                    centerX,
                    centerY,
                    startRadius,
                    0.0F);

            animatorHide.addListener(new Animator.AnimatorListener() {
                @Override
                public void onAnimationStart(Animator animation) {
                }

                @Override
                public void onAnimationEnd(Animator animation) {
                    finishClosing(view);
                }

                @Override
                public void onAnimationCancel(Animator animation) {
                }

                @Override
                public void onAnimationRepeat(Animator animation) {
                }
            });
            animatorHide.setInterpolator(new LinearInterpolator());
            animatorHide.setDuration(ANIMATOR_DURATION_SHOW);
            animatorHide.start();
        } else {
            finishClosing(view);
        }
    }

    protected void finishClosing(@NonNull View view) {
        //noinspection ConstantConditions
        getDialog().getWindow().clearFlags(WindowManager.LayoutParams.FLAG_DIM_BEHIND);
        hideKeyboard();
        view.setVisibility(View.INVISIBLE);
        mCloseRequested = false;
        mCloseAnimationRunning = false;
        dismissAllowingStateLoss();
    }

    protected void onShowSearchAnimationEnd() {
    }

    protected void onSuggestionsDismissed() {
    }

    protected void onQueryTextChanged(Editable s) {
    }

    protected boolean isSpeechRecognitionToolAvailable() {
        PackageManager pm = getActivity().getPackageManager();
        List<ResolveInfo> activities = pm.queryIntentActivities(new Intent(RecognizerIntent.ACTION_RECOGNIZE_SPEECH), 0);
        return activities.size() != 0;
    }

    protected final NotAllowedToEditCallBack mNotAllowedToEditCallback = new NotAllowedToEditCallBack();

    protected final SuggestionDismissListener mSuggestionsDismissListener = new SuggestionDismissListener() {
        @Override
        public void onSuggestionDismissed() {
            onSuggestionsDismissed();
        }
    };

    protected final QueryTextWatcher mSearchTextWatcher = new QueryTextWatcher() {
        @Override
        public void onQueryTextChanged(Editable s) {
            BaseRestoreInstanceFragment.this.onQueryTextChanged(s);
        }
    };

    protected void onCloseVoiceClicked() {
    }

    protected final View.OnClickListener mCloseVoiceClickListener = new View.OnClickListener() {
        @Override
        public void onClick(View v) {
            onCloseVoiceClicked();
        }
    };

    protected void onNavigationBackClicked() {
    }

    protected final View.OnClickListener mNavigationBackClickListener = new View.OnClickListener() {
        @Override
        public void onClick(View v) {
            onNavigationBackClicked();
        }
    };

    protected void onOutsideTouch(@NonNull View v, @NonNull MotionEvent event) {
    }

    protected final View.OnTouchListener mOnOutsideTouchListener = new View.OnTouchListener() {
        @Override
        public boolean onTouch(View v, MotionEvent event) {
            onOutsideTouch(v, event);
            return false;
        }
    };

    private float calculateProportionX(int xPosition) {
        return (float) xPosition / getDisplayWidth();
    }

    private int getDisplayWidth() {
        Display display = getActivity().getWindowManager().getDefaultDisplay();
        Point size = new Point();
        display.getSize(size);
        return size.x;
    }

    private int calculateCenterX(int itemId) {
        View view = getActivity().findViewById(itemId);
        //3. Calculate Animation Position:
        mAnimProportionX = calculateProportionX(getAnimationPositionX(view));
        return (int) (mAnimProportionX * getDisplayWidth());
    }

    private int getAnimationPositionX(View view) {
        // If view is null then will be used current screen width parameter
        if (view == null) {
            return getDisplayWidth();
        }

        int[] position = new int[2];
        view.getLocationInWindow(position);
        // x = View.x + View.width / 2
        // y = View.y + View.height / 2
        return position[0] + view.getWidth() / 2;
    }

    void showKeyboard() {
        ((InputMethodManager) getActivity().getSystemService(Context.INPUT_METHOD_SERVICE))
                .toggleSoftInput(InputMethodManager.SHOW_IMPLICIT, 0);
    }

    void hideKeyboard() {
        ((InputMethodManager) getActivity().getSystemService(Context.INPUT_METHOD_SERVICE))
                .toggleSoftInput(InputMethodManager.HIDE_IMPLICIT_ONLY, 0);
    }
}