import android.support.doclava.DoclavaMultilineJavadocOptionFileOption
import com.android.build.gradle.internal.coverage.JacocoReportTask
import com.android.build.gradle.internal.tasks.DeviceProviderInstrumentTestTask

import android.support.checkapi.CheckApiTask
import android.support.checkapi.UpdateApiTask
import android.support.doclava.DoclavaTask

buildscript {
    repositories {
        maven { url '../../prebuilts/gradle-plugin' }
        maven { url '../../prebuilts/tools/common/m2/repository' }
        maven { url '../../prebuilts/tools/common/m2/internal' }
        maven { url "../../prebuilts/maven_repo/android" }
    }
    dependencies {
        classpath 'com.android.tools.build:gradle:2.2.0'
    }
}

repositories {
    maven { url '../../prebuilts/tools/common/m2/repository' }
}

configurations {
    doclava
}

dependencies {
    doclava project(':doclava')
}

ext.supportVersion = '25.0.0'
ext.extraVersion = 39
ext.supportRepoOut = ''
ext.buildToolsVersion = '23.0.2'
ext.buildNumber = Integer.toString(ext.extraVersion)

ext.testRunnerVersion = '0.6-alpha'
ext.espressoVersion = '2.3-alpha'

// Enforce the use of prebuilt dependencies in all sub-projects. This is
// required for the doclava dependency.
ext.usePrebuilts = "true"

/*
 * With the build server you are given two env variables.
 * The OUT_DIR is a temporary directory you can use to put things during the build.
 * The DIST_DIR is where you want to save things from the build.
 *
 * The build server will copy the contents of DIST_DIR to somewhere and make it available.
 */
if (System.env.DIST_DIR != null && System.env.OUT_DIR != null) {
    buildDir = new File(System.env.OUT_DIR + '/gradle/frameworks/support/build').getCanonicalFile()
    project.ext.distDir = new File(System.env.DIST_DIR).getCanonicalFile()

    // the build server does not pass the build number so we infer it from the last folder of the dist path.
    ext.buildNumber = project.ext.distDir.getName()
} else {
    buildDir = file("${project.rootDir}/../../out/host/gradle/frameworks/support/build")
    project.ext.distDir = file("${project.rootDir}/../../out/dist")
}

subprojects {
    // Change buildDir first so that all plugins pick up the new value.
    project.buildDir = project.file("$project.parent.buildDir/../$project.name/build")
}

ext.docsDir = new File(buildDir, 'javadoc')
ext.supportRepoOut = new File(buildDir, 'support_repo')
ext.testApkDistOut = ext.distDir

// Main task called by the build server.
task(createArchive) << {
}

// upload anchor for subprojects to upload their artifacts
// to the local repo.
task(mainUpload) << {
}

// repository creation task
task createRepository(type: Zip, dependsOn: mainUpload) {
    from project.ext.supportRepoOut
    destinationDir project.ext.distDir
    into 'm2repository'
    baseName = String.format("sdk-repo-linux-m2repository-%s", project.ext.buildNumber)
}
createArchive.dependsOn createRepository

// prepare repository with older versions
task unzipRepo(type: Copy) {
    from "${project.rootDir}/../../prebuilts/maven_repo/android"
    into project.ext.supportRepoOut
}

unzipRepo.doFirst {
    project.ext.supportRepoOut.deleteDir()
    project.ext.supportRepoOut.mkdirs()
}

// anchor for prepare repo. This is post unzip + sourceProp.
task(prepareRepo) << {
}


import android.support.build.ApiModule
import com.google.common.io.Files
import com.google.common.base.Charsets

task(createXml) << {
    def repoArchive = createRepository.archivePath
    def repoArchiveName = createRepository.archiveName
    def size = repoArchive.length()
    def sha1 = getSha1(repoArchive)

    def xml =
"<sdk:sdk-addon xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\" xmlns:sdk=\"http://schemas.android.com/sdk/android/addon/6\">\n\
  <sdk:extra>\n\
    <sdk:revision>\n\
      <sdk:major>${project.ext.extraVersion}</sdk:major>\n\
    </sdk:revision>\n\
    <sdk:vendor-display>Android</sdk:vendor-display>\n\
    <sdk:vendor-id>android</sdk:vendor-id>\n\
    <sdk:name-display>Local Maven repository for Support Libraries</sdk:name-display>\n\
    <sdk:path>m2repository</sdk:path>\n\
    <sdk:archives>\n\
      <sdk:archive>\n\
       <sdk:size>${size}</sdk:size>\n\
       <sdk:checksum type=\"sha1\">${sha1}</sdk:checksum>\n\
       <sdk:url>${repoArchiveName}</sdk:url>\n\
      </sdk:archive>\n\
    </sdk:archives>\n\
  </sdk:extra>\n\
</sdk:sdk-addon>"

    Files.write(xml, new File(project.ext.distDir, 'repo-extras.xml'), Charsets.UTF_8)
}
createArchive.dependsOn createXml

task(createSourceProp) << {
    def sourceProp =
"Extra.VendorDisplay=Android\n\
Extra.Path=m2repository\n\
Archive.Arch=ANY\n\
Extra.NameDisplay=Android Support Repository\n\
Archive.Os=ANY\n\
Pkg.Desc=Local Maven repository for Support Libraries\n\
Pkg.Revision=${project.ext.extraVersion}.0.0\n\
Extra.VendorId=android"

    Files.write(sourceProp, new File(project.ext.supportRepoOut, 'source.properties'), Charsets.UTF_8)
}
createSourceProp.dependsOn unzipRepo
prepareRepo.dependsOn createSourceProp

import com.google.common.hash.HashCode
import com.google.common.hash.HashFunction
import com.google.common.hash.Hashing
import java.nio.charset.Charset

/**
 * Generates SHA1 hash for the specified file's absolute path.
 *
 * @param inputFile file to hash
 * @return SHA1 hash
 */
String getSha1(File inputFile) {
    HashFunction hashFunction = Hashing.sha1()
    HashCode hashCode = hashFunction.hashString(inputFile.getAbsolutePath(), Charset.forName("UTF-8"))
    return hashCode.toString()
}

/**
 * Returns the Android prebuilt JAR for the specified API level.
 *
 * @param apiLevel the API level or "current"
 * @return a file collection containing the Android prebuilt JAR
 */
FileCollection getAndroidPrebuilt(apiLevel) {
    files("${project.rootDir}/../../prebuilts/sdk/$apiLevel/android.jar")
}

/**
 * Populates the sub-project's set of source sets with the specified modules.
 *
 * @param subProject the sub-project to which the modules belong
 * @param apiModules the modules from which to populate
 */
void createApiSourceSets(Project subProject, List<ApiModule> apiModules) {
    subProject.ext._apiModules = apiModules
    subProject.ext.allSS = []
    if (gradle.ext.studioCompat.enableApiModules) {
        // nothing to do, they are all modules
        return
    }
    // create a jar task for the api specific internal implementations
    def internalJar = subProject.tasks.create(name: "internalJar", type: Jar) {
        baseName "internal_impl"
    }
    apiModules.each { ApiModule apiModule ->
        apiModule.sourceSet = createApiSourceset(subProject, apiModule.folderName, apiModule.folderName,
                apiModule.apiForSourceSet.toString(), apiModule.prev == null ? null : apiModule.prev.sourceSet)
        subProject.ext.allSS.add(apiModule.sourceSet)
    }
    subProject.android.libraryVariants.all { variant ->
        variant.javaCompile.dependsOn internalJar
    }
}

/**
 * Adds the specified module to the sub-project's set of source sets and
 * internal JAR. Also sets up dependencies, if supplied.
 *
 * @param subProject the sub-project to which the module belongs
 * @param name the name of the module
 * @param folder the module's source folder
 * @param apiLevel the module's compile API level
 * @param previousSource source set dependency (optional)
 * @return a source set for the module
 */
SourceSet createApiSourceset(Project subProject, String name, String folder, String apiLevel,
                       SourceSet previousSource) {
    def sourceSet = subProject.sourceSets.create(name)
    sourceSet.java.srcDirs = [folder]

    // The Android gradle plugin doesn't touch Java sub-tasks, so we need to
    // manually set the Java task's boot classpath to the correct Android SDK.
    def compileJavaTaskName = sourceSet.getCompileJavaTaskName();
    def compileJavaOptions = subProject.tasks."${compileJavaTaskName}".options
    compileJavaOptions.bootClasspath = getAndroidPrebuilt(apiLevel)

    // Useful for cleaning up compiler warnings...
    //compileJavaOptions.compilerArgs << "-Xlint:unchecked" << "-Xlint:deprecation"

    def configName = sourceSet.getCompileConfigurationName()
    subProject.getDependencies().add(configName, getAndroidPrebuilt(apiLevel))
    if (previousSource != null) {
        setupDependencies(subProject, configName, previousSource)
    }
    subProject.ext.allSS.add(sourceSet)
    subProject.tasks.internalJar.from sourceSet.output
    return sourceSet
}

/**
 * Adds the specified source set as a dependency for the sub-project.
 *
 * @param subProject the sub-project to modify
 * @param configName
 * @param previousSourceSet the source set to add as a dependency
 */
void setupDependencies(Project subProject, String configName, SourceSet previousSourceSet) {
    subProject.getDependencies().add(configName, previousSourceSet.output)
    subProject.getDependencies().add(configName, previousSourceSet.compileClasspath)
}

void setApiModuleDependencies(Project subProject, DependencyHandler handler, List extraDeps) {
    if (gradle.ext.studioCompat.enableApiModules) {
        subProject.android.enforceUniquePackageName=false
        // add dependency on the latest module
        handler.compile(project(subProject.ext._apiModules.last().moduleName))
    } else {
        handler.compile(files(subProject.tasks.internalJar.archivePath))
        def firstModule = subProject.ext._apiModules[0]
        extraDeps.each { dep ->
            handler."${firstModule.folderName}Compile"(project(dep))
            handler.compile(project(dep))
        }
    }
}

void registerForDocsTask(Task task, Project subProject, releaseVariant) {
    task.dependsOn releaseVariant.javaCompile
    task.source {
        def buildConfig = fileTree(releaseVariant.getGenerateBuildConfig().sourceOutputDir)
        return releaseVariant.javaCompile.source.minus(buildConfig) +
            fileTree(releaseVariant.aidlCompile.sourceOutputDir) +
            fileTree(releaseVariant.outputs[0].processResources.sourceOutputDir)
    }
    task.classpath += files(releaseVariant.javaCompile.classpath) +
            files(releaseVariant.javaCompile.destinationDir)

    if (subProject.hasProperty('allSS')) {
        subProject.allSS.each { ss ->
            task.source ss.java
        }
    }
}

// Generates online docs.
task generateDocs(type: DoclavaTask, dependsOn: configurations.doclava) {
    docletpath = configurations.doclava.resolve()
    destinationDir = new File(project.docsDir, "online")

    // Base classpath is Android SDK, sub-projects add their own.
    classpath = getAndroidPrebuilt(gradle.ext.currentSdk)

    def hdfOption = new DoclavaMultilineJavadocOptionFileOption('hdf')
    hdfOption.add(
            ['android.whichdoc', 'online'],
            ['android.hasSamples', 'true']);

    options {
        addStringOption "templatedir",
                "${project.rootDir}/../../build/tools/droiddoc/templates-sdk"
        addStringOption "federate Android", "http://developer.android.com"
        addStringOption "federationapi Android",
                "${project.rootDir}/../../prebuilts/sdk/api/24.txt"
        addStringOption "stubpackages", "android.support.*"
        addStringOption "samplesdir", "${project.rootDir}/samples"
        addOption hdfOption
    }

    exclude '**/BuildConfig.java'
}

// Generates API files.
task generateApi(type: DoclavaTask, dependsOn: configurations.doclava) {
    docletpath = configurations.doclava.resolve()
    destinationDir = project.docsDir

    // Base classpath is Android SDK, sub-projects add their own.
    classpath = getAndroidPrebuilt(gradle.ext.currentSdk)

    apiFile = new File(project.docsDir, 'release/current.txt')
    removedApiFile = new File(project.docsDir, 'release/removed.txt')
    generateDocs = false

    options {
        addStringOption "templatedir",
                "${project.rootDir}/../../build/tools/droiddoc/templates-sdk"
        addStringOption "federate Android", "http://developer.android.com"
        addStringOption "federationapi Android",
                "${project.rootDir}/../../prebuilts/sdk/api/24.txt"
        addStringOption "stubpackages", "android.support.*"
    }
    exclude '**/BuildConfig.java'
    exclude '**/R.java'
}

// Copies generated API files to current version.
task updateApi(type: UpdateApiTask, dependsOn: generateApi) {
    newApiFile = new File(project.docsDir, 'release/current.txt')
    oldApiFile = new File(project.rootDir, 'api/current.txt')
    newRemovedApiFile = new File(project.docsDir, 'release/removed.txt')
    oldRemovedApiFile = new File(project.rootDir, 'api/removed.txt')
}

// Checks generated API files against current version.
task checkApi(type: CheckApiTask, dependsOn: generateApi) {
    doclavaClasspath = generateApi.docletpath

    checkApiTaskPath = name
    updateApiTaskPath = updateApi.name

    newApiFile = new File(project.docsDir, 'release/current.txt')
    oldApiFile = new File(project.rootDir, 'api/current.txt')
    newRemovedApiFile = new File(project.docsDir, 'release/removed.txt')
    oldRemovedApiFile = new File(project.rootDir, 'api/removed.txt')
}
createArchive.dependsOn checkApi

subprojects {
    // Only modify android projects.
    if (project.name.equals('doclava')) return;

    // current SDK is set in studioCompat.gradle
    project.ext.currentSdk = gradle.ext.currentSdk
    apply plugin: 'maven'
    project.ext.createApiSourceSets = this.&createApiSourceset
    project.ext.setApiModuleDependencies = this.&setApiModuleDependencies

    version = rootProject.ext.supportVersion
    group = 'com.android.support'

    repositories {
        maven { url "${project.parent.projectDir}/../../prebuilts/tools/common/m2/repository" }
        maven { url "${project.parent.projectDir}/../../prebuilts/tools/common/m2/internal" }
        maven { url "${project.parent.projectDir}/../../prebuilts/maven_repo/android" }
    }

    project.plugins.whenPluginAdded { plugin ->
        if ("com.android.build.gradle.LibraryPlugin".equals(plugin.class.name)
                || "com.android.build.gradle.AppPlugin".equals(plugin.class.name)) {
            project.android.buildToolsVersion = rootProject.buildToolsVersion
            // enable code coverage for debug builds only if we are not running inside the IDE
            // enabling coverage reports breaks the method parameter resolution in the IDE debugger
            project.android.buildTypes.debug.testCoverageEnabled = !hasProperty('android.injected.invoked.from.ide')
        }

        // Create release and separate zip task for Android libraries (and android-annotations,
        // which is just a Java library).
        if ("com.android.build.gradle.LibraryPlugin".equals(plugin.class.name)
                || "org.gradle.api.plugins.JavaPlugin".equals(plugin.class.name)) {
            task release(type: Upload) {
                configuration = configurations.archives
                repositories {
                    mavenDeployer {
                        repository(url: uri("$rootProject.ext.supportRepoOut"))

                        // Disable unique names for SNAPSHOTS so they can be updated in place.
                        setUniqueVersion(false)
                        doLast {
                            // Remove any invalid maven-metadata.xml files that may have been
                            // created for SNAPSHOT versions that are *not* uniquely versioned.
                            pom*.each { pom ->
                                if (pom.version.endsWith('-SNAPSHOT')) {
                                    final File artifactDir = new File(
                                            rootProject.ext.supportRepoOut,
                                            pom.groupId.replace('.', '/')
                                                    + '/' + pom.artifactId
                                                    + '/' + pom.version)
                                    delete fileTree(dir: artifactDir,
                                            include: 'maven-metadata.xml*')
                                }
                            }
                        }
                    }
                }
            }

            def deployer = release.repositories.mavenDeployer
            deployer.pom*.whenConfigured { pom ->
                pom.dependencies.findAll { dep ->
                    dep.groupId == 'com.android.support' && dep.artifactId != 'support-annotations'
                }*.type = 'aar'
            }

            ext.versionDir = {
                def groupDir = new File(rootProject.ext.supportRepoOut,
                        project.group.replace('.','/'))
                def artifactDir = new File(groupDir, archivesBaseName)
                return new File(artifactDir, version)
            }

            task generateSourceProps(dependsOn: createRepository) << {
                def content = "Maven.GroupId=$deployer.pom.groupId\n" +
                        "Maven.ArtifactId=$deployer.pom.artifactId\n" +
                        "Maven.Version=$deployer.pom.version\n" +
                        "Extra.VendorDisplay=Android\n" +
                        "Extra.VendorId=android\n" +
                        "Pkg.Desc=$project.name\n" +
                        "Pkg.Revision=1\n" +
                        "Maven.Dependencies=" +
                        String.join(",", project.configurations.compile.allDependencies.collect {
                            def p = parent.findProject(it.name)
                            return p ? "$p.group:$p.archivesBaseName:$p.version" : null
                        }.grep()) +
                        "\n"
                Files.write(content, new File(versionDir(), 'source.properties'), Charsets.UTF_8)
            }

            task createSeparateZip(type: Zip, dependsOn: generateSourceProps)  {
                into archivesBaseName
                destinationDir project.parent.ext.distDir
                baseName = project.group
                version = project.parent.ext.buildNumber
            }
            project.parent.createArchive.dependsOn createSeparateZip

            // before the upload, make sure the repo is ready.
            release.dependsOn rootProject.tasks.prepareRepo
            // make the mainupload depend on this one.
            mainUpload.dependsOn release
        }
    }

    project.afterEvaluate {
        // The archivesBaseName isn't available intially, so set it now
        def createZipTask = project.tasks.findByName("createSeparateZip")
        if (createZipTask != null) {
            createZipTask.appendix = archivesBaseName
            createZipTask.from versionDir()
        }

        // Copy instrumentation test APK into the dist dir
        def assembleTestTask = project.tasks.findByPath('assembleAndroidTest')
        if (assembleTestTask != null) {
            assembleTestTask.doLast {
                // If the project actually has some instrumentation tests, copy its APK
                if (!project.android.sourceSets.androidTest.java.sourceFiles.isEmpty()) {
                    def pkgTask = project.tasks.findByPath('packageDebugAndroidTest')
                    copy {
                        from(pkgTask.outputFile)
                        into(rootProject.ext.testApkDistOut)
                    }
                }
            }
        }
    }

    project.afterEvaluate { p ->
        // remove dependency on the test so that we still get coverage even if some tests fail
        p.tasks.findAll { it instanceof JacocoReportTask}.each { task ->
            def toBeRemoved = new ArrayList()
            def dependencyList = task.taskDependencies.values
            dependencyList.each { dep ->
                if (dep instanceof String) {
                    def t = tasks.findByName(dep)
                    if (t instanceof DeviceProviderInstrumentTestTask) {
                        toBeRemoved.add(dep)
                        task.mustRunAfter(t)
                    }
                }
            }
            toBeRemoved.each { dep ->
                dependencyList.remove(dep)
            }
        }
    }

    project.afterEvaluate { p ->
        if (p.hasProperty('android')
                && p.android.hasProperty('libraryVariants')
                && !(p.android.hasProperty('noDocs') && p.android.noDocs)) {
            p.android.libraryVariants.all { v ->
                if (v.name == 'release') {
                    registerForDocsTask(rootProject.generateDocs, p, v)
                    registerForDocsTask(rootProject.generateApi, p, v)
                }
            }
        }
    }
}

project.gradle.buildFinished { buildResult ->
    if (buildResult.getFailure() != null) {
        println()
        println 'Build failed. Possible causes include:'
        println '    1) Bad codes'
        println '    2) Out of date prebuilts in prebuilts/sdk'
        println '    3) Need to update the compileSdkVersion in a library\'s build.gradle'
        println()
    }
}