// Copyright 2018 Google LLC
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//      http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

// Defines the task to extract the Jar file from the Android Aar, and
// the task to convert the Jar format to dex, using the dx tool.
def defineExtractionTasks(String resourceName, String buildType) {
  project.ext {
    sdk_dir = project.android.sdkDirectory
    if (sdk_dir == null || !sdk_dir.exists()) {
      sdk_dir = System.getenv('ANDROID_HOME')
      if (sdk_dir == null || !sdk_dir.exists()) {
        throw new StopActionException(
          'Android SDK directory should be specified using the ' +
          'sdk.dir property or ANDROID_HOME environment variable.')
      }
    }
  }

  // Create the tasks after evaluation, as the Android tasks are not available
  // until that phase.
  afterEvaluate {
    Task extractJar = tasks.create(
        name: "extractJar$buildType",
        dependsOn: "bundle${buildType}Aar",
        type: Copy).with {
      String aarPath =
          project.tasks.getByName("bundle${buildType}Aar").archivePath
      from zipTree(aarPath)
      into buildDir
      // Aar files are defined as having a single jar file named classes.jar,
      // which is the specific file we need to extract.
      include "classes.jar"
    }

    String dexedJar = "$buildDir/${resourceName}_lib.jar"
    String outPro = "$buildDir/${resourceName}.pro"
    Task dexJar = tasks.create(
        name: "generateDexJar$buildType",
        dependsOn: [extractJar],
        type: Exec).with {
      String sdk_dir = project.ext.sdk_dir
      String buildToolsVersion = project.android.buildToolsVersion

      // Define the outputs of this task (the proguard file is generated in the
      // doLast below, but must be declared as an output here).
      outputs.file "$dexedJar"
      outputs.file "$outPro"

      // Convert the jar format using the dx tool.
      String dex_path = "${sdk_dir}/build-tools/${buildToolsVersion}/lib/d8.jar"
      commandLine "java", "-cp", "${dex_path}", "com.android.tools.r8.D8",
          "$buildDir/classes.jar",
          "--output",
          "$dexedJar"
    }

    // Once the dexed jar has been made, generate a proguard file for it.
    dexJar.doLast {
      // The path to all the class files, to pass along to jdeps.
      String classPath = "$buildDir";

      def jdepsOutStream = new ByteArrayOutputStream()
      def jdepsArgs = ['-verbose:class', '-cp', "$dexedJar"] + classPath
      exec {
        executable 'jdeps'
        args jdepsArgs
        standardOutput = jdepsOutStream
      }

      // Use a set to make the written lines unique.
      Set<String> jdepSet = new HashSet<String>()
      for (String jdepsLine : jdepsOutStream.toString().split("[\\r\\n]+")) {
        // The lines we care about are formatted like "  -> java.class  extra"
        // Split on the arrow to get the class name.
        String[] splitJdep = jdepsLine.split("^ [^-]*-> *")
        if (splitJdep.length == 2) {
          // We don't care about the system level classes that begin with
          // java or android.
          String className = splitJdep[1]
          if (!className.matches("^(java|android).*")) {
            jdepSet.add(
                "-keep,includedescriptorclasses public class " +
                // Sometimes the class name has additional text after it, which
                // this removes.
                className.split()[0] +
                " { *; }");
          }
        }
      }

      new File("$outPro").text = String.join('\n', jdepSet.toSorted())
    }
  }
}

// Defines the tasks for both debug and release.
def extractAndDexAarFile(String resourceName) {
  defineExtractionTasks(resourceName, 'Debug')
  defineExtractionTasks(resourceName, 'Release')
}

// Sets the dependencies on the Android prebuild steps to the generated tasks.
def setupDexDependencies(String subproject) {
  preDebugBuild.dependsOn("$subproject:generateDexJarDebug")
  preReleaseBuild.dependsOn("$subproject:generateDexJarRelease")
}

ext {
  extractAndDexAarFile = this.&extractAndDexAarFile
  setupDexDependencies = this.&setupDexDependencies
}