package com.grkj.iscs.util

import android.content.Context
import android.content.Intent
import android.content.pm.PackageManager
import android.os.Build
import android.os.Looper
import com.grkj.iscs.util.log.LogUtil
import java.io.*
import java.lang.reflect.Field
import java.text.DateFormat
import java.text.SimpleDateFormat

class CrashUtil : Thread.UncaughtExceptionHandler {
    var mContext: Context? = null
    var mDefaultHandler: Thread.UncaughtExceptionHandler? = null

    // 用来存储设备信息和异常信息
    private val infos: MutableMap<String, String> = mutableMapOf()

    // 用于格式化日期,作为日志文件名的一部分
    private val formatter: DateFormat = SimpleDateFormat("yyyyMMdd-HHmmss")

    companion object {
        val instance by lazy(mode = LazyThreadSafetyMode.SYNCHRONIZED) { CrashUtil() }
    }

    fun init(pContext: Context) {
        this.mContext = pContext
        // 获取系统默认的UncaughtException处理器
        mDefaultHandler = Thread.getDefaultUncaughtExceptionHandler()
        // 设置该CrashHandler为程序的默认处理器
        Thread.setDefaultUncaughtExceptionHandler(this)
    }

    //当UncaughtException发生时会转入该函数来处理
    override fun uncaughtException(t: Thread?, e: Throwable?) {
        if (!handleException(e) && mDefaultHandler != null) {
            //如果用户没有处理则让系统默认的异常处理器来处理
            mDefaultHandler?.uncaughtException(t, e)
        } else {
            try {
                //给Toast留出时间
                Thread.sleep(2000)
            } catch (e: InterruptedException) {
                e.printStackTrace()
            }
            mContext?.let {
                val intent = it.packageManager.getLaunchIntentForPackage(it.packageName);
                intent?.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP);
                it.startActivity(intent)
            }

            //退出程序
//            App.i.removeAllActivity()
            android.os.Process.killProcess(android.os.Process.myPid())
            System.exit(0)

        }

    }

    fun handleException(ex: Throwable?): Boolean {
        if (ex == null) {
            return false
        }
        Thread {
            Looper.prepare()
            ToastUtils.tip("很抱歉,程序出现异常,即将退出")
            Looper.loop()
        }.start()
        //收集设备参数信息
        mContext?.let {
            collectDeviceInfo(it)
            //保存日志文件
            saveCrashInfo2File(ex, it)
        }
        // 注：收集设备信息和保存日志文件的代码就没必要在这里贴出来了
        //文中只是提供思路，并不一定必须收集信息和保存日志
        //因为现在大部分的项目里都集成了第三方的崩溃分析日志，如`Bugly` 或 `啄木鸟等`
        //如果自己定制化处理的话，反而比较麻烦和消耗精力，毕竟开发资源是有限的
        return true
    }

    /**
     * 收集设备参数信息
     *
     * @param ctx
     */
    private fun collectDeviceInfo(ctx: Context) {
        try {
            val pm = ctx.packageManager
            val pi = pm.getPackageInfo(ctx.packageName, PackageManager.GET_ACTIVITIES)
            pi?.let {
                val versionName = if (it.versionName == null) "null" else it.versionName
                val versionCode = it.versionCode.toString()
                infos["versionName"] = versionName
                infos["versionCode"] = versionCode
            }
        } catch (e: PackageManager.NameNotFoundException) {
            LogUtil.e(e)
        }
        val fields: Array<Field> = Build::class.java.declaredFields
        fields?.forEach {
            try {
                it.isAccessible = true
                infos[it.name] = it.get(null).toString()
            } catch (e: Exception) {
                LogUtil.e(e)
            }
        }
    }

    /**
     * 保存错误信息到文件中
     *
     * @param ex
     * @return 返回文件名称,便于将文件传送到服务器
     */
    private fun saveCrashInfo2File(ex: Throwable, ctx: Context): String? {
        val sb = StringBuffer()
        infos.forEach {
            sb.append("${it.key}=${it.value}\n")
        }
        val writer: Writer = StringWriter()
        val printWriter = PrintWriter(writer)
        ex.printStackTrace(printWriter)
        var cause = ex.cause
        while (cause != null) {
            cause.printStackTrace(printWriter)
            cause = cause.cause
        }
        printWriter.close()
        val result: String = writer.toString()
        sb.append(result)
        return null
    }
}