package com.gh.gamecenter.xapk.io

import android.content.Context
import android.content.pm.PackageInstaller
import android.content.pm.PackageInstaller.SessionParams
import android.content.pm.PackageManager
import android.os.Build
import com.gh.gamecenter.xapk.XApkUnZipTask
import com.gh.gamecenter.xapk.data.XApkManifest
import com.gh.gamecenter.xapk.pi.IPackageInstaller
import com.gh.gamecenter.xapk.pi.SplitPackagesInstaller

class SplitApksOutput(
    private val context: Context,
    private val factory: (Int) -> IPackageInstaller = {
        SplitPackagesInstaller(it)
    }
) : XApkFileOutput<IPackageInstaller> {

    override fun output(
        writer: XApkUnZipTask.XApkWriter,
        manifest: XApkManifest
    ): IPackageInstaller = SessionParams(SessionParams.MODE_FULL_INSTALL)
        .apply {
            if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O)
                setInstallReason(PackageManager.INSTALL_REASON_USER)
        }.let {
            val installer = context
                .packageManager
                .packageInstaller

            var session: PackageInstaller.Session? = null
            var sessionId: Int = INVALID_SESSION_ID
            try {

                // 创建安装会话并获取返回会话ID
                sessionId = installer.createSession(it)
                // 开启安装会话
                session = installer.openSession(sessionId)

                manifest.splitApks.forEach { apk ->
                    // 创建一个输出流，将APK文件流输入到输入流当中，流名称可以自定义
                    writer.write(
                        apk.file,
                        session.openWrite(apk.file, 0, -1),
                        bufferedSize = DEFAULT_BUFFER_SIZE,
                        onCopyFinish = { outputStream ->
                            session.fsync(outputStream)
                        }
                    )
                }
            } catch (e: Throwable) {
                session?.abandon()
                throw e
            } finally {
                session?.close()
            }
            sessionId
        }
        .let(factory)

    companion object {
        private const val INVALID_SESSION_ID = -1
    }

}