import java.util.regex.Matcher
import java.util.regex.Pattern

final String SDK_PACKAGE_NAME = sdkPackageName.replace('.', '/')
final String SDK_PACKAGE_DIR = sdkPackageName.replace('.', File.separator)

final String PACK_PREFIX = 'sdk_hotfix'
final String JAR_TASK_NAME = 'jar_' + PACK_PREFIX
final String AAR_TASK_NAME = 'aar_' + PACK_PREFIX
//输出路径为\sdk\robustjar\release
final PATH = projectDir.toString() + File.separator + 'robustjar' + File.separator + 'release'

// hook编译过程，把R文件的final修饰符去掉（library的R是变量不是常量）
hookBuild(SDK_PACKAGE_DIR)
// 生成打jar包和aar包的对应的task 名字为jar_sdk_hotfix 和 aar_sdk_hotfix
createJarTask(JAR_TASK_NAME, PATH, SDK_PACKAGE_NAME)
createAarTask(AAR_TASK_NAME, PATH)
hookAssembleAndCopyRes(PATH, JAR_TASK_NAME, AAR_TASK_NAME)
hookBundle(PATH)

private void hookBuild(sdkPackageDir) {

    tasks.whenTaskAdded { task ->
        if (!isAppModule.toBoolean()) {
            // 不是Application模式不处理
            return
        }
        if (task.name != "processReleaseResources") {
            return
        }

        println '--监听task：' + task.name
        task.doLast {
            // hook所需资源所在的父目录， \sdk\build\generated
            String generatedPath = buildDir.toString() + File.separator + "generated"

            // R资源文件的路径，\sdk\build\generated\r\release\包名\R.java
            String rPath = generatedPath + File.separator + "source" + File.separator + "r" + File.separator + "release" + File.separator + sdkPackageDir + File.separator + "R.java"

            println '--R文件路径：' + rPath

            File file = new File(rPath)
            if (file.exists()) {
                println '--R文件存在，开始修改修饰符'
                ant.replace(
                        file: rPath,
                        token: 'public static final int',
                        value: 'public static int'
                ) {
                    fileset(file: rPath)
                }
                println '--R文件修改完成！'
            } else {
                println '--【告警】R文件不存在！'
            }
        }
    }
}

private Task createJarTask(taskName, path, sdkPackageName) {
    tasks.create(name: taskName, type: Jar) {

        // jar包命名为classes.jar
        baseName 'classes'

        String intermediatesPath = buildDir.toString() + File.separator + "intermediates"

        // gradle-2.3.3 & robust-0.4.7对应的路径为 \sdk\build\intermediates\transforms\proguard\release\jars\3\1f\main.jar
        // String robustJarPath = intermediatesPath + File.separator + "transforms" + File.separator + "proguard"  + File.separator + "release" + File.separator + "jars" + File.separator + "3" + File.separator + "1f" + File.separator + "main.jar"

        // gradle-3.0.0 & robust-0.4.71对应的路径为 \sdk\build\intermediates\transforms\proguard\release\0.jar
        String jarDirName = isProguard.toBoolean() ? "proguard" : "robust"
        String robustJarPath = intermediatesPath + File.separator + "transforms" + File.separator + jarDirName + File.separator + "release" + File.separator + "0.jar"

        def zipFile = new File(robustJarPath)
        // 将jar包解压
        FileTree jarTree = zipTree(zipFile)

        from jarTree

        // jar包输出路径为 ${path}\origin
        File destDir = file(path + File.separator + 'origin')
        // 设置输出路径
        setDestinationDir destDir

        include {
            // 只打包我们需要的类
            it.path.startsWith(sdkPackageName)
        }

        exclude {
            // println "执行排除：" + it.path
            // 排除R相关class文件，排除MainActivity.class文件
            it.path.startsWith(sdkPackageName + '/R$') || it.path.startsWith(sdkPackageName + '/R.class') || it.path.startsWith(sdkPackageName + '/MainActivity.class')
        }

        println '--创建压缩jar包Task完毕'
    }
}


private Task createAarTask(taskName, path) {
    tasks.create(name: taskName, type: Zip) {
        // aar包输出路径为 ${path}\aar
        File destDir = file(path + File.separator + 'aar')
        // aar包命名为 library-release.aar
        archiveName 'library-release.aar'
        // 源路径为 ${path}\origin
        from path + File.separator + 'origin'
        // 设置压缩后输出的路径
        destinationDir destDir

        println '--创建压缩aar包Task完毕'
    }
}

private void hookAssembleAndCopyRes(path, jarTaskName, aarTaskName) {
// 项目打release版本apk包的话，必然会调用到assemble(渠道)Release的命令，于是我们可以用正则匹配来匹配所有渠道的打Release包过程
    Pattern p = Pattern.compile("^assemble(.*)Release\$")

    // 在task添加到列表的时候，进行打包task的匹配
    tasks.whenTaskAdded { task ->
        if (!isAppModule.toBoolean()) {
            // 不是Application模式不处理
            return
        }
        // 在任务执行的时候，匹配执行assemble(渠道)Release的打APK任务
        Matcher m = p.matcher(task.name)
        if (!m.find()) {
            return
        }

        println '--监听task：' + task.name
        // 打release包task完成之后进行资源的整合以及jar包去指定class文件，并且生成aar包
        task.doLast {

            delete {
                // 删除上次生成的文件目录，目录为 ${path}
                delete projectDir.toString() + File.separator + 'robustjar' + File.separator + "release"
            }

            // 打包所需资源所在的父目录， \sdk\build\intermediates
            String intermediatesPath = buildDir.toString() + File.separator + "intermediates"

            // gradle-3.0.0 & robust-0.4.71对应的路径为 \sdk\build\intermediates\transforms\proguard\release\0.jar
            String jarDirName = isProguard.toBoolean() ? "proguard" : "robust"
            String robustJarPath = intermediatesPath + File.separator + "transforms" + File.separator + jarDirName + File.separator + "release" + File.separator + "0.jar"

            // gradle-2.3.3 & robust-0.4.7对应的路径为 \sdk\build\intermediates\transforms\proguard\release\jars\3\1f\main.jar
            // String robustJarPath = intermediatesPath + File.separator + "transforms" + File.separator + "proguard"  + File.separator + "release" + File.separator + "jars" + File.separator + "3" + File.separator + "1f" + File.separator + "main.jar"

            // 资源文件的路径，\sdk\build\intermediates\assets\release
            String assetsPath = intermediatesPath + File.separator + "assets" + File.separator + "release"

            // 依赖本地jar包路径，\sdk\build\intermediates\jniLibs\release
            String libsPath = intermediatesPath + File.separator + "jniLibs" + File.separator + "release"

            // res资源文件的路径，\sdk\build\intermediates\res\merged\release，经测试发现此目录下生成的.9图片会失效，因此弃置，换另外方式处理
            // String resPath = intermediatesPath + File.separator + "res" + File.separator + "merged" + File.separator + "release"

            // 由于上述问题，直接用项目的res路径 \sdk\src\main\res ，因此第三方依赖的资源文件无法整合，但是我是基于生成只包含自身代码的jar包和资源，其余依赖宿主另外再依赖的方案，所以可以这样处理
            String resPath = projectDir.toString() + File.separator + "src" + File.separator + "main" + File.separator + "res"

            // 资源id路径，\sdk\build\intermediates\symbols\release
            String resIdPath = intermediatesPath + File.separator + "symbols" + File.separator + "release"

            // 清单文件路径，\sdk\build\intermediates\manifests\full\release，由于是生成的application的清单文件，因此下面还会做删除组件声明的处理
            String manifestPath = intermediatesPath + File.separator + "manifests" + File.separator + "full" + File.separator + "release"

            // 整合上述文件后的目标路径，${path}\origin
            String destination = path + File.separator + 'origin'

            // 貌似aidl的文件夹没啥用，打包会根据例如G:\\sms-hotfix\\SmsParsingForRcs-Library\\library\\src\\main\\aidl\\com\\cmic\\IMyAidlInterface.aidl的定义代码生成com.cmic.IMyAidlInterface到jar包里面，因此aidl仅仅是空文件夹
            // String aidlPath = buildDir.toString() + File.separator + "generated" + File.separator + "source" + File.separator + "aidl"  + File.separator + "release"

            File file = file(robustJarPath)
            if (!file.exists()) {
                println '--【告警】robust插桩jar包不存在，结束'
                return
            }
            println '--开始复制robust插桩jar包'
            copy {

                // 拷贝到assets目录
                from(assetsPath) {
                    into 'assets'
                }

                //  第三方本地jar包不处理，提供宿主集成的时候另外提供
                //  from(libsPath) {
                //      into 'libs'
                //      include '**/*.jar'
                //      exclude {
                //          // println it.path+";"+it.isDirectory()
                //          it.isDirectory()
                //      }
                //  }

                // .so文件拷贝到jni目录
                from(libsPath) {
                    into 'jni'
                    include '**/*/*.so'
                }

                // 资源文件拷贝到res目录
                from(resPath) {
                    // 排除MainActivity加载的布局文件，因为输出的是jar包，加MainActivity仅仅是为了能让打apk包任务执行
                    exclude '/layout/activity_main.xml'
                    exclude {
                        // 排除空文件夹
                        it.isDirectory() && it.getFile().listFiles().length == 0
                    }
                    into 'res'
                }

                // aidl的文件夹没啥用，不处理
                // from(aidlPath) {
                //     into 'aidl'
                // }

                // 拷贝此目录下资源id文件 R.txt
                from resIdPath

                // 拷贝到目录 ${path}\origin
                into destination

            }

            copy {
                // 复制供宿主的混淆规则，这里我在android{ defaultConfig { consumerProguardFiles 'lib-proguard-rules.pro' }}，配置了一个混淆规则
                def files = android.defaultConfig.consumerProguardFiles
                if (files != null && files.size() > 0) {
                    def file1 = files.get(0)
                    //  println '混淆文件路径：'+file1.path
                    from file1.path
                    into destination
                    // 复制混淆规则并且重命名
                    rename(file1.name, 'proguard.txt')
                }
            }

            // 补丁生成需要的mapping.txt和methodsMap.robust文件
            copy {
                // 混淆mapping文件的路径，\sdk\build\outputs\mapping\release\mapping.txt
                from(buildDir.toString() + File.separator + 'outputs' + File.separator + 'mapping' + File.separator + 'release') {
                    include 'mapping.txt'
                }
                // 拷贝到目录 ${path}
                into path
            }

            copy {
                // robust生成的methodsMap文件路径，\sdk\build\outputs\robust\methodsMap.robust
                from(buildDir.toString() + File.separator + 'outputs' + File.separator + 'robust') {
                    include 'methodsMap.robust'
                }
                // 拷贝到目录 ${path}
                into path
            }

            // 若不存在aidl目录，创建aidl空目录
            createDir(destination + File.separator + "aidl")
            // 同上
            createDir(destination + File.separator + "assets")
            // 同上
            createDir(destination + File.separator + "jni")
            // 同上
            createDir(destination + File.separator + "libs")
            // 同上
            createDir(destination + File.separator + "res")

            // 将清单文件application节点的内容和activity节点的内容替换，将清单文件provider节点的内容和meta-data节点的内容替换
            def oldStr = ["<application[\\s\\S]*?>", "<activity[\\s\\S]*?</activity>", "<provider[\\s\\S]*?(</provider>|/>)", "<meta-data[\\s\\S]*?(</meta-data>|/>)"]
            def newStr = ["<application\n" + "        android:allowBackup=\"false\"\n" + "        android:supportsRtl=\"true\">", "", "", ""]
          //  try {
                // 处理 \sdk\build\intermediates\manifests\full\release\AndroidManifest.xml
                String strBuffer = fileReader(manifestPath + File.separator + "AndroidManifest.xml", oldStr, newStr)
                // 输出至 ${path}\origin\AndroidManifest.xml
                fileWrite(destination + File.separator + "AndroidManifest.xml", strBuffer)
        //    } catch (FileNotFoundException e) {
        //        e.printStackTrace()
        //    }
            println '--输出robust插桩jar包成功!'

            // 执行打jar包的task，这里会做原jar包的过滤处理，只保留我们需要的代码
            tasks.findByName(jarTaskName).execute()

            // 执行打aar包的task，其实就是将目录\sdk\robustjar\develop\release\origin压缩成aar后缀的压缩包
            tasks.findByName(aarTaskName).execute()

            delete project.buildDir
        }
    }
}

//读取文件并替换字符串
static def fileReader(path, oldStr, newStr) {
    def readerString = new File(path).getText('UTF-8')
    for (int i = 0; i < oldStr.size(); i++) {
        readerString = readerString.replaceFirst(oldStr[i], newStr[i])
    }
    return readerString
}

//写文件
static def fileWrite(path, stringBuffer) {
    new File(path).withWriter('UTF-8') {
        within ->
            within.append(stringBuffer)
    }
}

// 创建目录
static def createDir(String destDirName) {
    File dir = new File(destDirName)
    if (dir.exists()) {
        println '--目标目录已存在！无需创建'
        return false
    }
    if (!destDirName.endsWith(File.separator)) {
        destDirName = destDirName + File.separator
    }
    if (dir.mkdirs()) {
        println '--创建目录成功！' + destDirName
        return true
    } else {
        println '--创建目录失败！'
        return false
    }
}

//项目uploadArchives时，必然会调用到bundleRelease Task，hook bundle* Task 用于在上传maven前把本地打包的aar改为插桩后的aar
private void hookBundle(path) {
    tasks.whenTaskAdded { task ->
        if (isAppModule.toBoolean()) {
            // 是Application模式不处理，因为Application模式没有bundleRelease Task
            return
        }

        if (!'bundleRelease'.equals(task.name)) {
            return
        }
        task.doFirst {
            println '--hook bundleRelease!'
            forEachInputs(it, path)
        }
    }
}

private void forEachInputs(Task it, String path) {
    String jarName = 'classes.jar'
    it.inputs.files.each { input ->
        if (input.absolutePath.indexOf(jarName) != -1) {
            String jarInputPath = input.absolutePath.substring(0, input.absolutePath.lastIndexOf(File.separator) + 1)
            copy {
                // 源路径为 ${path}\origin
                from(path + File.separator + 'origin') {
                    include jarName
                }
                into jarInputPath
            }
        }
    }
}
