apply plugin: 'com.android.library'
apply plugin: 'maven'

group = 'host.exp.exponent'
version = '10.3.0'

buildscript {
  // Simple helper that allows the root project to override versions declared by this library.
  ext.safeExtGet = { prop, fallback ->
    rootProject.ext.has(prop) ? rootProject.ext.get(prop) : fallback
  }

  repositories {
    mavenCentral()
  }

  dependencies {
    classpath("de.undercouch:gradle-download-task:${safeExtGet("gradleDownloadTaskVersion", "3.4.3")}")
  }
}

import java.nio.file.Paths

import de.undercouch.gradle.tasks.download.Download
import org.apache.tools.ant.taskdefs.condition.Os
import org.apache.tools.ant.filters.ReplaceTokens

def BOOST_VERSION = "1_63_0"
def DOUBLE_CONVERSION_VERSION = "1.1.6"
def FOLLY_VERSION = "2018.10.22.00"
def GLOG_VERSION = "0.3.5"



// We download various C++ open-source dependencies into downloads.
// We then copy both the downloaded code and our custom makefiles and headers into third-party-ndk.
// After that we build native code from src/main/jni with module path pointing at third-party-ndk.

def customDownloadsDir = System.getenv("REACT_NATIVE_DOWNLOADS_DIR")
def downloadsDir = customDownloadsDir ? new File(customDownloadsDir) : new File("$buildDir/downloads")
def thirdPartyNdkDir = new File("$buildDir/third-party-ndk")

// You need to have following folders in this directory:
//   - boost_1_63_0
//   - double-conversion-1.1.6
//   - folly-deprecate-dynamic-initializer
//   - glog-0.3.5
def dependenciesPath = System.getenv("REACT_NATIVE_DEPENDENCIES")

// The Boost library is a very large download (>100MB).
// If Boost is already present on your system, define the REACT_NATIVE_BOOST_PATH env variable
// and the build will use that.
def boostPath = dependenciesPath ?: System.getenv("REACT_NATIVE_BOOST_PATH")

//Upload android library to maven with javadoc and android sources
configurations {
  deployerJars
}

//Creating sources with comments
task androidSourcesJar(type: Jar) {
  classifier = 'sources'
  from android.sourceSets.main.java.srcDirs
}

//Put the androidSources and javadoc to the artifacts
artifacts {
  archives androidSourcesJar
}

uploadArchives {
  repositories {
    mavenDeployer {
      configuration = configurations.deployerJars
      repository(url: mavenLocal().url)
    }
  }
}

android {
  compileSdkVersion safeExtGet("compileSdkVersion", 30)

  defaultConfig {
    minSdkVersion safeExtGet("minSdkVersion", 21)
    targetSdkVersion safeExtGet("targetSdkVersion", 30)
    versionCode 31
    versionName "10.3.0"

    ndk {
      abiFilters 'armeabi-v7a', 'x86', 'arm64-v8a', 'x86_64'
      moduleName 'expo-gl'
    }

    sourceSets.main {
      jni.srcDirs = []
      jniLibs.srcDir "$projectDir/dist"
    }
  }
  lintOptions {
    abortOnError false
  }
}

repositories {
  mavenCentral()
}

if (new File(rootProject.projectDir.parentFile, 'package.json').exists()) {
  apply from: project(":unimodules-core").file("../unimodules-core.gradle")
} else {
  throw new GradleException(
      "'unimodules-core.gradle' was not found in the usual React Native dependency location. " +
          "This package can only be used in such projects. Are you sure you've installed the dependencies properly?")
}

dependencies {
  compileOnly 'com.facebook.soloader:soloader:0.8.2'
}
task createNativeDepsDirectories {
  downloadsDir.mkdirs()
  thirdPartyNdkDir.mkdirs()
}

task downloadBoost(dependsOn: createNativeDepsDirectories, type: Download) {
  src("https://github.com/react-native-community/boost-for-react-native/releases/download/v${BOOST_VERSION.replace("_", ".")}-0/boost_${BOOST_VERSION}.tar.gz")
  onlyIfNewer(true)
  overwrite(false)
  dest(new File(downloadsDir, "boost_${BOOST_VERSION}.tar.gz"))
}

task prepareBoost(dependsOn: boostPath ? [] : [downloadBoost], type: Copy) {
  from(boostPath ?: tarTree(resources.gzip(downloadBoost.dest)))
  from("src/main/jni/boost/Android.mk")
  include("Android.mk", "boost_${BOOST_VERSION}/boost/**/*.hpp", "boost/boost/**/*.hpp")
  includeEmptyDirs = false
  into("$thirdPartyNdkDir/boost")
  doLast {
    file("$thirdPartyNdkDir/boost/boost").renameTo("$thirdPartyNdkDir/boost/boost_${BOOST_VERSION}")
  }
}

task downloadDoubleConversion(dependsOn: createNativeDepsDirectories, type: Download) {
  src("https://github.com/google/double-conversion/archive/v${DOUBLE_CONVERSION_VERSION}.tar.gz")
  onlyIfNewer(true)
  overwrite(false)
  dest(new File(downloadsDir, "double-conversion-${DOUBLE_CONVERSION_VERSION}.tar.gz"))
}

task prepareDoubleConversion(dependsOn: dependenciesPath ? [] : [downloadDoubleConversion], type: Copy) {
  from(dependenciesPath ?: tarTree(downloadDoubleConversion.dest))
  from("src/main/jni/double-conversion/Android.mk")
  include("double-conversion-${DOUBLE_CONVERSION_VERSION}/src/**/*", "Android.mk")
  filesMatching("*/src/**/*", { fname -> fname.path = "double-conversion/${fname.name}" })
  includeEmptyDirs = false
  into("$thirdPartyNdkDir/double-conversion")
}

task downloadFolly(dependsOn: createNativeDepsDirectories, type: Download) {
  src("https://github.com/facebook/folly/archive/v${FOLLY_VERSION}.tar.gz")
  onlyIfNewer(true)
  overwrite(false)
  dest(new File(downloadsDir, "folly-${FOLLY_VERSION}.tar.gz"))
}

task prepareFolly(dependsOn: dependenciesPath ? [] : [downloadFolly], type: Copy) {
  from(dependenciesPath ?: tarTree(downloadFolly.dest))
  from("src/main/jni/folly/Android.mk")
  include("folly-${FOLLY_VERSION}/folly/**/*", "Android.mk")
  eachFile { fname -> fname.path = (fname.path - "folly-${FOLLY_VERSION}/") }
  includeEmptyDirs = false
  into("$thirdPartyNdkDir/folly")
}

task downloadGlog(dependsOn: createNativeDepsDirectories, type: Download) {
  src("https://github.com/google/glog/archive/v${GLOG_VERSION}.tar.gz")
  onlyIfNewer(true)
  overwrite(false)
  dest(new File(downloadsDir, "glog-${GLOG_VERSION}.tar.gz"))
}

// Prepare glog sources to be compiled, this task will perform steps that normally should've been
// executed by automake. This way we can avoid dependencies on make/automake
task prepareGlog(dependsOn: dependenciesPath ? [] : [downloadGlog], type: Copy) {
  from(dependenciesPath ?: tarTree(downloadGlog.dest))
  from("src/main/jni/glog/")
  include("glog-${GLOG_VERSION}/src/**/*", "Android.mk", "config.h")
  includeEmptyDirs = false
  filesMatching("**/*.h.in") {
    filter(ReplaceTokens, tokens: [
        ac_cv_have_unistd_h           : "1",
        ac_cv_have_stdint_h           : "1",
        ac_cv_have_systypes_h         : "1",
        ac_cv_have_inttypes_h         : "1",
        ac_cv_have_libgflags          : "0",
        ac_google_start_namespace     : "namespace google {",
        ac_cv_have_uint16_t           : "1",
        ac_cv_have_u_int16_t          : "1",
        ac_cv_have___uint16           : "0",
        ac_google_end_namespace       : "}",
        ac_cv_have___builtin_expect   : "1",
        ac_google_namespace           : "google",
        ac_cv___attribute___noinline  : "__attribute__ ((noinline))",
        ac_cv___attribute___noreturn  : "__attribute__ ((noreturn))",
        ac_cv___attribute___printf_4_5: "__attribute__((__format__ (__printf__, 4, 5)))"
    ])
    it.path = (it.name - ".in")
  }
  into("$thirdPartyNdkDir/glog")

  doLast {
    copy {
      from(fileTree(dir: "$thirdPartyNdkDir/glog", includes: ["stl_logging.h", "logging.h", "raw_logging.h", "vlog_is_on.h", "**/src/glog/log_severity.h"]).files)
      includeEmptyDirs = false
      into("$thirdPartyNdkDir/glog/exported/glog")
    }
  }
}

task prepareJSI() {
  def hermesPackagePath = findNodeModulePath(projectDir, "hermes-engine")
  if (!hermesPackagePath) {
    throw new GradleScriptException("Could not find the hermes-engine npm package")
  }
  copy {
    from("../../../android/ReactCommon/jsi")
    into "$thirdPartyNdkDir/jsi"
  }
}

task downloadNdkBuildDependencies {
  if (!boostPath) {
    dependsOn(downloadBoost)
  }
  dependsOn(downloadDoubleConversion)
  dependsOn(downloadFolly)
  dependsOn(downloadGlog)
}

/**
 * Finds the path of the installed npm package with the given name using Node's
 * module resolution algorithm, which searches "node_modules" directories up to
 * the file system root. This handles various cases, including:
 *
 *   - Working in the open-source RN repo:
 *       Gradle: /path/to/react-native/ReactAndroid
 *       Node module: /path/to/react-native/node_modules/[package]
 *
 *   - Installing RN as a dependency of an app and searching for hoisted
 *     dependencies:
 *       Gradle: /path/to/app/node_modules/react-native/ReactAndroid
 *       Node module: /path/to/app/node_modules/[package]
 *
 *   - Working in a larger repo (e.g., Facebook) that contains RN:
 *       Gradle: /path/to/repo/path/to/react-native/ReactAndroid
 *       Node module: /path/to/repo/node_modules/[package]
 *
 * The search begins at the given base directory (a File object). The returned
 * path is a string.
 */
def findNodeModulePath(baseDir, packageName) {
  def basePath = baseDir.toPath().normalize()
  // Node's module resolution algorithm searches up to the root directory,
  // after which the base path will be null
  while (basePath) {
    def candidatePath = Paths.get(basePath.toString(), "node_modules", packageName)
    if (candidatePath.toFile().exists()) {
      return candidatePath.toString()
    }
    basePath = basePath.getParent()
  }
  return null
}

def getNdkBuildName() {
  if (Os.isFamily(Os.FAMILY_WINDOWS)) {
    return "ndk-build.cmd"
  } else {
    return "ndk-build"
  }
}

def findNdkBuildFullPath() {
  // we allow to provide full path to ndk-build tool
  if (hasProperty("ndk.command")) {
    return property("ndk.command")
  }
  // or just a path to the containing directory
  if (hasProperty("ndk.path")) {
    def ndkDir = property("ndk.path")
    return new File(ndkDir, getNdkBuildName()).getAbsolutePath()
  }

  if (System.getenv("ANDROID_NDK_HOME") != null) {
    def ndkDir = System.getenv("ANDROID_NDK_HOME")
    return new File(ndkDir, getNdkBuildName()).getAbsolutePath()
  }

  // Left for the legacy reasons, use the previous one.
  if (System.getenv("ANDROID_NDK") != null) {
    def ndkDir = System.getenv("ANDROID_NDK")
    return new File(ndkDir, getNdkBuildName()).getAbsolutePath()
  }

  try {
    def ndkDir = android.ndkDirectory.absolutePath
    if (ndkDir) {
      return new File(ndkDir, getNdkBuildName()).getAbsolutePath()
    }
  } catch (InvalidUserDataException e) {
    throw new GradleScriptException(
        "Default side-by-side NDK installation is not found.\n" +
        "Set \$ANDROID_NDK_HOME environment variable correctly or setup ndk.dir in local.properties.", e
    )
  }

  return null
}

def getNdkBuildFullPath() {
  def ndkBuildFullPath = findNdkBuildFullPath()
  if (ndkBuildFullPath == null) {
    throw new GradleScriptException(
        "ndk-build binary cannot be found, check if you've set " +
            "\$ANDROID_NDK_HOME environment variable correctly or if ndk.dir is " +
            "setup in local.properties",
        null)
  }
  if (!new File(ndkBuildFullPath).canExecute()) {
    throw new GradleScriptException(
        "ndk-build binary " + ndkBuildFullPath + " doesn't exist or isn't executable.\n" +
            "Check that the \$ANDROID_NDK_HOME environment variable, or ndk.dir in local.properties, is set correctly.\n" +
            "(On Windows, make sure you escape backslashes in local.properties or use forward slashes, e.g. C:\\\\ndk or C:/ndk rather than C:\\ndk)",
        null)
  }
  return ndkBuildFullPath
}

task buildNdkLib(dependsOn: [prepareJSI, prepareBoost, prepareDoubleConversion, prepareFolly, prepareGlog /* */], type: Exec) {
  inputs.dir("src/main/jni")
  inputs.dir("../cpp")
  outputs.dir("$buildDir/expo-gl-ndk/all")
  commandLine(getNdkBuildFullPath(),
      "NDK_PROJECT_PATH=null",
      "NDK_APPLICATION_MK=$projectDir/src/main/jni/Application.mk",
      "NDK_OUT=" + temporaryDir,
      "NDK_LIBS_OUT=$buildDir/expo-gl-ndk/all",
      "THIRD_PARTY_NDK_DIR=$buildDir/third-party-ndk",
      "-C", file("src/main/jni").absolutePath,
      "--jobs", project.findProperty("jobs") ?: Runtime.runtime.availableProcessors()
  )
}

task cleanNdkLib(type: Exec) {
  ignoreExitValue(true)
  errorOutput(new ByteArrayOutputStream())
  commandLine(getNdkBuildFullPath(),
      "THIRD_PARTY_NDK_DIR=$buildDir/third-party-ndk",
      "-C", file("src/main/jni").absolutePath,
      "clean")
}

task packageNdkLibs(dependsOn: buildNdkLib, type: Copy) {
  from("$buildDir/expo-gl-ndk/all")
  include("**/libexpo-gl.so")
  into("$projectDir/dist")
}
