import java.nio.file.Paths

// android/build.gradle

// based on:
//
// * https://github.com/facebook/react-native/blob/0.60-stable/template/android/build.gradle
//   previous location:
//   - https://github.com/facebook/react-native/blob/0.58-stable/local-cli/templates/HelloWorld/android/build.gradle
//
// * https://github.com/facebook/react-native/blob/0.60-stable/template/android/app/build.gradle
//   previous location:
//   - https://github.com/facebook/react-native/blob/0.58-stable/local-cli/templates/HelloWorld/android/app/build.gradle

// FBJNI build is based on:
// https://github.com/facebookincubator/fbjni/blob/main/docs/android_setup.md

// These defaults should reflect the SDK versions used by
// the minimum React Native version supported.
def DEFAULT_COMPILE_SDK_VERSION = 28
def DEFAULT_BUILD_TOOLS_VERSION = '28.0.3'
def DEFAULT_MIN_SDK_VERSION = 16
def DEFAULT_TARGET_SDK_VERSION = 28

def safeExtGet(prop, fallback) {
    rootProject.ext.has(prop) ? rootProject.ext.get(prop) : fallback
}

def isNewArchitectureEnabled() {
    // To opt-in for the New Architecture, you can either:
    // - Set `newArchEnabled` to true inside the `gradle.properties` file
    // - Invoke gradle with `-newArchEnabled=true`
    // - Set an environment variable `ORG_GRADLE_PROJECT_newArchEnabled=true`
    return project.hasProperty("newArchEnabled") && project.newArchEnabled == "true"
}

apply plugin: 'com.android.library'

def reactNativeArchitectures() {
  def value = project.getProperties().get("reactNativeArchitectures")
  return value ? value.split(",") : ["armeabi-v7a", "x86", "x86_64", "arm64-v8a"]
}

static def findNodeModules(baseDir) {
  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 nodeModulesPath = Paths.get(basePath.toString(), "node_modules")
    def reactNativePath = Paths.get(nodeModulesPath.toString(), "react-native")
    if (nodeModulesPath.toFile().exists() && reactNativePath.toFile().exists()) {
      return nodeModulesPath.toString()
    }
    basePath = basePath.getParent()
  }
  throw new GradleException("React-Native-Skia: Failed to find node_modules/ path!")
}

def nodeModules = findNodeModules(projectDir)
logger.warn("react-native-skia: node_modules/ found at: ${nodeModules}")

def sourceBuild = false
def defaultDir

if (isNewArchitectureEnabled()) {
    apply plugin: "com.facebook.react"
}

if (rootProject.ext.has('reactNativeAndroidRoot')) {
  defaultDir = rootProject.ext.get('reactNativeAndroidRoot')
} else if (findProject(':ReactAndroid') != null) {
    sourceBuild = true
    defaultDir = project(':ReactAndroid').projectDir
} else {
  defaultDir = file("$nodeModules/react-native")
}

if (!defaultDir.exists()) {
    throw new GradleException(
      "${project.name}: React Native android directory (node_modules/react-native/android) does not exist! Resolved node_modules to: ${nodeModules}"
    )
}

def prebuiltDir = sourceBuild
    ? "$nodeModules/react-native/ReactAndroid/src/main/jni/prebuilt/lib"
    : "$buildDir/react-native-0*/jni"


def buildType = "debug"
if (gradle.startParameter.taskRequests.args[0].toString().contains("Release")) {
    buildType = "release"
} else if (gradle.startParameter.taskRequests.args[0].toString().contains("Debug")) {
    buildType = "debug"
}

def reactProperties = new Properties()
file("$nodeModules/react-native/ReactAndroid/gradle.properties").withInputStream { reactProperties.load(it) }
def FULL_RN_VERSION =  (System.getenv("REACT_NATIVE_OVERRIDE_VERSION") ?: reactProperties.getProperty("VERSION_NAME"))
def REACT_NATIVE_VERSION = FULL_RN_VERSION.split("\\.")[1].toInteger()
def ENABLE_PREFAB = REACT_NATIVE_VERSION > 68

logger.warn("react-native-skia: RN Version: ${REACT_NATIVE_VERSION} / ${FULL_RN_VERSION}")
logger.warn("react-native-skia: isSourceBuild: ${sourceBuild}")
logger.warn("react-native-skia: PrebuiltDir: ${prebuiltDir}")
logger.warn("react-native-skia: buildType: ${buildType}")
logger.warn("react-native-skia: buildDir: ${buildDir}")
logger.warn("react-native-skia: node_modules: ${nodeModules}")
logger.warn("react-native-skia: Enable Prefab: ${ENABLE_PREFAB}")

buildscript {
    // The Android Gradle plugin is only required when opening the android folder stand-alone.
    // This avoids unnecessary downloads and potential conflicts when the library is included as a
    // module dependency in an application project.
    // ref: https://docs.gradle.org/current/userguide/tutorial_using_tasks.html#sec:build_script_external_dependencies
    if (project == rootProject) {
        repositories {
            google()
        }
        dependencies {
            // This should reflect the Gradle plugin version used by
            // the minimum React Native version supported.
            classpath 'com.android.tools.build:gradle:3.4.1'
        }
    }
}

android {
    def agpVersion = com.android.Version.ANDROID_GRADLE_PLUGIN_VERSION
    if (agpVersion.tokenize('.')[0].toInteger() >= 7) {
      namespace "com.shopify.reactnative.skia"
    }

    compileSdkVersion safeExtGet('compileSdkVersion', DEFAULT_COMPILE_SDK_VERSION)
    buildToolsVersion safeExtGet('buildToolsVersion', DEFAULT_BUILD_TOOLS_VERSION)
    defaultConfig {
        minSdkVersion safeExtGet('minSdkVersion', DEFAULT_MIN_SDK_VERSION)
        targetSdkVersion safeExtGet('targetSdkVersion', DEFAULT_TARGET_SDK_VERSION)
        versionCode 1
        versionName "1.0"
        buildConfigField "boolean", "IS_NEW_ARCHITECTURE_ENABLED", isNewArchitectureEnabled().toString()

        externalNativeBuild {
            cmake {
                cppFlags "-fexceptions", "-frtti", "-std=c++1y", "-DONANDROID"
                abiFilters (*reactNativeArchitectures())
                arguments '-DANDROID_STL=c++_shared',
                          "-DREACT_NATIVE_VERSION=${REACT_NATIVE_VERSION}",
                          "-DNODE_MODULES_DIR=${nodeModules}",
                          "-DPREBUILT_DIR=${prebuiltDir}"

            }
        }
    }
    lintOptions {
        abortOnError false
    }

    publishing {
        singleVariant("release") {
            withSourcesJar()
        }
    }

    sourceSets.main {
        jniLibs {
            srcDirs += ['../libs/android']
        }
        java {
            if (!isNewArchitectureEnabled()) {
                srcDirs += [
                    "src/paper/java",
                ]
            }

            if (REACT_NATIVE_VERSION >= 74) {
                srcDirs += [
                    "src/reactnative74/java"
                ]
            } else {
                srcDirs += [
                    "src/reactnative69/java"
                ]
            }
        }
    }

    externalNativeBuild {
        cmake {
            path file('CMakeLists.txt')
        }
    }

    packagingOptions {
        excludes = [
             "**/libc++_shared.so",
             "**/libfbjni.so",
             "**/libjsi.so",
             "**/libreact_nativemodule_core.so",
             "**/libreactnativejni.so",
             "**/libruntimeexecutor.so",
             "**/libturbomodulejsijni.so",
             "META-INF/**"
        ]
    }

    if (ENABLE_PREFAB) {
        buildFeatures {
                prefab true
                prefabPublishing true
        }
        prefab {
            rnskia {
                headers "${project.buildDir}/headers/rnskia/"
            }
        }
    }

    // Create new configurations that can be referred to in dependencies.
    // The Android Gradle Plugin 3.* does not allow hooking into existing
    // configurations like `implementation`.
    configurations {
        extractHeaders
        extractJNI
    }
}

repositories {
    maven {
        // All of React Native (JS, Obj-C sources, Android binaries) is installed from npm
        url("$rootDir/../node_modules/react-native/android")
    }
    maven {
        // Android JSC is installed from npm
        url("$rootDir/../node_modules/jsc-android/dist")
    }
    mavenCentral {
        // We don't want to fetch react-native from Maven Central as there are
        // older versions over there.
        content {
            excludeGroup "com.facebook.react"
        }
    }
    google()
    maven { url 'https://www.jitpack.io' }
}

dependencies {
    //noinspection GradleDynamicVersion
    implementation 'com.facebook.react:react-native:+'  // From node_modules

    if (REACT_NATIVE_VERSION < 71) {
        logger.warn("react-native-skia: Extracting files from AAR (pre RN 0.71)")
        //noinspection GradleDynamicVersion
        extractHeaders("com.facebook.fbjni:fbjni:0.2.2:headers")
        //noinspection GradleDynamicVersion
        extractJNI("com.facebook.fbjni:fbjni:0.2.2")
    }

    if (!sourceBuild) {
        def rnAarMatcher = "**/react-native/**/*${buildType}.aar"
        if (REACT_NATIVE_VERSION < 69) {
            logger.warn("react-native-skia: aar state pre 69. match **/**/*.aar")
            rnAarMatcher = "**/**/*.aar"
        } else if (REACT_NATIVE_VERSION >= 71) {
            logger.warn("react-native-skia: aar state post 70, do nothing")
            return
        }
        def rnAAR = fileTree("${nodeModules}/react-native/android").matching({ it.include rnAarMatcher }).singleFile
        logger.warn("react-native-skia: Extracting JNI files (pre RN 0.71) ${rnAAR}")
        extractJNI(files(rnAAR))
    }
}

afterEvaluate { project ->
    android.libraryVariants.all { variant ->
        def name = variant.name.capitalize()
        def javaCompileTask = variant.javaCompileProvider.get()

        task "jar${name}"(type: Jar, dependsOn: javaCompileTask) {
            from javaCompileTask.destinationDir
        }
    }
}

task extractAARHeaders {
    doLast {
        configurations.extractHeaders.files.each {
            def file = it.absoluteFile
            copy {
                from zipTree(file)
                into "$buildDir/$file.name"
                include "**/*.h"
            }
        }
    }
}

task extractJNIFiles {
    doLast {
        configurations.extractJNI.files.each {
            def file = it.absoluteFile
            copy {
                from zipTree(file)
                into "$buildDir/$file.name"
                include "jni/**/*"
            }
        }
    }
}

extractJNIFiles.mustRunAfter extractAARHeaders

if (ENABLE_PREFAB) {
    // Package all the cpp code in a flattened directory structure
    task prepareHeaders(type: Copy) {
        from("../cpp")
        from("./cpp")
        into "${project.buildDir}/headers/rnskia/react-native-skia"
        includeEmptyDirs = false
        include "**/*.h"
        eachFile {
            String path = it.path

            if (path.contains("api/third_party")) {
                // Skip flattening third_party dir
                path = path.substring("api/".length())
            } else if (path.contains("skia/include") || path.contains("skia/modules") || path.contains("skia/src")) {
                // Skip flattening Skia dir
                path = path.substring("skia/".length())
            } else {
                // flatten anything else
                path = path.substring(path.lastIndexOf("/") + 1)
            }
            it.path = path
        }
    }
    preBuild.dependsOn(prepareHeaders)
}

def nativeBuildDependsOn(dependsOnTask, variant) {
  def buildTasks = tasks.findAll({ task ->
      !task.name.contains("Clean") && (task.name.contains("externalNative") || task.name.contains("CMake")) })
  if (variant != null) {
    buildTasks = buildTasks.findAll({ task -> task.name.contains(variant) })
  }
  buildTasks.forEach { task -> task.dependsOn(dependsOnTask) }
}

afterEvaluate {
  nativeBuildDependsOn(extractAARHeaders, null)
  nativeBuildDependsOn(extractJNIFiles, null)
}
