package dev.utils.app;

import android.annotation.SuppressLint;
import android.content.res.Configuration;
import android.net.wifi.WifiInfo;
import android.net.wifi.WifiManager;
import android.os.Build;
import android.provider.Settings;
import android.text.TextUtils;

import androidx.annotation.RequiresApi;
import androidx.annotation.RequiresPermission;

import java.io.BufferedReader;
import java.io.File;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.net.InetAddress;
import java.net.NetworkInterface;
import java.util.Arrays;
import java.util.Enumeration;
import java.util.HashMap;
import java.util.Iterator;
import java.util.Map;

import dev.utils.LogPrintUtils;

/**
 * detail: 设备相关工具类
 * @author Ttt
 * <pre>
 *     @see <a href="http://blog.csdn.net/zhangcanyan/article/details/52817866"/>
 *     android.os.Build.BOARD: 获取设备基板名称
 *     android.os.Build.BOOTLOADER: 获取设备引导程序版本号
 *     android.os.Build.BRAND: 获取设备品牌
 *     android.os.Build.CPU_ABI: 获取设备指令集名称 (CPU 的类型 )
 *     android.os.Build.CPU_ABI2: 获取第二个指令集名称
 *     android.os.Build.DEVICE: 获取设备驱动名称
 *     android.os.Build.DISPLAY: 获取设备显示的版本包 ( 在系统设置中显示为版本号 ) 和 ID 一样
 *     android.os.Build.FINGERPRINT: 设备的唯一标识, 由设备的多个信息拼接合成
 *     android.os.Build.HARDWARE: 设备硬件名称, 一般和基板名称一样 (BOARD)
 *     android.os.Build.HOST: 设备主机地址
 *     android.os.Build.ID: 设备版本号
 *     android.os.Build.MODEL : 获取手机的型号 设备名称
 *     android.os.Build.MANUFACTURER: 获取设备制造商
 *     android:os.Build.PRODUCT: 整个产品的名称
 *     android:os.Build.RADIO: 无线电固件版本号, 通常是不可用的 显示 unknown
 *     android.os.Build.TAGS: 设备标签, 如 release-keys 或测试的 test-keys
 *     android.os.Build.TIME: 时间
 *     android.os.Build.TYPE: 设备版本类型 主要为 "user" 或 "eng".
 *     android.os.Build.USER: 设备用户名 基本上都为 android-build
 *     android.os.Build.VERSION.RELEASE: 获取系统版本字符串, 如 4.1.2 或 2.2 或 2.3 等
 *     android.os.Build.VERSION.CODENAME: 设备当前的系统开发代号, 一般使用 REL 代替
 *     android.os.Build.VERSION.INCREMENTAL: 系统源代码控制值, 一个数字或者 git hash 值
 *     android.os.Build.VERSION.SDK: 系统的 API 级别 一般使用下面大的 SDK_INT 来查看
 *     android.os.Build.VERSION.SDK_INT: 系统的 API 级别 数字表示
 *     <p></p>
 *     所需权限
 *     <uses-permission android:name="android.permission.INTERNET" />
 *     <uses-permission android:name="android.permission.ACCESS_WIFI_STATE" />
 * </pre>
 */
public final class DeviceUtils {

    private DeviceUtils() {
    }

    // 日志 TAG
    private static final String TAG = DeviceUtils.class.getSimpleName();
    // 换行字符串
    private static final String NEW_LINE_STR = System.getProperty("line.separator");

    /**
     * 获取设备信息
     * @return {@link Map<String, String>}
     */
    public static Map<String, String> getDeviceInfo() {
        return getDeviceInfo(new HashMap<>());
    }

    /**
     * 获取设备信息
     * @param deviceInfoMap 设备信息 Map
     * @return {@link Map<String, String>}
     */
    public static Map<String, String> getDeviceInfo(final Map<String, String> deviceInfoMap) {
        // 获取设备信息类的所有申明的字段, 即包括 public、private 和 proteced, 但是不包括父类的申明字段
        Field[] fields = Build.class.getDeclaredFields();
        // 遍历字段
        for (Field field : fields) {
            try {
                // 取消 Java 的权限控制检查
                field.setAccessible(true);
                // 转换当前设备支持的 ABI - CPU 指令集
                if (field.getName().toLowerCase().startsWith("SUPPORTED".toLowerCase())) {
                    try {
                        Object object = field.get(null);
                        // 判断是否数组
                        if (object instanceof String[]) {
                            if (object != null) {
                                // 获取类型对应字段的数据, 并保存支持的指令集 [arm64-v8a, armeabi-v7a, armeabi]
                                deviceInfoMap.put(field.getName(), Arrays.toString((String[]) object));
                            }
                            continue;
                        }
                    } catch (Exception e) {
                    }
                }
                // 获取类型对应字段的数据, 并保存
                deviceInfoMap.put(field.getName(), field.get(null).toString());
            } catch (Exception e) {
                LogPrintUtils.eTag(TAG, e, "getDeviceInfo");
            }
        }
        return deviceInfoMap;
    }

    /**
     * 处理设备信息
     * @param deviceInfoMap 设备信息 Map
     * @param errorInfo     错误提示信息, 如获取设备信息失败
     * @return 拼接后的设备信息字符串
     */
    public static String handlerDeviceInfo(final Map<String, String> deviceInfoMap, final String errorInfo) {
        try {
            // 初始化 Builder, 拼接字符串
            StringBuilder builder = new StringBuilder();
            // 获取设备信息
            Iterator<Map.Entry<String, String>> mapIter = deviceInfoMap.entrySet().iterator();
            // 遍历设备信息
            while (mapIter.hasNext()) {
                // 获取对应的 key - value
                Map.Entry<String, String> rnEntry = mapIter.next();
                String rnKey = rnEntry.getKey(); // key
                String rnValue = rnEntry.getValue(); // value
                // 保存设备信息
                builder.append(rnKey);
                builder.append(" = ");
                builder.append(rnValue);
                builder.append(NEW_LINE_STR);
            }
            return builder.toString();
        } catch (Exception e) {
            LogPrintUtils.eTag(TAG, e, "handlerDeviceInfo");
        }
        return errorInfo;
    }

    // =

    /**
     * 获取设备基板名称
     * @return 设备基板名称
     */
    public static String getBoard() {
        return Build.BOARD;
    }

    /**
     * 获取设备引导程序版本号
     * @return 设备引导程序版本号
     */
    public static String getBootloader() {
        return Build.BOOTLOADER;
    }

    /**
     * 获取设备品牌
     * @return 设备品牌
     */
    public static String getBrand() {
        return Build.BRAND;
    }

    /**
     * 获取支持的第一个指令集
     * @return 支持的第一个指令集
     */
    public static String getCPU_ABI() {
        return Build.CPU_ABI;
    }

    /**
     * 获取支持的第二个指令集
     * @return 支持的第二个指令集
     */
    public static String getCPU_ABI2() {
        return Build.CPU_ABI2;
    }

    /**
     * 获取支持的指令集 如: [arm64-v8a, armeabi-v7a, armeabi]
     * @return 支持的指令集
     */
    public static String[] getABIs() {
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
            return Build.SUPPORTED_ABIS;
        } else {
            if (!TextUtils.isEmpty(Build.CPU_ABI2)) {
                return new String[]{Build.CPU_ABI, Build.CPU_ABI2};
            }
            return new String[]{Build.CPU_ABI};
        }
    }

    /**
     * 获取支持的 32 位指令集
     * @return 支持的 32 位指令集
     */
    public static String[] getSUPPORTED_32_BIT_ABIS() {
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
            return Build.SUPPORTED_32_BIT_ABIS;
        }
        return null;
    }

    /**
     * 获取支持的 64 位指令集
     * @return 支持的 64 位指令集
     */
    public static String[] getSUPPORTED_64_BIT_ABIS() {
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
            return Build.SUPPORTED_64_BIT_ABIS;
        }
        return null;
    }

    /**
     * 获取设备驱动名称
     * @return 设备驱动名称
     */
    public static String getDevice() {
        return Build.DEVICE;
    }

    /**
     * 获取设备显示的版本包 ( 在系统设置中显示为版本号 ) 和 ID 一样
     * @return 设备显示的版本包
     */
    public static String getDisplay() {
        return Build.DISPLAY;
    }

    /**
     * 获取设备的唯一标识, 由设备的多个信息拼接合成
     * @return 设备的唯一标识, 由设备的多个信息拼接合成
     */
    public static String getFingerprint() {
        return Build.FINGERPRINT;
    }

    /**
     * 获取设备硬件名称, 一般和基板名称一样 (BOARD)
     * @return 设备硬件名称, 一般和基板名称一样 (BOARD)
     */
    public static String getHardware() {
        return Build.HARDWARE;
    }

    /**
     * 获取设备主机地址
     * @return 设备主机地址
     */
    public static String getHost() {
        return Build.HOST;
    }

    /**
     * 获取设备版本号
     * @return 设备版本号
     */
    public static String getID() {
        return Build.ID;
    }

    /**
     * 获取设备型号 如 RedmiNote4X
     * @return 设备型号
     */
    public static String getModel() {
        String model = Build.MODEL;
        if (model != null) {
            model = model.trim().replaceAll("\\s*", "");
        } else {
            model = "";
        }
        return model;
    }

    /**
     * 获取设备厂商 如 Xiaomi
     * @return 设备厂商
     */
    public static String getManufacturer() {
        return Build.MANUFACTURER;
    }

    /**
     * 获取整个产品的名称
     * @return 整个产品的名称
     */
    public static String getProduct() {
        return Build.PRODUCT;
    }

    /**
     * 获取无线电固件版本号, 通常是不可用的 显示 unknown
     * @return 无线电固件版本号
     */
    public static String getRadio() {
        return Build.RADIO;
    }

    /**
     * 获取设备标签, 如 release-keys 或测试的 test-keys
     * @return 设备标签
     */
    public static String getTags() {
        return Build.TAGS;
    }

    /**
     * 获取设备时间
     * @return 设备时间
     */
    public static long getTime() {
        return Build.TIME;
    }

    /**
     * 获取设备版本类型 主要为 "user" 或 "eng".
     * @return 设备版本类型
     */
    public static String getType() {
        return Build.TYPE;
    }

    /**
     * 获取设备用户名 基本上都为 android-build
     * @return 设备用户名
     */
    public static String getUser() {
        return Build.USER;
    }

    // =

    /**
     * 获取 SDK 版本号
     * @return SDK 版本号
     */
    public static int getSDKVersion() {
        return Build.VERSION.SDK_INT;
    }

    /**
     * 获取系统版本号, 如 4.1.2 或 2.2 或 2.3 等
     * @return 系统版本号
     */
    public static String getRelease() {
        return Build.VERSION.RELEASE;
    }

    /**
     * 获取设备当前的系统开发代号, 一般使用 REL 代替
     * @return 设备当前的系统开发代号
     */
    public static String getCodename() {
        return Build.VERSION.CODENAME;
    }

    /**
     * 获取系统源代码控制值, 一个数字或者 git hash 值
     * @return 系统源代码控制值
     */
    public static String getIncremental() {
        return Build.VERSION.INCREMENTAL;
    }

    /**
     * 获取 Android id
     * <pre>
     *     在设备首次启动时, 系统会随机生成一个 64 位的数字, 并把这个数字以十六进制字符串的形式保存下来,
     *     这个十六进制的字符串就是 ANDROID_ID, 当设备被 wipe 后该值会被重置
     * </pre>
     * @return Android id
     */
    public static String getAndroidId() {
        String androidId = null;
        try {
            androidId = Settings.Secure.getString(ResourceUtils.getContentResolver(), Settings.Secure.ANDROID_ID);
        } catch (Exception e) {
            LogPrintUtils.eTag(TAG, e, "getAndroidId");
        }
        return androidId;
    }

    /**
     * 获取基带版本 BASEBAND-VER
     * @return 基带版本 BASEBAND-VER
     */
    public static String getBaseband_Ver() {
        String basebandVersion = "";
        try {
            Class clazz = Class.forName("android.os.SystemProperties");
            Object invoker = clazz.newInstance();
            Method method = clazz.getMethod("get", String.class, String.class);
            Object result = method.invoke(invoker, "gsm.version.baseband", "no message");
            basebandVersion = (String) result;
        } catch (Exception e) {
            LogPrintUtils.eTag(TAG, e, "getBaseband_Ver");
        }
        return basebandVersion;
    }

    /**
     * 获取内核版本 CORE-VER
     * @return 内核版本 CORE-VER
     */
    public static String getLinuxCore_Ver() {
        String kernelVersion = "";
        try {
            Process process = Runtime.getRuntime().exec("cat /proc/version");
            InputStream is = process.getInputStream();
            InputStreamReader isr = new InputStreamReader(is);
            BufferedReader br = new BufferedReader(isr, 8 * 1024);

            String line;
            StringBuilder builder = new StringBuilder();
            while ((line = br.readLine()) != null) {
                builder.append(line);
            }
            String result = builder.toString();
            if (result != "") {
                String keyword = "version ";
                int index = result.indexOf(keyword);
                line = result.substring(index + keyword.length());
                index = line.indexOf(" ");
                kernelVersion = line.substring(0, index);
            }
        } catch (Exception e) {
            LogPrintUtils.eTag(TAG, e, "getLinuxCore_Ver");
        }
        return kernelVersion;
    }

    // =

    /**
     * 判断设备是否 root
     * @return {@code true} yes, {@code false} no
     */
    public static boolean isDeviceRooted() {
        String su = "su";
        String[] locations = {"/system/bin/", "/system/xbin/", "/sbin/", "/system/sd/xbin/",
                "/system/bin/failsafe/", "/data/local/xbin/", "/data/local/bin/", "/data/local/"};
        for (String location : locations) {
            if (new File(location + su).exists()) {
                return true;
            }
        }
        return false;
    }

    /**
     * 获取是否启用 ADB
     * @return {@code true} yes, {@code false} no
     */
    @RequiresApi(api = Build.VERSION_CODES.JELLY_BEAN_MR1)
    public static boolean isAdbEnabled() {
        try {
            return Settings.Secure.getInt(ResourceUtils.getContentResolver(), Settings.Global.ADB_ENABLED, 0) > 0;
        } catch (Exception e) {
            LogPrintUtils.eTag(TAG, e, "isAdbEnabled");
        }
        return false;
    }

    // =

    // Default MAC address reported to a client that does not have the android.permission.LOCAL_MAC_ADDRESS permission.
    private static final String DEFAULT_MAC_ADDRESS = "02:00:00:00:00:00";

    /**
     * 获取设备 MAC 地址
     * <pre>
     *     没有打开 Wifi, 则获取 WLAN MAC 地址失败
     * </pre>
     * @return 设备 MAC 地址
     */
    @RequiresPermission(allOf = {android.Manifest.permission.INTERNET, android.Manifest.permission.ACCESS_WIFI_STATE})
    public static String getMacAddress() {
        String macAddress = getMacAddressByWifiInfo();
        if (!DEFAULT_MAC_ADDRESS.equals(macAddress)) {
            return macAddress;
        }
        macAddress = getMacAddressByNetworkInterface();
        if (!DEFAULT_MAC_ADDRESS.equals(macAddress)) {
            return macAddress;
        }
        macAddress = getMacAddressByInetAddress();
        if (!DEFAULT_MAC_ADDRESS.equals(macAddress)) {
            return macAddress;
        }
        macAddress = getMacAddressByFile();
        if (!DEFAULT_MAC_ADDRESS.equals(macAddress)) {
            return macAddress;
        }
        return null;
    }

    /**
     * 获取 MAC 地址
     * @return MAC 地址
     */
    @RequiresPermission(android.Manifest.permission.ACCESS_WIFI_STATE)
    private static String getMacAddressByWifiInfo() {
        try {
            @SuppressLint("WifiManagerLeak")
            WifiManager wifiManager = AppUtils.getWifiManager();
            if (wifiManager != null) {
                WifiInfo wifiInfo = wifiManager.getConnectionInfo();
                if (wifiInfo != null) return wifiInfo.getMacAddress();
            }
        } catch (Exception e) {
            LogPrintUtils.eTag(TAG, e, "getMacAddressByWifiInfo");
        }
        return DEFAULT_MAC_ADDRESS;
    }

    /**
     * 获取 MAC 地址
     * @return MAC 地址
     */
    @RequiresPermission(android.Manifest.permission.INTERNET)
    private static String getMacAddressByNetworkInterface() {
        try {
            Enumeration<NetworkInterface> nis = NetworkInterface.getNetworkInterfaces();
            while (nis.hasMoreElements()) {
                NetworkInterface ni = nis.nextElement();
                if (ni == null || !ni.getName().equalsIgnoreCase("wlan0")) continue;
                byte[] macBytes = ni.getHardwareAddress();
                if (macBytes != null && macBytes.length > 0) {
                    StringBuilder builder = new StringBuilder();
                    for (byte b : macBytes) {
                        builder.append(String.format("%02x:", b));
                    }
                    return builder.substring(0, builder.length() - 1);
                }
            }
        } catch (Exception e) {
            LogPrintUtils.eTag(TAG, e, "getMacAddressByNetworkInterface");
        }
        return DEFAULT_MAC_ADDRESS;
    }

    /**
     * 通过 InetAddress 获取 Mac 地址
     * @return Mac 地址
     */
    private static String getMacAddressByInetAddress() {
        try {
            InetAddress inetAddress = getInetAddress();
            if (inetAddress != null) {
                NetworkInterface ni = NetworkInterface.getByInetAddress(inetAddress);
                if (ni != null) {
                    byte[] macBytes = ni.getHardwareAddress();
                    if (macBytes != null && macBytes.length > 0) {
                        StringBuilder builder = new StringBuilder();
                        for (byte b : macBytes) {
                            builder.append(String.format("%02x:", b));
                        }
                        return builder.substring(0, builder.length() - 1);
                    }
                }
            }
        } catch (Exception e) {
            LogPrintUtils.eTag(TAG, e, "getMacAddressByInetAddress");
        }
        return DEFAULT_MAC_ADDRESS;
    }

    /**
     * 获取 InetAddress
     * @return {@link InetAddress}
     */
    private static InetAddress getInetAddress() {
        try {
            Enumeration<NetworkInterface> nis = NetworkInterface.getNetworkInterfaces();
            while (nis.hasMoreElements()) {
                NetworkInterface ni = nis.nextElement();
                // To prevent phone of xiaomi return "10.0.2.15"
                if (!ni.isUp()) continue;
                Enumeration<InetAddress> addresses = ni.getInetAddresses();
                while (addresses.hasMoreElements()) {
                    InetAddress inetAddress = addresses.nextElement();
                    if (!inetAddress.isLoopbackAddress()) {
                        String hostAddress = inetAddress.getHostAddress();
                        if (hostAddress.indexOf(':') < 0) return inetAddress;
                    }
                }
            }
        } catch (Exception e) {
            LogPrintUtils.eTag(TAG, e, "getInetAddress");
        }
        return null;
    }

    /**
     * 获取 MAC 地址
     * @return MAC 地址
     */
    private static String getMacAddressByFile() {
        ShellUtils.CommandResult result = ShellUtils.execCmd("getprop wifi.interface", false);
        if (result.isSuccess()) {
            String name = result.successMsg;
            if (name != null) {
                result = ShellUtils.execCmd("cat /sys/class/net/" + name + "/address", false);
                if (result.result == 0) {
                    String address = result.successMsg;
                    if (address != null && address.length() > 0) {
                        return address;
                    }
                }
            }
        }
        return DEFAULT_MAC_ADDRESS;
    }

    // =

    /**
     * 关机 ( 需要 root 权限 )
     * @return {@code true} success, {@code false} fail
     */
    public static boolean shutdown() {
        return ADBUtils.shutdown();
    }

    /**
     * 重启设备 ( 需要 root 权限 )
     * @return {@code true} success, {@code false} fail
     */
    public static boolean reboot() {
        return ADBUtils.reboot();
    }

    /**
     * 重启设备 ( 需要 root 权限 ) - 并进行特殊的引导模式 (recovery、Fastboot)
     * @param reason 传递给内核来请求特殊的引导模式, 如 "recovery"
     *               重启到 Fastboot 模式 bootloader
     * @return {@code true} success, {@code false} fail
     */
    public static boolean reboot(final String reason) {
        return ADBUtils.reboot(reason);
    }

    /**
     * 重启引导到 recovery ( 需要 root 权限 )
     * @return {@code true} success, {@code false} fail
     */
    public static boolean rebootToRecovery() {
        return ADBUtils.rebootToRecovery();
    }

    /**
     * 重启引导到 bootloader ( 需要 root 权限 )
     * @return {@code true} success, {@code false} fail
     */
    public static boolean rebootToBootloader() {
        return ADBUtils.rebootToBootloader();
    }

    // =

    /**
     * 判断是否是平板
     * @return {@code true} yes, {@code false} no
     */
    public static boolean isTablet() {
        try {
            return (ResourceUtils.getConfiguration().screenLayout & Configuration.SCREENLAYOUT_SIZE_MASK) >= Configuration.SCREENLAYOUT_SIZE_LARGE;
        } catch (Exception e) {
            LogPrintUtils.eTag(TAG, e, "isTablet");
        }
        return false;
    }
}