package com.linxiao.framework.widget;

import android.app.Activity;
import android.content.Context;
import android.content.res.Resources;
import android.graphics.Bitmap;
import android.graphics.Canvas;
import android.graphics.Paint;
import android.graphics.PorterDuff;
import android.graphics.PorterDuffXfermode;
import android.graphics.Rect;
import android.graphics.RectF;
import android.graphics.drawable.Drawable;
import android.os.Build;
import androidx.annotation.AttrRes;
import androidx.annotation.ColorInt;
import androidx.annotation.ColorRes;
import androidx.annotation.DrawableRes;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.core.content.ContextCompat;
import androidx.collection.ArrayMap;
import android.util.AttributeSet;
import android.util.DisplayMetrics;
import android.view.Gravity;
import android.view.MotionEvent;
import android.view.View;
import android.view.ViewGroup;
import android.view.Window;
import android.widget.FrameLayout;
import android.widget.ImageView;

import java.util.ArrayList;
import java.util.List;
import java.util.Map;

/**
 * 高亮引导控件
 * <p> 对于单个引导页创建,请使用 {@link #newInstance(Activity)} 创建引导页实例,
 * 如果有依次弹出若干引导页的需求,可以使用{@link #newGuideQueue()}创建引导页队列,
 * 队列会自动管理引导页销毁逻辑
 * </p>
 *
 * Created by linxiao on 2017/8/3.
 */
public class HighlightGuideView extends FrameLayout {
    /**
     * 方形高亮
     * */
    public static final int STYLE_RECT = 0;
    /**
     * 原型高亮
     * */
    public static final int STYLE_CIRCLE = 1;
    /**
     * 椭圆形高亮
     * */
    public static final int STYLE_OVAL = 2;

    /**
     * 没有高亮目标的引导控件容器ID
     * */
    private static final int NO_TARGET_GUIDE_ID = 10152;

    /**
     * 引导页销毁监听
     * */
    public interface OnDismissListener {
        void onDismiss();
    }
    
    // 需要高亮页面的根布局
    private ViewGroup mRootView;

    private Map<Integer, View> mTargetViewMap = new ArrayMap<>();
    private Map<Integer, List<View>> mGuideViewsMap = new ArrayMap<>();
    private Map<Integer, Integer> mHighlightPaddingMap = new ArrayMap<>();
    private Map<Integer, Map<String, Integer>> mGuideRelativePosMap = new ArrayMap<>();

    private int mHighlightStyle = STYLE_CIRCLE; // 默认为圆形高亮
    // 绘制参数
    private Bitmap backgroundBitmap; //背景
    private Paint mHighlightPaint;
    private Canvas mCanvas;
    private int screenWidth;
    private int screenHeight;
    private int backgroundColor = 0xCC000000; //背景默认颜色
    
    private boolean touchOutsideCancelable = true;
    
    private List<OnDismissListener> dismissListeners = new ArrayList<>();
    
    /**
     * 新建引导页实例
     * <p>必须使用Activity</p>
     * */
    public static HighlightGuideView newInstance(Activity activity) {
        return new HighlightGuideView(activity);
    }

    /**
     * 新建引导页队列
     * */
    public static GuideQueue newGuideQueue() {
        return new GuideQueue();
    }

    private HighlightGuideView(@NonNull Context context) {
        super(context);
        if (context instanceof Activity) {
            mRootView = (ViewGroup) ((Activity)context).findViewById(Window.ID_ANDROID_CONTENT);
        }
        setWillNotDraw(false);
        mHighlightPaint = new Paint(Paint.ANTI_ALIAS_FLAG | Paint.DITHER_FLAG); // 开启抗锯齿和抗抖动
        mHighlightPaint.setARGB(0, 255, 0, 0);
        mHighlightPaint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.DST_IN));
    
        Resources resources = getContext().getResources();
        DisplayMetrics dm = resources.getDisplayMetrics();
        screenWidth = dm.widthPixels;
        screenHeight = dm.heightPixels;
        
        backgroundBitmap = Bitmap.createBitmap(screenWidth, screenHeight, Bitmap.Config.ARGB_4444);
        mCanvas = new Canvas(backgroundBitmap);
        mCanvas.drawColor(backgroundColor);
    }

    private HighlightGuideView(@NonNull Context context, @Nullable AttributeSet attrs) {
        this(context);
    }

    private HighlightGuideView(@NonNull Context context, @Nullable AttributeSet attrs, @AttrRes int defStyleAttr) {
        this(context);
    }

    /**
     * 设置高亮导航目标
     *
     * @param view 目标控件
     * */
    public HighlightGuideView addTargetView(View view) {
        mTargetViewMap.put(view.hashCode(), view);
        if (!mGuideViewsMap.containsKey(view.hashCode())) {
            mGuideViewsMap.put(view.hashCode(), new ArrayList<View>());
        }
        return this;
    }

    /**
     * 设置目标View高亮区域内边距
     * @param view 目标View
     * @param padding 内边距
     * */
    public HighlightGuideView setHighlightPadding(View view, int padding) {
        if (view == null) {
            return this;
        }
        mHighlightPaddingMap.put(view.hashCode(), padding);
        return this;
    }

    /**
     * 添加目标引导控件
     *
     * @param targetView 目标View,如果没有目标则传入null
     * @param guideView 图片资源Drawable
     * @param width 引导图片宽度
     * @param height 引导图片高度
     * @param relativeX 引导图片相对目标View左上角横向距离
     * @param relativeY 引导图片相对目标View左上角纵向距离
     * */
    public HighlightGuideView addGuideView(View targetView, View guideView, int width, int height, int relativeX, int relativeY) {
        if (targetView != null && !mGuideViewsMap.containsKey(targetView.hashCode())) {
            return this;
        }
        LayoutParams lp = new LayoutParams(width, height);
        guideView.setLayoutParams(lp);
        
        Map<String, Integer> paramsMap = new ArrayMap<>();
        paramsMap.put("x", relativeX);
        paramsMap.put("y", relativeY);
        mGuideRelativePosMap.put(guideView.hashCode(), paramsMap);

        if (targetView != null) {
            mGuideViewsMap.get(targetView.hashCode()).add(guideView);
        }
        else {
            if (!mGuideViewsMap.containsKey(NO_TARGET_GUIDE_ID)) {
                mGuideViewsMap.put(NO_TARGET_GUIDE_ID, new ArrayList<View>());
            }
            mGuideViewsMap.get(NO_TARGET_GUIDE_ID).add(guideView);
        }
        this.addView(guideView);
        return this;
    }
    
    /**
     * 添加目标引导控件
     * <p>不传入宽高默认为wrap_content</p>
     *
     * @param targetView 目标View,如果没有目标则传入null
     * @param guideView 图片资源Drawable
     * @param relativeX 引导图片相对目标View左上角横向距离
     * @param relativeY 引导图片相对目标View左上角纵向距离
     * */
    public HighlightGuideView addGuideView(View targetView, View guideView, int relativeX, int relativeY) {
        addGuideView(targetView, guideView,
                LayoutParams.WRAP_CONTENT,
                LayoutParams.WRAP_CONTENT,
                relativeX, relativeY
        );
        return this;
    }

    /**
     * 添加引导图片
     *
     * @param targetView 目标View,如果没有目标则传入null
     * @param guideDrawable 图片资源Drawable
     * @param width 引导图片宽度
     * @param height 引导图片高度
     * @param relativeX 引导图片相对目标View左上角横向距离
     * @param relativeY 引导图片相对目标View左上角纵向距离
     * */
    public HighlightGuideView addGuideImage(View targetView, Drawable guideDrawable, int width, int height, int relativeX, int relativeY) {
        ImageView guideImageView = new ImageView(getContext());
        guideImageView.setImageDrawable(guideDrawable);
        addGuideView(targetView, guideImageView, width, height, relativeX, relativeY);
        return this;
    }
    
    /**
     * 添加引导图片
     * <p>不传入宽高默认为wrap_content</p>
     *
     * @param targetView 目标View,如果没有目标则传入null
     * @param guideDrawable 图片资源Drawable
     * @param relativeX 引导图片相对目标View左上角横向距离
     * @param relativeY 引导图片相对目标View左上角纵向距离
     * */
    public HighlightGuideView addGuideImage(View targetView, Drawable guideDrawable, int relativeX, int relativeY) {
        addGuideImage(targetView, guideDrawable,
                LayoutParams.WRAP_CONTENT,
                LayoutParams.WRAP_CONTENT,
                relativeX, relativeY
        );
        return this;
    }
    
    /**
     * 添加引导图片
     *
     *
     * @param targetView 目标View,如果没有目标则传入null
     * @param resId 图片资源ID
     * @param width 引导图片宽度
     * @param height 引导图片高度
     * @param relativeX 引导图片相对目标View左上角横向距离
     * @param relativeY 引导图片相对目标View左上角纵向距离
     * */
    public HighlightGuideView addGuideImage(View targetView, @DrawableRes int resId, int width, int height, int relativeX, int relativeY) {
        ImageView guideImageView = new ImageView(getContext());
        guideImageView.setImageResource(resId);
        addGuideView(targetView, guideImageView, width, height, relativeX, relativeY);
        return this;
    }
    
    /**
     * 添加引导图片
     * <p>不传入宽高默认为wrap_content</p>
     *
     * @param targetView 目标View,如果没有目标则传入null
     * @param resId 图片资源ID
     * @param relativeX 引导图片相对目标View左上角横向距离
     * @param relativeY 引导图片相对目标View左上角纵向距离
     * */
    public HighlightGuideView addGuideImage(View targetView, @DrawableRes int resId, int relativeX, int relativeY) {
        addGuideImage(targetView, resId,
                LayoutParams.WRAP_CONTENT,
                LayoutParams.WRAP_CONTENT,
                relativeX, relativeY
        );
        return this;
    }

    /**
     * 是否允许点击空白区域隐藏HighlightGuideView
     * <p>默认为true</p>
     *
     * @param cancel 是否允许
     * */
    public HighlightGuideView setCancelOnTouchOutside(boolean cancel) {
        touchOutsideCancelable = cancel;
        return this;
    }

    /**
     * 设置高亮样式
     *
     * @param style 高亮样式
     * */
    public HighlightGuideView setHighlightStyle(int style) {
        if (style > 2 || style < 0) {
            mHighlightStyle = 1;
        }
        mHighlightStyle = style;
        return this;
    }

    /**
     * 设置蒙版背景颜色
     *
     * @param color 颜色色值
     * */
    public HighlightGuideView setMaskBackgroundColor(@ColorInt int color) {
        backgroundColor = color;
        return this;
    }
    
    /**
     * 设置蒙版背景颜色
     *
     * @param resId 颜色资源ID
     * */
    public HighlightGuideView setMaskBackgroundRes(@ColorRes int resId) {
        backgroundColor = ContextCompat.getColor(getContext(), resId);
        return this;
    }
    
    public HighlightGuideView addOnDismissListener(OnDismissListener listener) {
        dismissListeners.add(listener);
        return this;
    }

    /**
     * 显示引导
     * */
    public void show() {
        if (mRootView == null) {
            return;
        }
        for (int i = 0; i < mRootView.getChildCount(); i++) {
            if (mRootView.getChildAt(i).equals(this)) {
                return;
            }
        }
        mRootView.addView(this,
                ViewGroup.LayoutParams.MATCH_PARENT,
                ViewGroup.LayoutParams.MATCH_PARENT
        );
        this.setVisibility(VISIBLE);
    }

    /**
     * 移除引导
     * */
    public void dismiss() {
        if (mRootView == null) {
            return;
        }
        this.setVisibility(GONE);
        mRootView.removeView(this);
        for (OnDismissListener listener : dismissListeners) {
            if (listener != null) {
                listener.onDismiss();
            }
        }
    }
    
    @Override
    public boolean onTouchEvent(MotionEvent event) {
        if (event.getAction() != MotionEvent.ACTION_UP) {
            return true;
        }
        if (touchOutsideCancelable) {
            dismiss();
        }
        return true;
    }
    
    @Override
    protected void onLayout(boolean changed, int left, int top, int right, int bottom) {
        super.onLayout(changed, left, top, right, bottom);
        for (int targetId : mGuideViewsMap.keySet()) {
            int targetX = 0, targetY = 0;
            if (targetId != NO_TARGET_GUIDE_ID) {
                View targetView = mTargetViewMap.get(targetId);
                Rect rect = getTargetViewRect(targetView);
                targetX = rect.left;
                targetY = rect.top;
            }
            for (View guideView : mGuideViewsMap.get(targetId)) {
                int relativeX = 0;
                int relativeY = 0;
                if (mGuideRelativePosMap.containsKey(guideView.hashCode())) {
                    relativeX = mGuideRelativePosMap.get(guideView.hashCode()).get("x");
                    relativeY = mGuideRelativePosMap.get(guideView.hashCode()).get("y");
                }

                LayoutParams params = (LayoutParams) guideView.getLayoutParams();
                //尝试兼容View中设定的水平居中或垂直居中属性
                int gravity = params.gravity;
                int absoluteGravity = 0;
                if (Build.VERSION.SDK_INT >= 17) {
                    final int layoutDirection = getLayoutDirection();
                    absoluteGravity = Gravity.getAbsoluteGravity(gravity, layoutDirection);
                }
                final int verticalGravity = gravity & Gravity.VERTICAL_GRAVITY_MASK;
                if (absoluteGravity == Gravity.CENTER_HORIZONTAL) {
                    guideView.setX((screenWidth - guideView.getMeasuredWidth()) / 2);
                }
                else {
                    guideView.setX(targetX + params.leftMargin + relativeX);
                }
                if (verticalGravity == Gravity.CENTER_VERTICAL) {
                    guideView.setY((screenHeight - guideView.getMeasuredHeight()) / 2);
                }
                else {
                    guideView.setY(targetY + params.topMargin + relativeY);
                }
            }
        }
    }
    
    @Override
    protected void onDraw(Canvas canvas) {
        super.onDraw(canvas);
        //绘制背景
        canvas.drawBitmap(backgroundBitmap, 0, 0, null);
        //绘制高亮区域
        if (mTargetViewMap.size() == 0) {
            return;
        }
        for (int targetId : mGuideViewsMap.keySet()) {
            View targetView = mTargetViewMap.get(targetId);
            int padding = 0;
            if (mHighlightPaddingMap.containsKey(targetId)) {
                padding = mHighlightPaddingMap.get(targetId);
            }
            drawHighlightArea(targetView, padding);
        }
    }
    /**
     * 绘制高亮区域
     * */
    private void drawHighlightArea(View highlightView, int padding) {
        int width = highlightView.getWidth();
        int height = highlightView.getHeight();
        //高亮控件坐标
        Rect targetRect = getTargetViewRect(highlightView);
        int left = targetRect.left;
        int top = targetRect.top;
        int right = targetRect.right;
        int bottom = targetRect.bottom;
    
        RectF highlightRect;
        switch (mHighlightStyle) {
        case STYLE_RECT :
            highlightRect = new RectF(left - padding, top - padding, right + padding, bottom + padding);
            mCanvas.drawRect(highlightRect, mHighlightPaint);
            break;
        case STYLE_OVAL:
            highlightRect = new RectF(left - padding, top - padding, right + padding, bottom + padding);
            mCanvas.drawOval(highlightRect, mHighlightPaint);
            break;
        case STYLE_CIRCLE:
            int radius = ((width > height ? width : height) + padding) / 2;
            mCanvas.drawCircle(left + width / 2, top + height / 2, radius, mHighlightPaint);
            break;
        }
    }
    
    /**
     * 获取目标控件在Activity根布局中的坐标矩阵
     * */
    private Rect getTargetViewRect(View targetView) {
        View parent = mRootView.getChildAt(0);
        View decorView = null;
        Context context = targetView.getContext();
        if (context instanceof Activity) {
            decorView = ((Activity) context).getWindow().getDecorView();
        }
        Rect result = new Rect();
        Rect tmpRect = new Rect();
        
        View tmp = targetView;
        
        if (targetView == parent) {
            targetView.getHitRect(result);
            return result;
        }
        while (tmp != decorView && tmp != parent) {
            tmp.getHitRect(tmpRect);
            if (!tmp.getClass().toString().equals("NoSaveStateFrameLayout")) {
                result.left += tmpRect.left;
                result.top += tmpRect.top;
            }
            tmp = (View) tmp.getParent();
        }
        result.right = result.left + targetView.getMeasuredWidth();
        result.bottom = result.top + targetView.getMeasuredHeight();
        return result;
    }

    /**
     * 引导页队列
     * <p>使用此类构建按添加先后顺序依次显示的引导页队列</p>
     *
     * */
    public static class GuideQueue {

        private List<HighlightGuideView> mGuideViewList = new ArrayList<>();
        private int showCount = 0;

        public void add(HighlightGuideView guideView) {
            if (guideView == null) {
                return;
            }
            guideView.addOnDismissListener(new OnDismissListener() {
                @Override
                public void onDismiss() {
                    showNext();
                }
            });
            mGuideViewList.add(guideView);
        }

        /**
         * 移除引导页,显示过后的页面无法移除
         * */
        public void remove(HighlightGuideView guideView) {
            if (mGuideViewList.indexOf(guideView) > showCount) {
                mGuideViewList.remove(guideView);
            }
        }

        public void show() {
            if (mGuideViewList.size() > 0) {
                mGuideViewList.get(0).show();
            }
        }

        /**
         * 取消后续引导显示
         * */
        public void cancelAll() {
            mGuideViewList.get(showCount).dismiss();
            mGuideViewList.clear();
        }

        private void showNext() {
            if (++showCount >= mGuideViewList.size()) {
                mGuideViewList.clear();
                showCount = 0;
                return ;
            }
            mGuideViewList.get(showCount).show();
        }
    }
}