package com.ryuunoakaihitomi.rebootmenu.util.ui;

import android.annotation.SuppressLint;
import android.content.Context;
import android.content.ContextWrapper;
import android.os.Build;
import android.view.Display;
import android.view.Gravity;
import android.view.View;
import android.view.ViewGroup;
import android.view.WindowManager;
import android.widget.Toast;

import com.ryuunoakaihitomi.rebootmenu.util.DebugLog;
import com.ryuunoakaihitomi.rebootmenu.util.SpecialSupport;

import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;

import androidx.annotation.NonNull;

import static com.ryuunoakaihitomi.rebootmenu.util.DebugLog.LogLevel.D;
import static com.ryuunoakaihitomi.rebootmenu.util.DebugLog.LogLevel.I;

/**
 * 快速生成默认文本烤面包片的工具类
 * Created by ZQY on 2018/2/8.
 *
 * @author ZQY
 * @version 1.3
 * @see android.widget.Toast
 */

@SuppressLint("ShowToast")
@SuppressWarnings("JavaReflectionMemberAccess")
public class TextToast {
    private static final String TAG = "TextToast";

    /**
     * 文本toast生成
     *
     * @param context 上下文
     * @param isLong  是否是持续时间较长的toast
     * @param message 显示的文本内容
     */
    public TextToast(Context context, boolean isLong, String message) {
        toastCompat(context, message, isLong, false);
    }

    /**
     * 短暂文本toast生成
     *
     * @param context 上下文
     * @param message 文本内容
     */
    public TextToast(Context context, String message) {
        toastCompat(context, message, false, false);
    }

    /**
     * 中心位置toast生成
     *
     * @param context  上下文
     * @param isLong   是否是持续时间较长的toast
     * @param message  文本内容
     * @param isCenter 是否显示在中心位置
     */
    public TextToast(@NonNull Context context, boolean isLong, String message, boolean isCenter) {
        toastCompat(context, message, isLong, isCenter);
    }

    /**
     * toast兼容适配
     *
     * @param context  {@link Context}
     * @param text     文本信息
     * @param isLong   是否是持续时间较长的toast
     * @param isCenter 是否显示在中心位置
     */
    private static void toastCompat(Context context, CharSequence text, boolean isLong, boolean isCenter) {
        boolean isMI = SpecialSupport.isMIUI();
        //MIUI的Toast在文本前面添加应用名称很不合理,因为Toast显示时间有限,需要尽快让用户注意最重要的内容
        Toast toast = Toast.makeText(context.getApplicationContext(), isMI ? null : text, isLong ? Toast.LENGTH_LONG : Toast.LENGTH_SHORT);
        if (isMI) toast.setText(text);
        if (isCenter) toast.setGravity(Gravity.CENTER, 0, 0);
        fixBadTokenException(context, toast);
        toast.show();
    }

    /**
     * https://blog.csdn.net/qq331710168/article/details/85320098
     * 伪装成系统Toast以躲避通知权限检查...
     * 调用影响全局
     */
    @SuppressLint("PrivateApi")
    public static void defineSystemToast() {
        try {
            Method getServiceMethod = Toast.class.getDeclaredMethod("getService");
            getServiceMethod.setAccessible(true);
            Object iNotificationManagerObj = getServiceMethod.invoke(null);
            Class iNotificationManagerCls = Class.forName("android.app.INotificationManager");
            Object iNotificationManagerProxy = Proxy.newProxyInstance(Thread.currentThread().getContextClassLoader(), new Class[]{iNotificationManagerCls}, (proxy, method, args) -> {
                //强制使用系统Toast
                if ("enqueueToast".equals(method.getName())
                        || "enqueueToastEx".equals(method.getName())) {  //华为p20 pro上为enqueueToastEx
                    //enqueueToastEx确认用,暂时没有以上设备
                    new DebugLog(TAG, "methodName:" + method.getName() + " D" + args[2], null);
                    args[0] = "android";
                }
                return method.invoke(iNotificationManagerObj, args);
            });
            Field sServiceField = Toast.class.getDeclaredField("sService");
            sServiceField.setAccessible(true);
            sServiceField.set(null, iNotificationManagerProxy);
        } catch (Exception e) {
            new DebugLog(e, TAG + "defineSystemToast: ", false);
        }
    }

    /**
     * --------------------------------------
     * https://github.com/drakeet/ToastCompat
     */
    private static void fixBadTokenException(Context context, Toast toast) {
        if (Build.VERSION.SDK_INT == 25) {
            try {
                Field field = View.class.getDeclaredField("mContext");
                field.setAccessible(true);
                field.set(toast.getView(), new SafeToastContext(context));
            } catch (Throwable throwable) {
                throwable.printStackTrace();
            }
        }
    }

    private final static class SafeToastContext extends ContextWrapper {


        SafeToastContext(@NonNull Context base) {
            super(base);
        }


        @Override
        public Context getApplicationContext() {
            return new ApplicationContextWrapper(getBaseContext().getApplicationContext());
        }

        private final class ApplicationContextWrapper extends ContextWrapper {

            private ApplicationContextWrapper(@NonNull Context base) {
                super(base);
            }


            @Override
            public Object getSystemService(@NonNull String name) {
                if (Context.WINDOW_SERVICE.equals(name)) {
                    // noinspection ConstantConditions
                    return new WindowManagerWrapper((WindowManager) getBaseContext().getSystemService(name));
                }
                return super.getSystemService(name);
            }
        }


        private final class WindowManagerWrapper implements WindowManager {

            private final @NonNull
            WindowManager base;


            private WindowManagerWrapper(@NonNull WindowManager base) {
                this.base = base;
            }


            @Override
            public Display getDefaultDisplay() {
                return base.getDefaultDisplay();
            }


            @Override
            public void removeViewImmediate(View view) {
                base.removeViewImmediate(view);
            }


            @Override
            public void addView(View view, ViewGroup.LayoutParams params) {
                try {
                    new DebugLog(TAG, "WindowManager's addView(view, params) has been hooked.", D);
                    base.addView(view, params);
                } catch (BadTokenException e) {
                    new DebugLog(TAG, e.getMessage(), I);
                } catch (Throwable throwable) {
                    new DebugLog(throwable, TAG + "[addView]", true);
                }
            }


            @Override
            public void updateViewLayout(View view, ViewGroup.LayoutParams params) {
                base.updateViewLayout(view, params);
            }


            @Override
            public void removeView(View view) {
                base.removeView(view);
            }
        }
    }
}