package com.kfzs.duanduan.utils;

import android.annotation.SuppressLint;
import android.content.Context;
import android.content.Intent;
import android.content.pm.ApplicationInfo;
import android.content.pm.PackageInfo;
import android.content.pm.PackageManager;
import android.content.pm.Signature;
import android.graphics.drawable.Drawable;
import android.net.Uri;
import android.text.TextUtils;
import android.util.Base64;
import android.view.View;

import com.sheep.gamegroup.absBase.AbsObserver;
import com.sheep.gamegroup.model.entity.ApkFileInfo;
import com.sheep.gamegroup.model.entity.DialogConfig;
import com.sheep.gamegroup.util.ActivityManager;
import com.sheep.gamegroup.util.DataKey;
import com.sheep.gamegroup.util.DataUtil;
import com.sheep.gamegroup.util.ViewUtil;
import com.sheep.jiuyan.samllsheep.SheepApp;
import com.sheep.jiuyan.samllsheep.utils.G;
import com.sheep.jiuyan.samllsheep.utils.PackageUtil;

import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.UUID;

import io.reactivex.Observable;
import io.reactivex.android.schedulers.AndroidSchedulers;
import io.reactivex.functions.Predicate;
import io.reactivex.schedulers.Schedulers;
import rx.functions.Action1;

/**
 * apk helper
 * <p>
 * Created by HooRang on 2017/2/23.
 */
public class ApkUtils {

    private Map<String, PackageInfo> installedApkContainer;

    private static ApkUtils apkUtil;

    public static ApkUtils getInstance() {
        if (null == apkUtil) {
            apkUtil = new ApkUtils();
            apkUtil.setInstalledApkContainer();
        }
        return apkUtil;

    }


    public void clear() {
        installedApkContainer = null;

    }

    private void setInstalledApkContainer() {
        installedApkContainer = getInstalledApks(SheepApp.getInstance());

    }

    /**
     * get UnInstallApkPackageName
     *
     * @param context Context
     * @param apkPath apkPath
     * @return apk PackageName
     */
    public static String getUnInstallApkPackageName(Context context, String apkPath) {
        PackageManager pm = context.getPackageManager();
        PackageInfo info = pm.getPackageArchiveInfo(apkPath, PackageManager.GET_ACTIVITIES);
        if (info != null) {
            ApplicationInfo appInfo = info.applicationInfo;
            if (appInfo != null) {
                return appInfo.packageName;
            }
        }
        return null;
    }

    /**
     * install an apk bu apkPath
     *
     * @param context Context
     * @param apkPath apkPath
     */
    public static final void installApk(final Context context, final String apkPath) {
        final ApkFileInfo apkFileInfo = new ApkFileInfo();
        apkFileInfo.setPath(apkPath);
        Observable.just(apkFileInfo)
                .filter(new Predicate<ApkFileInfo>() {
                    @Override
                    public boolean test(ApkFileInfo item){
                        return !TextUtils.isEmpty(item.getPath());
                    }
                })
                .filter(new Predicate<ApkFileInfo>() {
                    @Override
                    public boolean test(ApkFileInfo item) {
                        return item.existsPath();
                    }
                })
                .filter(new Predicate<ApkFileInfo>() {
                    @Override
                    public boolean test(ApkFileInfo item) {
                        return !TextUtils.isEmpty(item.initPackageNameFromPath(context).getPackageName());
                    }
                })
                .filter(new Predicate<ApkFileInfo>() {
                    @Override
                    public boolean test(ApkFileInfo item) {
                        //已经安装且签名的md5相同，文件与安装应用的md5也相同，则过滤掉
                        return !(item.checkInstall(context) && item.checkApkAndFileSignMd5() && item.checkApkAndFileMd5());
                    }
                })
                .subscribeOn(Schedulers.io())
                .observeOn(AndroidSchedulers.mainThread())
                .subscribe(new AbsObserver<ApkFileInfo>() {
                    private boolean doneNext;
                    @Override
                    public void onComplete() {
                        if(doneNext)
                            return;
                        if(apkFileInfo.isAppInstalled()){//在onNext中未处理且应用已经安装的情况下，直接打开应用
                            PackageUtil.startApp(context, apkFileInfo.getPackageName());
                            return;
                        }
                        G.showToast("无需安装");
                    }

                    @Override
                    public void onError(Throwable e) {
                        G.showToast(e.getMessage());
                    }

                    @Override
                    public void onNext(final ApkFileInfo item) {
                        doneNext = true;
                        if (!item.isAppInstalled() || (item.isEqualsSignMd5() && item.isCanInstallVersion())) {
                            PackageUtil.installApk(context, item.getPath());
                            DataUtil.putAsString(DataKey.KEY_INSTALL_APK_PACKAGE_NAME, item.getPackageName());
                        } else {//签名不同，则提示卸载
                            ViewUtil.showMsgDialog(ActivityManager.getInstance().currentActivity(), new DialogConfig()
                                    .setTitle("提示").setMsg("需要卸载当前应用才能继续安装，是否继续").setBtnLeftText("不用了")
                                    .setBtnRightText("继续").setBtnRightOnClickListener(new View.OnClickListener() {
                                        @Override
                                        public void onClick(View view) {
                                            //这里卸载后 没法继续，需要在接收卸载广播的地方进行安装操作
                                            DataUtil.putAsString(DataKey.KEY_WILL_INSTALL_APK_PATH, item.getPath());
                                            DataUtil.putAsString(DataKey.KEY_WILL_INSTALL_APK_PACKAGE_NAME, item.getPackageName());
                                            uninstallApk(SheepApp.getInstance(), item.getPackageName());
                                        }
                                    }));
                        }
                    }
                });
    }

    /**
     * MD5加密
     *
     * @param byteStr 需要加密的内容
     * @return 返回 byteStr的md5值
     */
    public static String encryptionMD5(byte[] byteStr) {
        MessageDigest messageDigest;
        StringBuilder md5Sb = new StringBuilder();
        try {
            messageDigest = MessageDigest.getInstance("MD5");
            messageDigest.reset();
            messageDigest.update(byteStr);
            byte[] byteArray = messageDigest.digest();
//            return Base64.encodeToString(byteArray,Base64.NO_WRAP);
            for (int i = 0; i < byteArray.length; i++) {
                if (Integer.toHexString(0xFF & byteArray[i]).length() == 1) {
                    md5Sb.append("0").append(Integer.toHexString(0xFF & byteArray[i]));
                } else {
                    md5Sb.append(Integer.toHexString(0xFF & byteArray[i]));
                }
            }
        } catch (NoSuchAlgorithmException e) {
            e.printStackTrace();
        }
        return md5Sb.toString();
    }

    /**
     * 获取app签名md5值
     */
    public static String getApkSignMd5StrByPackageName(String packageName, Action1<PackageInfo> action1) {
        try {
            PackageInfo packageInfo = SheepApp.getInstance().getPackageManager().getPackageInfo(
                    packageName, PackageManager.GET_ACTIVITIES | PackageManager.GET_SIGNATURES);
            Signature[] signs = packageInfo.signatures;
            Signature sign = signs[0];
            String signStr = encryptionMD5(sign.toByteArray());
            if (action1 != null)
                action1.call(packageInfo);
            return signStr;
        } catch (PackageManager.NameNotFoundException e) {
            e.printStackTrace();
        } catch (Exception e2) {
            e2.printStackTrace();
        }
        return "";
    }

    /**
     * 获取未安装app签名md5值
     */
    public static String getApkSignMd5StrByPath(String apkPath, Action1<PackageInfo> action1) {
        try {
            PackageInfo packageInfo = SheepApp.getInstance().getPackageManager().getPackageArchiveInfo(
                    apkPath, PackageManager.GET_ACTIVITIES | PackageManager.GET_SIGNATURES);
            Signature[] signs = packageInfo.signatures;
            Signature sign = signs[0];
            String signStr = encryptionMD5(sign.toByteArray());
            if (action1 != null)
                action1.call(packageInfo);
            return signStr;
        } catch (Exception e) {
            e.printStackTrace();
        }
        return "";
    }

    /* 卸载apk */
    public static void uninstallApk(Context context, String packageName) {
        Uri uri = Uri.parse("package:" + packageName);
        Intent intent = new Intent(Intent.ACTION_DELETE, uri);
        intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
        context.startActivity(intent);
    }

    /**
     * get alll installed apks
     *
     * @param context
     * @return
     */
    public static Map<String, PackageInfo> getInstalledApks(Context context) {

        Map<String, PackageInfo> apkContainer = new HashMap<>();
        if (context == null) {

            return apkContainer;
        }

        List<PackageInfo> installPkgs = context.getPackageManager().getInstalledPackages(0);
        for (PackageInfo apk : installPkgs) {
            apkContainer.put(apk.packageName, apk);
        }
        return apkContainer;
    }


    /**
     * Open a application by packagename
     *
     * @param context
     * @param pkgName
     */
    public static void openApplication(Context context, String pkgName) {

        Intent intent = context.getPackageManager().getLaunchIntentForPackage(pkgName);
        context.startActivity(intent);
    }


    /**
     * @param packageName
     * @return
     */
    public boolean isPkgInstalled(String packageName) {

        if (null == installedApkContainer || installedApkContainer.isEmpty()) {
            setInstalledApkContainer();
        }

        return installedApkContainer.containsKey(packageName);
    }


    /**
     * Has the apk need update that's installed already
     *
     * @param packageName
     * @param versionCode
     * @return
     */
    public boolean isPkgNeedUpdate(String packageName, int versionCode) {

        if (null == installedApkContainer || installedApkContainer.isEmpty()) {
            setInstalledApkContainer();
        }
        PackageInfo pkg = installedApkContainer.get(packageName);

        return pkg.versionCode < versionCode ? true : false;

    }

    /**
     * compare installed package's versionCode
     * <p>
     * 0  ： euqal
     * 1  : gt
     * -1  : lt
     *
     * @param packageName
     * @param versionCode
     * @return
     */
    public int comparePkgVersionCode(String packageName, int versionCode) {
        if (null == installedApkContainer || installedApkContainer.isEmpty()) {
            setInstalledApkContainer();
        }
        PackageInfo pkg = installedApkContainer.get(packageName);
        if (null == pkg) {
            return EQUALS;
        }
        if (pkg.versionCode == versionCode) {
            return EQUALS;
        } else if (pkg.versionCode < versionCode) {
            return GT;
        } else {
            return LT;
        }
    }

    /**
     * equals
     */
    public static final int EQUALS = 0;

    /**
     * great that
     */
    public static final int GT = 1;

    /**
     * less that
     */
    public static final int LT = -1;

    /**
     * Get application's icon by packagename
     *
     * @param context
     * @param packageName
     * @return
     */
    public Drawable getIconByPackageName(Context context, String packageName) {
        if (null == installedApkContainer || installedApkContainer.isEmpty()) {
            setInstalledApkContainer();
        }
        return installedApkContainer.get(packageName).applicationInfo.loadIcon(context.getPackageManager());
    }

    public Drawable getAppIconOnce(Context context, String packageName) {
        try {
            PackageManager packageManager = context.getPackageManager();
            PackageInfo packageInfo = packageManager.getPackageInfo(packageName, PackageManager.GET_ACTIVITIES);
            return packageInfo.applicationInfo.loadIcon(packageManager);
        } catch (PackageManager.NameNotFoundException e) {
            e.printStackTrace();
            return null;
        }
    }

    public String getAppLabelOnce(Context context, String packageName) {
        try {
            PackageManager packageManager = context.getPackageManager();
            PackageInfo packageInfo = packageManager.getPackageInfo(packageName, PackageManager.GET_ACTIVITIES);
            return packageInfo.applicationInfo.loadLabel(packageManager).toString();
        } catch (PackageManager.NameNotFoundException e) {
            e.printStackTrace();
            return null;
        }
    }


    /**
     * Get application's appname by packagename
     *
     * @param context
     * @param packageName
     * @return
     */
    public String getAppNameByPackageName(Context context, String packageName) {
        if (null == installedApkContainer || installedApkContainer.isEmpty()) {
            setInstalledApkContainer();
        }
        return installedApkContainer.get(packageName).applicationInfo.loadLabel(context.getPackageManager()).toString();
    }

    public static String getApkSignatureByPackageName(Context ctx, String packageName) {
        if (TextUtils.isEmpty(packageName)) {
            return null;
        }
        String res = null;
        try {
            @SuppressLint("PackageManagerGetSignatures") PackageInfo info = ctx.getPackageManager().getPackageInfo(
                    packageName, PackageManager.GET_SIGNATURES);
            for (Signature signature : info.signatures) {
                MessageDigest md = MessageDigest.getInstance("SHA");
                md.update(signature.toByteArray());
                res = Base64.encodeToString(md.digest(), Base64.DEFAULT);
            }
        } catch (PackageManager.NameNotFoundException | NoSuchAlgorithmException e) {
            e.printStackTrace();
        }
        return res;
    }


    public static String getCurrentPkgVersionName(Context context) {
        try {
            PackageManager pm = context.getPackageManager();
            PackageInfo pi = pm.getPackageInfo(context.getPackageName(), 0);
            return pi.versionName;
        } catch (Exception e) {
            return "0.0.0";
        }
    }

    public static PackageInfo getPackageInfo(String packageName) {
        try {
            PackageManager pm = SheepApp.getInstance().getPackageManager();
            PackageInfo pi = pm.getPackageInfo(packageName, 0);
            return pi;
        } catch (Exception e) {
            return null;
        }
    }


    public static int getCurrentPkgVersionCode(Context context) {
        try {
            PackageManager pm = context.getPackageManager();
            PackageInfo pi = pm.getPackageInfo(context.getPackageName(), 0);
            return pi.versionCode;
        } catch (Exception e) {
            return 0;
        }
    }


    public static void invokeBroswer(Context context, String targetUrl) {
        Uri uri = Uri.parse(targetUrl);
        Intent intent = new Intent(Intent.ACTION_VIEW);
        intent.setData(uri);
        context.startActivity(intent);
    }

    public static String getUUID() {
        return UUID.randomUUID().toString();
    }

}
