import java.security.MessageDigest

/**
 * PaddleOCR (https://github.com/PaddlePaddle/PaddleOCR) build script (Groovy).
 *
 * Created by TonyJiangWJ (https://github.com/TonyJiangWJ) on Aug 7, 2023.
 * Modified by TonyJiangWJ (https://github.com/TonyJiangWJ) as of Aug 11, 2023.
 * Modified by SuperMonster003 as of Sep 4, 2023.
 */

plugins {
    id "com.android.library"
}

def versions = new Versions("$rootDir/version.properties")

def versionNdk = versions.ndk
def versionCmake = versions.cmake

def deployer = new LibDeployer(versions.opencv)

android {
    namespace = "com.baidu.paddle.lite.ocr"

    ndkVersion versionNdk
    compileSdk 34
    defaultConfig {
        /* minSdk 默认为 23，这里修改为与 AutoJs6 最低 SDK 版本一致的 24. */
        minSdk 24
        targetSdk 34

        externalNativeBuild {
            cmake {
                cppFlags(([
                        "-std": "c++11",
                ].collect { "$it.key=$it.value" } + [
                        "-f": ["rtti", "exceptions"],
                        "-W": "no-format",
                ].collect {
                    it.value instanceof List
                            ? it.value.collect { opt -> (it.key + opt) }.join("\u0020")
                            : it.key + it.value
                }).join("\u0020"))

                arguments(*[
                        // @Hint by LZX284 on Sep 30, 2023.
                        //  ! "ANDROID_PLATFORM" 默认为 "android-23", 这里修改为与 AutoJs6 最低 SDK 版本一致的 24.
                        ANDROID_PLATFORM: "android-24",
                        ANDROID_STL     : "c++_shared",
                        ANDROID_ARM_NEON: "TRUE",
                ].collect {
                    // @Hint by SuperMonster003 on Nov 12, 2023.
                    //  ! Do not add a space (nbsp) after "-D".
                    //  ! Reference: https://stackoverflow.com/questions/14887438/spacing-in-d-option-in-cmake
                    "-D$it.key=$it.value"
                })
            }
        }
        ndk {
            // noinspection ChromeOsAbiSupport
            abiFilters "arm64-v8a", "armeabi-v7a"
            ldLibs "jnigraphics"
        }
    }
    buildTypes {
        release {
            minifyEnabled false
        }
    }
    externalNativeBuild {
        cmake {
            path "src/main/cpp/CMakeLists.txt"
            version versionCmake
        }
    }
}

dependencies {
    implementation fileTree(include: ["*.jar"], dir: "libs")
    implementation project(path: ":libs:org.opencv-${versions.opencv}")
    implementation "androidx.core:core-ktx:1.12.0"
    implementation "androidx.preference:preference-ktx:1.2.1"
}

ext {
    versions.showInfo()
    deployer.deploy(project)
}

class LibDeployer {

    String version

    LinkedHashMap<String, String> archive

    LibDeployer(String version) {
        this.version = version
        // @Hint by TonyJiangWJ on Aug 7, 2023.
        //   ! 下载 OpenCV 源码包 (默认为 4.2.0).
        //   ! 和 Auto.js 中的版本 (如 4.8.0) 不匹配会产生冲突,
        //   ! 可按需修改 version.properties 中对应内容.
        this.archive = [
                "src" : "https://github.com/opencv/opencv/releases/download" +
                        "/$version/opencv-$version-android-sdk.zip",
                "dest": "OpenCV",
        ]
    }

    void deploy(Project project) {
        // Prepare cache folder for archives
        String cachePath = "cache"
        if (!project.file(cachePath).exists()) {
            project.mkdir(cachePath)
        }
        def messageDigest = MessageDigest.getInstance("MD5")
        messageDigest.update(archive.src.bytes)
        String cacheName = new BigInteger(1, messageDigest.digest()).toString(32)

        def cacheSrc = archive.src
        def cacheDest = project.file(new File(cachePath, "${cacheName}.zip"))
        def libDest = archive.dest
        def versionNameFileName = "opencv-${version}.version"
        def versionNameFile = project.file(new File(libDest, versionNameFileName))

        boolean copyFiles = false

        if (cacheDest.exists()) {
            println("No need to download the OpenCV archive as the cache exists")
            if (versionNameFile.exists()) {
                println("No need to extract OpenCV cached archive as the version name file exists")
            } else {
                println("OpenCV cached archive needs to be extracted as the version name file doesn't exist")
                copyFiles = true
            }
            println("")
        } else if (versionNameFile.exists()) {
            println("No need to download files as the version name file exists")
        } else {
            def title = "Downloading the OpenCV archive file including libs and models..."
            def srcInfo = "Source: ${cacheSrc}"
            def destInfo = "Destination: ${cacheDest}"
            def hintInfo = [
                    "If the download gets stuck and won't finish,",
                    "try downloading the source file with tools like IDM (Internet Download Manager),",
                    "then renaming it into the destination path above.",
            ]

            def maxLength = [
                    [title, srcInfo, destInfo].max { it.length() },
                    hintInfo.max { it.length() },
            ].max { it.length() }.length()

            def infoList = [
                    "=".repeat(maxLength),
                    title,
                    "-".repeat(maxLength),
                    srcInfo,
                    destInfo,
                    "-".repeat(maxLength),
                    hintInfo.join("\n"),
                    "=".repeat(maxLength),
                    "",
            ]
            infoList.forEach { println(it) }

            project.ant.get(src: cacheSrc, dest: cacheDest)

            // Force to copy files from the latest archive files
            copyFiles = true
        }

        // Extract the target archive if its dest path does not exists
        if (copyFiles) {
            if (project.file(libDest).exists()) {
                println("Delete old files in \"${project.file(libDest)}\"")
                project.delete(libDest)
            }
            project.copy {
                from project.zipTree("${cachePath}${File.separator}${cacheName}.zip")
                into libDest
            }
            println("All libs and models extracted into \"${project.file(libDest)}\"")
            if (!versionNameFile.exists()) {
                versionNameFile.createNewFile()
                println("Version name file \"${versionNameFileName}\" created")
            }
            println()
        }
    }

}

class Versions {

    String opencv
    String ndk
    String cmake

    Versions(String filePath) {
        File file = new File(filePath)
        if (!file.canRead()) {
            throw FileNotFoundException("Can't read file '$filePath'")
        }
        Properties properties = new Properties()
        properties.load(new FileInputStream(file))
        opencv = properties["OPENCV_VERSION"] as String
        ndk = properties["NDK_VERSION"] as String
        cmake = properties["CMAKE_VERSION"] as String
    }

    void showInfo() {
        def title = "Version information for paddleocr library"

        def infoOpencv = "OpenCV version: $opencv"
        def infoNdk = "NDK version: $ndk"
        def infoCmake = "CMake version: $cmake"

        def maxLength = maxOf([title, infoOpencv, infoNdk, infoCmake].collect { it.length() })

        [
                "=".repeat(maxLength),
                title,
                "-".repeat(maxLength),
                infoOpencv,
                infoNdk,
                infoCmake,
                "=".repeat(maxLength),
                "",
        ].each { println(it) }
    }

    private static int maxOf(List<Integer> integers) {
        int max = 0
        integers.each { max = Math.max(max, it) }
        return max
    }

}