package com.gh.gamecenter.core.utils;

import android.app.Application;
import android.os.Build;
import android.text.TextUtils;

import androidx.annotation.Nullable;

import java.io.BufferedReader;
import java.io.FileReader;
import java.lang.reflect.Method;

public class ProcessUtil {
    private static String currentProcessName;

    /**
     * @return 当前进程名
     */
    @Nullable
    public static String getCurrentProcessName() {
        if (!TextUtils.isEmpty(currentProcessName)) {
            return currentProcessName;
        }

        //0)通过反射 ActivityThread 获取实例再获取当前进程名
        currentProcessName = getCurrentProcessNameByActivityThreadInstance();
        if (!TextUtils.isEmpty(currentProcessName)) {
            return currentProcessName;
        }

        //1)通过 CMD 获取当前进程名
        currentProcessName = getCurrentProcessNameByCMD(android.os.Process.myPid());
        if (!TextUtils.isEmpty(currentProcessName)) {
            return currentProcessName;
        }

        //2)通过 Application 的 API 获取当前进程名
        currentProcessName = getCurrentProcessNameByApplication();
        if (!TextUtils.isEmpty(currentProcessName)) {
            return currentProcessName;
        }

        //3)通过反射 ActivityThread 获取当前进程名
        currentProcessName = getCurrentProcessNameByActivityThread();
        if (!TextUtils.isEmpty(currentProcessName)) {
            return currentProcessName;
        }

        return currentProcessName;
    }

    /**
     * 通过Application新的API获取进程名，无需反射，无需IPC，效率最高。
     */
    private static String getCurrentProcessNameByApplication() {
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.P) {
            return Application.getProcessName();
        }
        return null;
    }

    /**
     * 通过反射ActivityThread获取进程名，避免了ipc
     */
    private static String getCurrentProcessNameByActivityThread() {
        String processName = null;
        try {
            final Method declaredMethod = Class.forName("android.app.ActivityThread", false, Application.class.getClassLoader())
                    .getDeclaredMethod("currentProcessName", (Class<?>[]) new Class[0]);
            declaredMethod.setAccessible(true);
            final Object invoke = declaredMethod.invoke(null, new Object[0]);
            if (invoke instanceof String) {
                processName = (String) invoke;
            }
        } catch (Throwable e) {
            e.printStackTrace();
        }
        return processName;
    }

    private static String getCurrentProcessNameByActivityThreadInstance() {
        try {
            // Get ActivityThread class
            Class<?> activityThreadClass = Class.forName("android.app.ActivityThread",false, Application.class.getClassLoader());

            // Get the current ActivityThread instance
            Method currentActivityThreadMethod = activityThreadClass.getDeclaredMethod("currentActivityThread");
            currentActivityThreadMethod.setAccessible(true);
            Object activityThread = currentActivityThreadMethod.invoke(null);

            // Get the getProcessName method
            Method getProcessNameMethod = activityThreadClass.getDeclaredMethod("getProcessName");
            getProcessNameMethod.setAccessible(true);

            // Call the getProcessName method
            return (String) getProcessNameMethod.invoke(activityThread);
        } catch (Exception e) {
            e.printStackTrace();
            return null;
        }
    }

    private static String getCurrentProcessNameByCMD(int pid) {
        //get from /proc/PID/cmdline
        BufferedReader br = null;
        try {
            br = new BufferedReader(new FileReader("/proc/" + pid + "/cmdline"));
            String processName = br.readLine();
            if (!TextUtils.isEmpty(processName)) {
                processName = processName.trim();
                if (!TextUtils.isEmpty(processName)) {
                    return processName; //OK
                }
            }
        } catch (Exception ignored) {

        } finally {
            try {
                if (br != null) {
                    br.close();
                }
            } catch (Exception ignored) {
            }
        }
        //failed
        return null;
    }

}