/*
 * This file is part of LSPosed.
 *
 * LSPosed is free software: you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * LSPosed is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with LSPosed.  If not, see <https://www.gnu.org/licenses/>.
 *
 * Copyright (C) 2020 EdXposed Contributors
 * Copyright (C) 2021 LSPosed Contributors
 */

import org.apache.tools.ant.filters.FixCrLfFilter
import org.gradle.internal.os.OperatingSystem

import java.security.MessageDigest

apply plugin: 'com.android.application'

static def calcSha256(file) {
    def md = MessageDigest.getInstance("SHA-256")
    file.eachByte 4096, { bytes, size ->
        md.update(bytes, 0, size)
    }
    return md.digest().encodeHex()
}

ext {
    module_name = "LSPosed"
    jar_dest_dir = "${projectDir}/template_override/system/framework/"
    is_windows = OperatingSystem.current().isWindows()
    module_id = "riru_lsposed"
    authors = "LSPosed Developers"

    riruModuleId = "lsposed"

    moduleMinRiruApiVersion = 10
    moduleMinRiruVersionName = "v23.0"
    moduleMaxRiruApiVersion = 10
}

repositories {
    mavenLocal()
    jcenter()
    maven { url 'https://dl.bintray.com/rikkaw/Libraries' }
}

dependencies {
    implementation 'rikka.ndk:riru:10'
    implementation 'com.android.tools.build:apksig:4.1.2'
    implementation project(path: ':sandhook-hooklib')
    compileOnly project(':hiddenapi-stubs')
    compileOnly project(':key-selector')
    compileOnly 'androidx.annotation:annotation:1.1.0'
    implementation project(':interface')
    implementation project(':hiddenapi-bridge')
    implementation project(':manager-service')
}

android {
    compileSdkVersion androidCompileSdkVersion.toInteger()
    defaultConfig {
        applicationId "io.github.lsposed.lspd"
        minSdkVersion androidMinSdkVersion.toInteger()
        targetSdkVersion androidTargetSdkVersion.toInteger()
        multiDexEnabled false

        buildFeatures {
            prefab true
        }

        externalNativeBuild {
            cmake {
                abiFilters 'arm64-v8a', 'armeabi-v7a', 'x86', 'x86_64'
                cppFlags "-std=c++17 -ffixed-x18 -Qunused-arguments -frtti -fomit-frame-pointer"
                cFlags "-std=gnu99 -ffixed-x18 -Qunused-arguments -frtti -fomit-frame-pointer"
                arguments "-DRIRU_MODULE_API_VERSION=$moduleMaxRiruApiVersion",
                        "-DRIRU_MODULE_VERSION=$rootProject.ext.versionCode",
                        "-DRIRU_MODULE_VERSION_NAME:STRING=\"$rootProject.ext.versionName\""
            }
        }

        buildConfigField("int", "API_CODE", "$apiCode")
        buildConfigField("String", "VERSION_NAME", "\"${rootProject.ext.versionName}\"")
        buildConfigField("Integer", "VERSION_CODE", String.valueOf(rootProject.ext.versionCode))
    }

    lintOptions {
        abortOnError false
        checkReleaseBuilds false
    }

    buildTypes {
        debug {
            externalNativeBuild {
                cmake {
                    cppFlags "-O0"
                    cFlags "-O0"
                }
            }
        }
        release {
            minifyEnabled true
            proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'

            externalNativeBuild {
                cmake {
                    cppFlags "-fvisibility=hidden -fvisibility-inlines-hidden -O2 -s -Wno-unused-value -fomit-frame-pointer"
                    cFlags "-fvisibility=hidden -fvisibility-inlines-hidden -O2 -s -Wno-unused-value -fomit-frame-pointer"
                }
            }
        }
    }
    externalNativeBuild {
        cmake {
            path "src/main/cpp/CMakeLists.txt"
        }
    }
    ndkVersion androidCompileNdkVersion
    buildToolsVersion androidBuildToolsVersion
    compileOptions {
        targetCompatibility JavaVersion.VERSION_1_8
        sourceCompatibility JavaVersion.VERSION_1_8
    }
}

afterEvaluate {

    android.applicationVariants.all { variant ->
        def variantCapped = variant.name.capitalize()
        def variantLowered = variant.name.toLowerCase()
        def zipFileName = "${module_name}-${rootProject.ext.versionName}-${rootProject.ext.versionCode}-${variantLowered}.zip"

        delete file(zipPathMagiskReleasePath)

        def prepareMagiskFilesTask = task("prepareMagiskFiles${variantCapped}") {
            dependsOn "assemble${variantCapped}"
            dependsOn tasks.getByPath(":key-selector:copyKeySelector${variantCapped}LibraryToMagiskTemplate")
            doFirst {
                copy {
                    from "${projectDir}/tpl/module.prop.tpl"
                    into templateRootPath
                    rename "module.prop.tpl", "module.prop"
                    expand(moduleId: "$module_id",
                            versionName: "$rootProject.ext.versionName",
                            versionCode: "$rootProject.ext.versionCode", authorList: "$authors",
                            apiCode: "$apiCode", minApi: "$moduleMinRiruApiVersion")
                    filter(FixCrLfFilter.class, eol: FixCrLfFilter.CrLf.newInstance("lf"))
                }
                copy {
                    from ("${rootProject.projectDir}/README.md")
                    into file(zipPathMagiskReleasePath)
                }
            }
            def libPathRelease = "${buildDir}/intermediates/cmake/${variantLowered}/obj"
            def exclude_list = ["util_functions.sh"]
            doLast {
                def dexOutPath = variant.name.contains("release") ?
                        "${buildDir}/intermediates/dex/${variantLowered}/minify${variantCapped}WithR8" :
                        "${buildDir}/intermediates/dex/${variantLowered}/mergeDex${variantCapped}"
                copy {
                    from (dexOutPath){
                        rename("classes.dex", "lspd.dex")
                    }
                    into file(zipPathMagiskReleasePath + "system/framework/")
                }
                copy {
                    from "${projectDir}/template_override"
                    into zipPathMagiskReleasePath
                    exclude exclude_list
                }
                copy {
                    from "${projectDir}/template_override"
                    into zipPathMagiskReleasePath
                    include 'util_functions.sh'
                    filter { line ->
                        line.replaceAll('%%%RIRU_MODULE_ID%%%', riruModuleId)
                                .replaceAll('%%%RIRU_MIN_API_VERSION%%%', moduleMinRiruApiVersion.toString())
                                .replaceAll('%%%RIRU_MIN_VERSION_NAME%%%', moduleMinRiruVersionName)
                    }
                    filter(FixCrLfFilter.class,
                            eol: FixCrLfFilter.CrLf.newInstance("lf"))
                }
                copy {
                    include "libriru_lspd.so"
                    from "$libPathRelease/armeabi-v7a"
                    into "$zipPathMagiskReleasePath/system/lib"
                }
                copy {
                    include "libriru_lspd.so"
                    from "$libPathRelease/arm64-v8a"
                    into "$zipPathMagiskReleasePath/system/lib64"
                }
                copy {
                    include "libriru_lspd.so"
                    from "$libPathRelease/x86"
                    into "$zipPathMagiskReleasePath/system_x86/lib"
                }
                copy {
                    include "libriru_lspd.so"
                    from "$libPathRelease/x86_64"
                    into "$zipPathMagiskReleasePath/system_x86/lib64"
                }
                // generate sha1sum
                fileTree(zipPathMagiskReleasePath).matching {
                    exclude "README.md", "META-INF"
                }.visit { f ->
                    if (f.directory) return
                    file(f.file.path + ".sha256").text = calcSha256(f.file)
                }
            }
        }

        def zipTask = task("zip${variantCapped}", type: Zip) {
            dependsOn prepareMagiskFilesTask
            archiveName zipFileName
            destinationDir file("$projectDir/release")
            from "$zipPathMagiskReleasePath"
        }

        task("push${variantCapped}", type: Exec) {
            dependsOn zipTask
            workingDir "${projectDir}/release"
            def commands = [android.adbExecutable, "push",
                            zipFileName,
                            "/data/local/tmp/"]
            if (is_windows) {
                commandLine 'cmd', '/c', commands.join(" ")
            } else {
                commandLine commands
            }
        }
        task("flash${variantCapped}", type: Exec) {
            dependsOn tasks.getByPath("push${variantCapped}")
            workingDir "${projectDir}/release"
            def commands = [android.adbExecutable, "shell", "su", "-c",
                            "magisk --install-module /data/local/tmp/${zipFileName}"]
            if (is_windows) {
                commandLine 'cmd', '/c', commands.join(" ")
            } else {
                commandLine commands
            }
        }
        task("flashAndReboot${variantCapped}", type: Exec) {
            dependsOn tasks.getByPath("flash${variantCapped}")
            workingDir "${projectDir}/release"
            def commands = [android.adbExecutable, "shell", "reboot"]
            if (is_windows) {
                commandLine 'cmd', '/c', commands.join(" ")
            } else {
                commandLine commands
            }
        }
    }

}