package com.gh.gamecenter.xapk.core

import com.gh.gamecenter.xapk.data.XApkManifest
import net.lingala.zip4j.ZipFile
import net.lingala.zip4j.model.FileHeader
import java.io.File
import java.io.InputStream

/**
 * XAPK文件类
 * @param file 文件
 * @param useFlawToUnzip 是否使用系统漏洞进行解压
 *
 * https://wiki.shanqu.cc/pages/viewpage.action?pageId=127762915#%E5%BA%94%E7%94%A8%E7%B3%BB%E7%BB%9F%E6%BC%8F%E6%B4%9E
 */
class XApkFile(val file: File, val isXapkApks: Boolean, val useFlawToUnzip: Boolean = false) {

    companion object {
        const val MANIFEST_FILE_NAME = "manifest.json"
    }

    private val zipFile = ZipFile(file)

    /**
     * 安装配置信息
     */
    val manifest by lazy {
        if (isXapkApks) {
            // TODO: 目前apks安装只用到total_size和split_apks字段，如果需要其他字段，需要相应的构造。
            val apks = zipFile.fileHeaders.filter { it.fileName.endsWith(".apk") }.map { it.fileName }
                .map { XApkManifest.Apk(it, ""/*unused*/) }
            return@lazy XApkManifest(packageName = ""/*unused*/, totalSize = file.length(), splitApks = apks)
        } else {
            return@lazy XApkManifest.fromInputStream(
                getManifestInputStream()
            ).apply {
                if (useFlawToUnzip) {
                    expansions?.forEach {
                        it.installPath = it.installPath.replace("Android", "\u200BAndroid")
                    }
                }
            }
        }
    }

    /**
     * 文件头缓存池
     */
    private val fileHeaderCacheMap = mutableMapOf<String, FileHeader>()

    /**
     * 获取指定文件的输入流
     * @param fileName 文件名称
     */
    fun getInputStream(fileName: String): InputStream {
        return zipFile.getInputStream(getFileInfo(fileName))
    }

    /**
     * 获取指定文件的文件头信息
     * @param fileName 文件名称
     */
    fun getFileInfo(fileName: String): FileHeader {
        val cacheFileHeader = fileHeaderCacheMap[fileName]
        if (cacheFileHeader != null) {
            return cacheFileHeader
        }

        return zipFile.getFileHeader(fileName)
            .also {
                fileHeaderCacheMap[fileName] = it
            }
    }

    /**
     * 开启安装配置输入流
     */
    fun getManifestInputStream(): InputStream = getInputStream(MANIFEST_FILE_NAME)
}