package com.yyrh.ui.floatview;

import android.annotation.SuppressLint;
import android.app.Activity;
import android.content.Context;
import android.graphics.PixelFormat;
import android.graphics.drawable.ColorDrawable;
import android.os.Build;
import android.os.Handler;
import android.os.Looper;
import android.os.Message;
import android.support.annotation.NonNull;
import android.view.Gravity;
import android.view.MotionEvent;
import android.view.View;
import android.view.ViewGroup;
import android.view.WindowManager;
import android.widget.FrameLayout;
import android.widget.ImageView;
import android.widget.LinearLayout;
import android.widget.PopupWindow;

import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.concurrent.TimeUnit;

import cn.yyxx.support.DensityUtils;
import cn.yyxx.support.scheduler.ScheduledWorker;

/**
 * @author #Suyghur.
 * Created on 2021/10/21
 */
public class FloatingBall extends FrameLayout implements View.OnTouchListener {

    private Activity activity;
    private boolean isLandscape = false;
    private IFloatingBallCallback callback;
    private WindowManager wm;
    private WindowManager.LayoutParams wlp;
    private LinearLayout rootLinearLayout;
    private FrameLayout frameLayout;
    private ImageView ballView;
    private FloatingBallMenu ballMenu;
    private PopupWindow menu;

    // 浮标是否移动
    private boolean isMove = false;

    // 手按下时坐标
    private float mTouchStartX = 0f;
    private float mTouchStartY = 0f;

    // 是否展开
    private boolean hasShowContent = false;

    // 当前贴边状态
    private boolean isHide = false;

    // 默认吸附在左边
    private boolean isLeftLocation = true;

    // 菜单menu的长度
    private int mSize = 0;

    private boolean hasStatusBar;

    private ScheduledWorker displayWorker = null;

    private Handler timeHandler = new Handler(Looper.getMainLooper()) {
        @Override
        public void handleMessage(@NonNull Message msg) {
            if (msg.what == 1000) {
//                invokeBallFullOrHalt(true);
                callback.onUpdateBallView(ballView, isLeftLocation, true);
                // 把worker取消掉防止一直在执行
                if (displayWorker != null) {
                    displayWorker.cancel();
                }
            }
        }
    };

    public FloatingBall(Activity activity, boolean isLandscape, IFloatingBallCallback callback) {
        super(activity);
        this.activity = activity;
        this.isLandscape = isLandscape;
        this.callback = callback;
        this.hasStatusBar = (activity.getWindow().getAttributes().flags & WindowManager.LayoutParams.FLAG_FULLSCREEN) != WindowManager.LayoutParams.FLAG_FULLSCREEN;
        createWM();
        createView();
        addView(rootLinearLayout);
        wm.addView(this, wlp);
        displayWorker = new ScheduledWorker(1);
        invokeDisplayTimerWork();
    }

    @SuppressLint("WrongConstant")
    private void createWM() {
        this.wm = activity.getWindowManager();
        wlp = new WindowManager.LayoutParams();
        wlp.type = WindowManager.LayoutParams.LAST_APPLICATION_WINDOW;
        wlp.format = PixelFormat.RGBA_8888;
        wlp.flags = WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE;
        wlp.gravity = Gravity.TOP | Gravity.LEFT;
        wlp.x = 0;
        wlp.y = DensityUtils.getHeigthAndWidth(activity)[1] / 2;
        wlp.width = WindowManager.LayoutParams.WRAP_CONTENT;
        wlp.height = WindowManager.LayoutParams.WRAP_CONTENT;
    }

    private void createView() {
        rootLinearLayout = new LinearLayout(activity);
        rootLinearLayout.setLayoutParams(new ViewGroup.LayoutParams(DensityUtils.dip2px(activity, 45f), DensityUtils.dip2px(activity, 45f)));

        frameLayout = new FrameLayout(activity);
        frameLayout.setLayoutParams(new ViewGroup.LayoutParams(ViewGroup.LayoutParams.WRAP_CONTENT, ViewGroup.LayoutParams.WRAP_CONTENT));

        ballView = new ImageView(activity);
        ballView.setLayoutParams(new ViewGroup.LayoutParams(DensityUtils.dip2px(activity, 45f), DensityUtils.dip2px(activity, 45f)));
        callback.onUpdateBallView(ballView, isLeftLocation, false);
        ballView.setScaleType(ImageView.ScaleType.FIT_XY);
        ballView.setOnTouchListener(this);
        frameLayout.addView(ballView);

        ballMenu = new FloatingBallMenu(activity, new FloatingBallMenu.IFloatingBallMenuCallback() {
            @Override
            public ArrayList<FloatingBallMenu.Item> onInitMenuData() {
                mSize = callback.onInitMenuData().size();
                return callback.onInitMenuData();
            }

            @Override
            public void onDismissMenu() {
                callback.onDismissMenu();
            }

            @Override
            public void onMenuItemCLick(FloatingBallMenu.Item item, int pos) {
                callback.onMenuItemClick(item, pos);
            }
        });

        menu = new PopupWindow(activity);
        menu.setContentView(ballMenu.contentView);
        menu.setWidth(DensityUtils.dip2px(activity, 45f + mSize * 45f));
        menu.setHeight(DensityUtils.dip2px(activity, 45f));
        menu.setFocusable(true);
        menu.setBackgroundDrawable(new ColorDrawable());
        menu.setOutsideTouchable(true);
        menu.setOnDismissListener(new PopupWindow.OnDismissListener() {
            @Override
            public void onDismiss() {
                hasShowContent = false;
                invokeDisplayTimerWork();
            }
        });

        if (isLandscape) {
            handleFullScreenModel();
        }
        rootLinearLayout.addView(frameLayout);
    }

    public void dismissMenu() {
        hasShowContent = false;
        menu.dismiss();
        callback.onUpdateBallView(ballView, isLeftLocation, false);
    }

    private void invokeBallFullOrHalt(boolean isHalf) {
        if (hasShowContent && !isHide) {
            return;
        }
        this.isHide = isHalf;
        if (isHalf) {
            rootLinearLayout.setLayoutParams(new ViewGroup.LayoutParams(DensityUtils.dip2px(activity, 22.5f), DensityUtils.dip2px(activity, 45f)));
            setLayoutParams(new ViewGroup.LayoutParams(DensityUtils.dip2px(activity, 22.5f), DensityUtils.dip2px(activity, 45f)));
        } else {
            rootLinearLayout.setLayoutParams(new ViewGroup.LayoutParams(DensityUtils.dip2px(activity, 45f), DensityUtils.dip2px(activity, 45f)));
            setLayoutParams(new ViewGroup.LayoutParams(DensityUtils.dip2px(activity, 45f), DensityUtils.dip2px(activity, 45f)));
        }
        frameLayout.updateViewLayout(ballView, getLayoutParams());
    }

    private void invokeMenuShowOrDismiss() {
        if (hasShowContent) {
            hasShowContent = false;
            menu.dismiss();
        } else {
            hasShowContent = true;
            callback.onExpandMenu(false);
            ballMenu.updateLayout(isLeftLocation);
            if (hasStatusBar) {
                menu.showAtLocation(ballMenu.contentView, Gravity.NO_GRAVITY, wlp.x + 10, wlp.y + getStatusBarHeight(activity));
            } else {
                if (isNotch()) {
                    menu.showAtLocation(ballMenu.contentView, Gravity.NO_GRAVITY, wlp.x + 10, wlp.y + getStatusBarHeight(activity));
                } else if (isDisplayCutout(activity)) {
                    menu.showAtLocation(ballMenu.contentView, Gravity.NO_GRAVITY, wlp.x + 10, wlp.y + getStatusBarHeight(activity));
                } else {
                    menu.showAtLocation(ballMenu.contentView, Gravity.NO_GRAVITY, wlp.x + 10, wlp.y);
                }
            }
        }
    }

    private void invokeDisplayTimerWork() {
        if (displayWorker != null) {
            displayWorker.invokeAtFixedRate(new Runnable() {
                @Override
                public void run() {
                    timeHandler.sendEmptyMessage(1000);
                }
            }, 10, 10, TimeUnit.SECONDS);
        }
    }

    public void attach() {
        if (getVisibility() == GONE) {
            invokeDisplayTimerWork();
            ballView.setVisibility(VISIBLE);
            frameLayout.setVisibility(VISIBLE);
            rootLinearLayout.setVisibility(VISIBLE);
            setVisibility(VISIBLE);
        }
    }

    public void detach() {
        if (getVisibility() == VISIBLE) {
            if (hasShowContent) {
                hasShowContent = false;
                menu.dismiss();
            }

            if (displayWorker != null) {
                displayWorker.cancel();
            }
            ballView.setVisibility(GONE);
            frameLayout.setVisibility(GONE);
            rootLinearLayout.setVisibility(GONE);
            setVisibility(GONE);
        }
    }

    public void release() {
        if (displayWorker != null) {
            displayWorker.cancel();
            displayWorker = null;
        }

        frameLayout.removeAllViews();
        rootLinearLayout.removeAllViews();
        removeAllViews();
        wm.removeView(this);
    }

    private void handleFullScreenModel() {
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
            try {
                Field mLayoutInScreen = PopupWindow.class.getDeclaredField("mLayoutInScreen");
                mLayoutInScreen.setAccessible(true);
                mLayoutInScreen.set(menu, true);
            } catch (Exception e) {
                e.printStackTrace();
            }
        }
    }

    private int getStatusBarHeight(Context context) {
        int id = context.getResources().getIdentifier("status_bar_height", "dimen", "android");
        return context.getResources().getDimensionPixelSize(id);
    }

    /**
     * 适配小米Android O设备 判断是否是刘海屏
     */
    private boolean isNotch() {
        try {
            Method method = Class.forName("android.os.SystemProperties").getMethod("getInt", String.class, Integer.class);
            return ((int) method.invoke(null, "ro.miui.notch", 0)) == 1;
        } catch (Exception e) {
            e.printStackTrace();
        }
        return false;
    }

    /**
     * 判断是否为挖孔屏幕
     */
    private boolean isDisplayCutout(Activity activity) {
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.P) {
            try {
                return activity.getWindow().getDecorView().getRootWindowInsets().getDisplayCutout() != null;
            } catch (Exception e) {
                e.printStackTrace();
            }
        }
        return false;
    }

    @Override
    public boolean onTouch(View v, MotionEvent event) {
        if (displayWorker != null) {
            displayWorker.cancel();
        }
        float x = event.getRawX();
        float y = event.getRawY();

        switch (event.getAction()) {
            case MotionEvent.ACTION_DOWN:
                isMove = false;
                mTouchStartX = event.getX();
                mTouchStartY = event.getY();
                break;
            case MotionEvent.ACTION_MOVE:
                float moveStartX = event.getX();
                float moveStartY = event.getY();
                // 移动量大于3才判定移动
                if (Math.abs(mTouchStartX - moveStartX) > 3 && Math.abs(mTouchStartY - moveStartY) > 3) {
                    wlp.x = (int) (x - mTouchStartX);
                    wlp.y = (int) (y - mTouchStartY);
                    // 移动时收起菜单
                    wm.updateViewLayout(this, wlp);
                    isMove = true;
                    if (menu.isShowing()) {
                        menu.dismiss();
                        hasShowContent = false;
                    }
                    return false;
                }
                break;
            case MotionEvent.ACTION_UP:
                // 大于屏幕宽度一半是吸附在那一边
                v.performClick();
                if (wlp.x <= DensityUtils.getHeigthAndWidth(activity)[0] / 2) {
                    wlp.x = 0;
                    isLeftLocation = true;
                } else {
                    wlp.x = DensityUtils.getHeigthAndWidth(activity)[0];
                    isLeftLocation = false;
                }
                wm.updateViewLayout(this, wlp);
                if (isMove) {
                    invokeDisplayTimerWork();
                } else {
                    invokeMenuShowOrDismiss();
                }
                // 还原贴边的浮球
//                invokeBallFullOrHalt(false);
                callback.onUpdateBallView(ballView, isLeftLocation, false);
                break;
        }
        return true;
    }

    public interface IFloatingBallCallback {

        void onUpdateBallView(ImageView ballView, boolean isLeftLocation, boolean isHide);

        ArrayList<FloatingBallMenu.Item> onInitMenuData();

        void onMenuItemClick(FloatingBallMenu.Item item, int pos);

        void onExpandMenu(boolean hasRedDot);

        void onDismissMenu();
    }
}
