plugins {
    id 'io.github.gradle-nexus.publish-plugin'
}

apply from: 'app/config.gradle'
apply from: 'scripts/publish-root.gradle'

ext {
    PUBLISH_VERSION = getGodotPublishVersion()
}

group = ossrhGroupId
version = PUBLISH_VERSION

allprojects {
    repositories {
        google()
        mavenCentral()
        gradlePluginPortal()
        maven { url "https://plugins.gradle.org/m2/" }
        maven { url "https://central.sonatype.com/repository/maven-snapshots/"}
    }
}

ext {
    supportedAbis = ["arm32", "arm64", "x86_32", "x86_64"]
    supportedFlavors = ["editor", "template"]
    supportedAndroidDistributions = ["android", "horizonos", "picoos"]
    supportedFlavorsBuildTypes = [
        "editor": ["dev", "debug", "release"],
        "template": ["dev", "debug", "release"]
    ]
    supportedEditions = ["standard", "mono"]

    // Used by gradle to specify which architecture to build for by default when running
    // `./gradlew build` (this command is usually used by Android Studio).
    // If building manually on the command line, it's recommended to use the
    // `./gradlew generateGodotTemplates` build command instead after running the `scons` command(s).
    // The {selectedAbis} values must be from the {supportedAbis} values.
    selectedAbis = ["arm64"]

    rootDir = "../../.."
    binDir = "$rootDir/bin/"
    androidEditorBuildsDir = "$binDir/android_editor_builds/"
}

def getSconsTaskName(String flavor, String buildType, String abi) {
    return "compileGodotNativeLibs" + flavor.capitalize() + buildType.capitalize() + abi.capitalize()
}

/**
 * Generate Godot gradle build template by zipping the source files from the app directory, as well
 * as the AAR files generated by 'copyDebugAAR', 'copyDevAAR' and 'copyReleaseAAR'.
 * The zip file also includes some gradle tools to enable gradle builds from the Godot Editor.
 */
task zipGradleBuild(type: Zip) {
    onlyIf { generateGodotTemplates.state.executed || generateGodotMonoTemplates.state.executed || generateDevTemplate.state.executed }
    doFirst {
        logger.lifecycle("Generating Godot gradle build template")
    }
    from(fileTree(dir: 'app', excludes: ['**/build/**', '**/.gradle/**', '**/*.iml']), fileTree(dir: '.', includes: ['gradlew', 'gradlew.bat', 'gradle/**']))
    include '**/*'
    archiveFileName = 'android_source.zip'
    destinationDirectory = file(binDir)
}

/**
 * Returns true if the scons build tasks responsible for generating the Godot native shared
 * libraries should be excluded.
 */
def excludeSconsBuildTasks() {
    return !isAndroidStudio() && !project.hasProperty("generateNativeLibs")
}

/**
 * Generates the list of build tasks that should be excluded from the build process.\
 */
def templateExcludedBuildTask() {
    // We exclude these gradle tasks so we can run the scons command manually.
    def excludedTasks = []
    if (excludeSconsBuildTasks()) {
        logger.info("Excluding Android studio build tasks")
        for (String flavor : supportedFlavors) {
            String[] supportedBuildTypes = supportedFlavorsBuildTypes[flavor]
            for (String buildType : supportedBuildTypes) {
                for (String abi : selectedAbis) {
                    excludedTasks += ":lib:" + getSconsTaskName(flavor, buildType, abi)
                }
            }
        }
    }
    return excludedTasks
}

/**
 * Generates the build tasks for the given flavor
 * @param flavor Must be one of the supported flavors ('template' / 'editor')
 * @param edition Must be one of the supported editions ('standard' / 'mono')
 * @param androidDistro Must be one of the supported Android distributions ('android' / 'horizonos' / 'picoos')
 */
def generateBuildTasks(String flavor = "template", String edition = "standard", String androidDistro = "android") {
    if (!supportedFlavors.contains(flavor)) {
        throw new GradleException("Invalid build flavor: $flavor")
    }
    if (!supportedAndroidDistributions.contains(androidDistro)) {
        throw new GradleException("Invalid Android distribution: $androidDistro")
    }
    if (!supportedEditions.contains(edition)) {
        throw new GradleException("Invalid build edition: $edition")
    }
    if (edition == "mono" && flavor != "template") {
        throw new GradleException("'mono' edition only supports the 'template' flavor.")
    }

    String capitalizedAndroidDistro = androidDistro.capitalize()
    def buildTasks = []

    // Only build the binary files for which we have native shared libraries unless we intend
    // to run the scons build tasks.
    boolean excludeSconsBuildTasks = excludeSconsBuildTasks()
    boolean isTemplate = flavor == "template"
    String libsDir = isTemplate ? "lib/libs/" : "lib/libs/tools/"
    for (String target : supportedFlavorsBuildTypes[flavor]) {
        File targetLibs = new File(libsDir + target)

        String targetSuffix = target
        if (target == "dev") {
            targetSuffix = "debug.dev"
        }

        if (!excludeSconsBuildTasks || (targetLibs != null
            && targetLibs.isDirectory()
            && targetLibs.listFiles() != null
            && targetLibs.listFiles().length > 0)) {

            String capitalizedTarget = target.capitalize()
            String capitalizedEdition = edition.capitalize()
            if (isTemplate) {
                // Copy the Godot android library archive file into the app module libs directory.
                // Depends on the library build task to ensure the AAR file is generated prior to copying.
                String copyAARTaskName = "copy${capitalizedTarget}AARToAppModule"
                if (tasks.findByName(copyAARTaskName) != null) {
                    buildTasks += tasks.getByName(copyAARTaskName)
                } else {
                    buildTasks += tasks.create(name: copyAARTaskName, type: Copy) {
                        dependsOn ":lib:assembleTemplate${capitalizedTarget}"
                        from('lib/build/outputs/aar')
                        include("godot-lib.template_${targetSuffix}.aar")
                        into("app/libs/${target}")
                    }
                }

                // Copy the Godot android library archive file into the root bin directory.
                // Depends on the library build task to ensure the AAR file is generated prior to copying.
                String copyAARToBinTaskName = "copy${capitalizedTarget}AARToBin"
                if (tasks.findByName(copyAARToBinTaskName) != null) {
                    buildTasks += tasks.getByName(copyAARToBinTaskName)
                } else {
                    buildTasks += tasks.create(name: copyAARToBinTaskName, type: Copy) {
                        dependsOn ":lib:assembleTemplate${capitalizedTarget}"
                        from('lib/build/outputs/aar')
                        include("godot-lib.template_${targetSuffix}.aar")
                        into(binDir)
                    }
                }

                // Copy the generated binary template into the Godot bin directory.
                // Depends on the app build task to ensure the binary is generated prior to copying.
                String copyBinaryTaskName = "copy${capitalizedEdition}${capitalizedTarget}BinaryToBin"
                if (tasks.findByName(copyBinaryTaskName) != null) {
                    buildTasks += tasks.getByName(copyBinaryTaskName)
                } else {
                    buildTasks += tasks.create(name: copyBinaryTaskName, type: Copy) {
                        String filenameSuffix = edition == "mono" ? "${edition}${capitalizedTarget}" : target
                        dependsOn ":app:assemble${capitalizedEdition}${capitalizedTarget}"
                        from("app/build/outputs/apk/${edition}/${target}") {
                            include("android_${filenameSuffix}.apk")
                        }
                        into(binDir)
                    }
                }
            } else {
                // Copy the generated editor apk to the bin directory.
                String copyEditorApkTaskName = "copyEditor${capitalizedAndroidDistro}${capitalizedTarget}ApkToBin"
                if (tasks.findByName(copyEditorApkTaskName) != null) {
                    buildTasks += tasks.getByName(copyEditorApkTaskName)
                } else {
                    buildTasks += tasks.create(name: copyEditorApkTaskName, type: Copy) {
                        dependsOn ":editor:assemble${capitalizedAndroidDistro}${capitalizedTarget}"
                        from("editor/build/outputs/apk/${androidDistro}/${target}") {
                            include("android_editor-${androidDistro}-${target}*.apk")
                        }
                        into(androidEditorBuildsDir)
                    }
                }

                // Copy the generated editor aab to the bin directory.
                String copyEditorAabTaskName = "copyEditor${capitalizedAndroidDistro}${capitalizedTarget}AabToBin"
                if (tasks.findByName(copyEditorAabTaskName) != null) {
                    buildTasks += tasks.getByName(copyEditorAabTaskName)
                } else {
                    buildTasks += tasks.create(name: copyEditorAabTaskName, type: Copy) {
                        dependsOn ":editor:bundle${capitalizedAndroidDistro}${capitalizedTarget}"
                        from("editor/build/outputs/bundle/${androidDistro}${capitalizedTarget}")
                        into(androidEditorBuildsDir)
                        include("android_editor-${androidDistro}-${target}*.aab")
                    }
                }
            }
        } else {
            logger.info("No native shared libs for target $target. Skipping build.")
        }
    }

    return buildTasks
}

/**
 * Generate the Godot Editor binaries for Android devices.
 *
 * Note: Unless the 'generateNativeLibs` argument is specified, the Godot 'tools' shared libraries
 * must have been generated (via scons) prior to running this gradle task.
 * The task will only build the binaries for which the shared libraries is available.
 */
task generateGodotEditor {
    gradle.startParameter.excludedTaskNames += templateExcludedBuildTask()
    dependsOn = generateBuildTasks("editor", "standard", "android")
}

/**
 * Generate the Godot Editor binaries for HorizonOS devices.
 *
 * Note: Unless the 'generateNativeLibs` argument is specified, the Godot 'tools' shared libraries
 * must have been generated (via scons) prior to running this gradle task.
 * The task will only build the binaries for which the shared libraries is available.
 */
task generateGodotHorizonOSEditor {
    gradle.startParameter.excludedTaskNames += templateExcludedBuildTask()
    dependsOn = generateBuildTasks("editor", "standard", "horizonos")
}

/**
 * Generate the Godot Editor binaries for PicoOS devices.
 *
 * Note: Unless the 'generateNativeLibs` argument is specified, the Godot 'tools' shared libraries
 * must have been generated (via scons) prior to running this gradle task.
 * The task will only build the binaries for which the shared libraries is available.
 */
task generateGodotPicoOSEditor {
    gradle.startParameter.excludedTaskNames += templateExcludedBuildTask()
    dependsOn = generateBuildTasks("editor", "standard", "picoos")
}

/**
 * Master task used to coordinate the tasks defined above to generate the set of Godot templates.
 */
task generateGodotTemplates {
    gradle.startParameter.excludedTaskNames += templateExcludedBuildTask()
    dependsOn = generateBuildTasks("template")

    finalizedBy 'zipGradleBuild'
}

/**
 * Master task used to coordinate the tasks defined above to generate the set of Godot templates
 * for the 'mono' edition of the engine.
 */
task generateGodotMonoTemplates {
    gradle.startParameter.excludedTaskNames += templateExcludedBuildTask()
    dependsOn = generateBuildTasks("template", "mono")

    finalizedBy 'zipGradleBuild'
}

task clean(type: Delete) {
    dependsOn 'cleanGodotEditor'
    dependsOn 'cleanGodotTemplates'
}

/**
 * Clean the generated editor artifacts.
 */
task cleanGodotEditor(type: Delete) {
    // Delete the generated native tools libs
    delete("lib/libs/tools")

    // Delete the library generated AAR files
    delete("lib/build/outputs/aar")

    // Delete the generated binary apks
    delete("editor/build/outputs/apk")

    // Delete the generated aab binaries
    delete("editor/build/outputs/bundle")

    // Delete the Godot editor apks & aabs in the Godot bin directory
    delete(androidEditorBuildsDir)
}

/**
 * Clean the generated template artifacts.
 */
task cleanGodotTemplates(type: Delete) {
    // Delete the generated native libs
    delete("lib/libs")

    // Delete the library generated AAR files
    delete("lib/build/outputs/aar")

    // Delete the app libs directory contents
    delete("app/libs")

    // Delete the generated binary apks
    delete("app/build/outputs/apk")

    // Delete the Godot templates in the Godot bin directory
    delete("$binDir/android_debug.apk")
    delete("$binDir/android_dev.apk")
    delete("$binDir/android_release.apk")
    delete("$binDir/android_monoDebug.apk")
    delete("$binDir/android_monoDev.apk")
    delete("$binDir/android_monoRelease.apk")
    delete("$binDir/android_source.zip")
    delete("$binDir/godot-lib.template_debug.aar")
    delete("$binDir/godot-lib.template_debug.dev.aar")
    delete("$binDir/godot-lib.template_release.aar")

    // Cover deletion for the libs using the previous naming scheme
    delete("$binDir/godot-lib.debug.aar")
    delete("$binDir/godot-lib.dev.aar")
    delete("$binDir/godot-lib.release.aar")

    // Delete the native debug symbols files.
    delete("$binDir/android-editor-debug-native-symbols.zip")
    delete("$binDir/android-editor-dev-native-symbols.zip")
    delete("$binDir/android-editor-release-native-symbols.zip")
    delete("$binDir/android-template-debug-native-symbols.zip")
    delete("$binDir/android-template-dev-native-symbols.zip")
    delete("$binDir/android-template-release-native-symbols.zip")
}
