package com.example.base.viewmodel

import android.app.Activity
import androidx.lifecycle.ViewModel
import androidx.lifecycle.viewModelScope
import com.example.base.common.ActivityManager
import com.example.base.livedata.DialogLiveEvent
import com.example.base.livedata.SingleLiveEvent
import com.example.base.utils.L
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Job
import kotlinx.coroutines.coroutineScope
import kotlinx.coroutines.launch

/**
 * 文件名:    BaseViewModel
 * 创建时间:  2021-08-06 on 10:44
 * 描述:     ViewModel基类
 */

abstract class BaseViewModel : ViewModel() {

    /**
     * 用来通知 Activity／Fragment 是否显示等待Dialog
     */
    val showDialogLiveEvent: DialogLiveEvent = DialogLiveEvent()

    /**
     * 出现异常 用来通知 Activity／Fragment
     */
    val exceptionLiveEvent: SingleLiveEvent<Throwable> = SingleLiveEvent()

    /**
     * 用来通知 Activity／Fragment finish
     */
    val finishActivityLiveEvent: SingleLiveEvent<ViewModel> = SingleLiveEvent()

    fun setError(e: Throwable?) {
        exceptionLiveEvent.value = e!!
    }

    fun setError(e: String?) {
        exceptionLiveEvent.value = Exception(e)
    }

    fun showDialog(cancelable: Boolean = false) {
        showDialogLiveEvent.setValue(true, cancelable)
    }

    fun setMessage(msg: String, cancelable: Boolean = false) {
        showDialogLiveEvent.setValue(true, msg, cancelable)
    }

    fun dismissDialog() {
        showDialogLiveEvent.setValue(false)
    }

    fun finishActivity() {
        finishActivityLiveEvent.value = this
    }

    fun getActivity(): Activity? {
        return ActivityManager.lastOrNull()
    }

    /**
     * @desc 启动协程处理网络请求事件
     * 此方法会自动捕捉异不处理
     */
    fun launchOnUi(tryBlock: suspend CoroutineScope.() -> Unit): Job {
        return viewModelScope.launch {
            tryCatch(tryBlock, {}, {})
        }
    }

    /**
     * @desc 启动协程处理网络请求事件
     * 用户自己处理错误 错误回调
     * @see catchBlock
     */
    fun launchOnUiTryCatch(
        tryBlock: suspend CoroutineScope.() -> Unit,
        catchBlock: suspend CoroutineScope.(Throwable) -> Unit
    ): Job {

        return viewModelScope.launch {
            try {
                tryBlock()
            } catch (e: Throwable) {
                catchBlock(e)
            }
        }
    }

    /**
     * 异常处理
     */
    private suspend fun tryCatch(
        tryBlock: suspend CoroutineScope.() -> Unit,
        catchBlock: suspend CoroutineScope.(Throwable) -> Unit,
        finallyBlock: suspend CoroutineScope.() -> Unit
    ) {
        coroutineScope {
            try {
                tryBlock()
            } catch (e: Throwable) {
                catchBlock(e)
            } finally {
                finallyBlock()
            }
        }
    }

    /**
     * ViewModel销毁同时也取消请求
     */
    override fun onCleared() {
        super.onCleared()

        L.d("onDestroy: ${this}onCleared")
    }

}
