package razerdp.blur;

import android.animation.Animator;
import android.animation.AnimatorListenerAdapter;
import android.animation.ValueAnimator;
import android.content.Context;
import android.graphics.Bitmap;
import android.graphics.Matrix;
import android.graphics.Rect;
import android.os.Build;
import android.os.Looper;
import android.util.AttributeSet;
import android.view.View;
import android.view.animation.AccelerateDecelerateInterpolator;
import android.view.animation.AccelerateInterpolator;
import android.widget.ImageView;

import java.util.concurrent.atomic.AtomicBoolean;

import razerdp.blur.thread.ThreadPoolManager;
import razerdp.util.log.PopupLog;

/**
 * Created by 大灯泡 on 2017/12/27.
 * <p>
 * 模糊imageview
 */
public class BlurImageView extends ImageView {
    private static final String TAG = "BlurImageView";

    private volatile boolean abortBlur = false;
    private PopupBlurOption mBlurOption;
    private AtomicBoolean blurFinish = new AtomicBoolean(false);
    private volatile boolean isAnimating = false;
    private long startDuration;
    private CacheAction mCacheAction;
    private CacheAction mAttachedCache;
    private boolean isAttachedToWindow = false;
    private int cutoutX;
    private int cutoutY;


    public BlurImageView(Context context) {
        this(context, null);
    }

    public BlurImageView(Context context, AttributeSet attrs) {
        this(context, attrs, 0);
    }

    public BlurImageView(Context context, AttributeSet attrs, int defStyleAttr) {
        super(context, attrs, defStyleAttr);
        init();
    }

    private void init() {
        setFocusable(false);
        setFocusableInTouchMode(false);
        setScaleType(ScaleType.MATRIX);
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN) {
            setBackground(null);
        } else {
            setBackgroundDrawable(null);
        }
    }

    public BlurImageView setCutoutX(int cutoutX) {
        this.cutoutX = cutoutX;
        return this;
    }

    public BlurImageView setCutoutY(int cutoutY) {
        this.cutoutY = cutoutY;
        return this;
    }

    public void applyBlurOption(PopupBlurOption option) {
        applyBlurOption(option, false);
    }

    private void applyBlurOption(PopupBlurOption option, boolean isOnUpdate) {
        if (option == null) return;
        mBlurOption = option;
        View anchorView = option.getBlurView();
        if (anchorView == null) {
            PopupLog.e(TAG, "模糊锚点View为空，放弃模糊操作...");
            destroy();
            return;
        }
        //因为考虑到实时更新位置（包括模糊也要实时）的原因，因此强制更新时模糊操作在主线程完成。
        if (option.isBlurAsync() && !isOnUpdate) {
            PopupLog.i(TAG, "子线程blur");
            startBlurTask(anchorView);
        } else {
            try {
                PopupLog.i(TAG, "主线程blur");
                if (!BlurHelper.renderScriptSupported()) {
                    PopupLog.e(TAG, "不支持脚本模糊。。。最低支持api 17(Android 4.2.2)，将采用fastblur");
                }
                setImageBitmapOnUiThread(BlurHelper.blur(getContext(), anchorView, option.getBlurPreScaleRatio(), option
                        .getBlurRadius(), option.isFullScreen(), cutoutX, cutoutY), isOnUpdate);
            } catch (Exception e) {
                PopupLog.e(TAG, "模糊异常", e);
                e.printStackTrace();
                destroy();
            }
        }
    }

    @Override
    protected void onDetachedFromWindow() {
        super.onDetachedFromWindow();
        abortBlur = true;
    }

    public void update() {
        if (mBlurOption != null) {
            applyBlurOption(mBlurOption, true);
        }
    }

    /**
     * alpha进场动画
     */
    public void start(long duration) {
        startDuration = duration;
        if (!blurFinish.get()) {
            if (mCacheAction == null) {
                mCacheAction = new CacheAction(new Runnable() {
                    @Override
                    public void run() {
                        start(startDuration);
                    }
                }, 0);
                PopupLog.e(TAG, "缓存模糊动画，等待模糊完成");
            }
            return;
        }
        //干掉缓存的runnable
        if (mCacheAction != null) {
            mCacheAction.destroy();
            mCacheAction = null;
        }
        if (isAnimating) return;
        PopupLog.i(TAG, "开始模糊alpha动画");
        isAnimating = true;
        if (duration > 0) {
            startAlphaInAnimation(duration);
        } else if (duration == -2) {
            startAlphaInAnimation(mBlurOption == null ? 500 : mBlurOption.getBlurInDuration());
        } else {
            setImageAlpha(255);
        }
    }

    private void startAlphaInAnimation(long duration) {
        ValueAnimator valueAnimator = ValueAnimator.ofInt(0, 255);
        valueAnimator.setDuration(duration);
        valueAnimator.setInterpolator(new AccelerateDecelerateInterpolator());
        valueAnimator.addListener(new AnimatorListenerAdapter() {
            @Override
            public void onAnimationEnd(Animator animation) {
                isAnimating = false;
            }
        });
        valueAnimator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
            @Override
            public void onAnimationUpdate(ValueAnimator animation) {
                setImageAlpha((Integer) animation.getAnimatedValue());
            }
        });
        valueAnimator.start();
    }

    /**
     * alpha退场动画
     */
    public void dismiss(long duration) {
        isAnimating = false;
        PopupLog.i(TAG, "dismiss模糊imageview alpha动画");
        if (duration > 0) {
            startAlphaOutAnimation(duration);
        } else if (duration == -2) {
            startAlphaOutAnimation(mBlurOption == null ? 500 : mBlurOption.getBlurOutDuration());
        } else {
            setImageAlpha(0);
        }
    }

    private void startAlphaOutAnimation(long duration) {
        ValueAnimator valueAnimator = ValueAnimator.ofInt(255, 0);
        valueAnimator.setDuration(duration);
        valueAnimator.setInterpolator(new AccelerateInterpolator());
        valueAnimator.addListener(new AnimatorListenerAdapter() {
            @Override
            public void onAnimationEnd(Animator animation) {
                isAnimating = false;
            }
        });
        valueAnimator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
            @Override
            public void onAnimationUpdate(ValueAnimator animation) {
                setImageAlpha((Integer) animation.getAnimatedValue());
            }
        });
        valueAnimator.start();
    }

    /**
     * 子线程模糊
     *
     * @param anchorView
     */
    private void startBlurTask(View anchorView) {
        ThreadPoolManager.execute(new CreateBlurBitmapRunnable(anchorView));
    }


    /**
     * 判断是否处于主线程，并进行设置bitmap
     *
     * @param blurBitmap
     */
    private void setImageBitmapOnUiThread(final Bitmap blurBitmap, final boolean isOnUpdate) {
        if (isUiThread()) {
            handleSetImageBitmap(blurBitmap, isOnUpdate);
        } else {
            if (!isAttachedToWindow) {
                mAttachedCache = new CacheAction(new Runnable() {
                    @Override
                    public void run() {
                        handleSetImageBitmap(blurBitmap, isOnUpdate);
                    }
                }, 0);
            } else {
                post(new Runnable() {
                    @Override
                    public void run() {
                        handleSetImageBitmap(blurBitmap, isOnUpdate);
                    }
                });
            }
        }
    }

    @Override
    protected void onAttachedToWindow() {
        super.onAttachedToWindow();
        isAttachedToWindow = true;
        if (mAttachedCache != null) {
            mAttachedCache.forceRestore();
        }
    }

    /**
     * 设置bitmap，并进行后续处理（此方法必定运行在主线程）
     *
     * @param bitmap
     */
    private void handleSetImageBitmap(Bitmap bitmap, boolean isOnUpdate) {
        if (bitmap != null) {
            PopupLog.i("bitmap: 【" + bitmap.getWidth() + "," + bitmap.getHeight() + "】");
        }

        setImageAlpha(isOnUpdate ? 255 : 0);
        setImageBitmap(bitmap);
        PopupBlurOption option = mBlurOption;
        if (option != null && !option.isFullScreen()) {
            //非全屏的话，则需要将bitmap变化到对应位置
            View anchorView = option.getBlurView();
            if (anchorView == null) return;
            Rect rect = new Rect();
            anchorView.getGlobalVisibleRect(rect);
            Matrix matrix = getImageMatrix();
            matrix.setTranslate(rect.left, rect.top);
            setImageMatrix(matrix);
        }
        blurFinish.compareAndSet(false, true);
        PopupLog.i(TAG, "设置成功：" + blurFinish.get());
        if (mCacheAction != null) {
            PopupLog.i(TAG, "恢复缓存动画");
            mCacheAction.restore();
        }
        if (mAttachedCache != null) {
            mAttachedCache.destroy();
            mAttachedCache = null;
        }
    }

    private boolean isUiThread() {
        return Thread.currentThread() == Looper.getMainLooper().getThread();
    }

    public void destroy() {
        setImageBitmap(null);
        abortBlur = true;
        if (mBlurOption != null) {
            mBlurOption = null;
        }
        if (mCacheAction != null) {
            mCacheAction.destroy();
            mCacheAction = null;
        }
        blurFinish.set(false);
        isAnimating = false;
        startDuration = 0;
    }

    class CreateBlurBitmapRunnable implements Runnable {

        private int outWidth;
        private int outHeight;
        private Bitmap mBitmap;

        CreateBlurBitmapRunnable(View target) {
            outWidth = target.getWidth();
            outHeight = target.getHeight();
            PopupLog.i("aaadf", cutoutX, cutoutY);
            mBitmap = BlurHelper.getViewBitmap(target, mBlurOption.getBlurPreScaleRatio(), mBlurOption
                    .isFullScreen(), cutoutX, cutoutY);
        }

        @Override
        public void run() {
            if (abortBlur || mBlurOption == null) {
                PopupLog.e(TAG, "放弃模糊，可能是已经移除了布局");
                return;
            }
            PopupLog.i(TAG, "子线程模糊执行");
            setImageBitmapOnUiThread(BlurHelper.blur(getContext(),
                    mBitmap,
                    outWidth,
                    outHeight,
                    mBlurOption.getBlurRadius()),
                    false);
        }
    }

    class CacheAction {
        private static final long BLUR_TASK_WAIT_TIMEOUT = 1000;//图片模糊超时1秒
        Runnable action;
        long delay;
        final long startTime;

        CacheAction(Runnable action, long delay) {
            this.action = action;
            this.delay = delay;
            this.startTime = System.currentTimeMillis();
        }

        void restore() {
            if (isOverTime()) {
                PopupLog.e(TAG, "模糊超时");
                destroy();
                return;
            }
            if (action != null) {
                post(action);
            }
        }

        void forceRestore() {
            if (action != null) {
                post(action);
            }
        }

        boolean isOverTime() {
            return System.currentTimeMillis() - startTime > BLUR_TASK_WAIT_TIMEOUT;
        }

        void destroy() {
            if (action != null) {
                removeCallbacks(action);
            }
            action = null;
            delay = 0;

        }


        public boolean matches(Runnable otherAction) {
            return otherAction == null && action == null
                    || action != null && action.equals(otherAction);
        }
    }
}
