import org.apache.tools.ant.taskdefs.condition.Os

if (project.ext.forceBootifulArtifact || (!rootProject.publishReleases && !rootProject.publishSnapshots && !rootProject.skipBootifulArtifact)) {
    logger.info "Applying Spring Boot plugin to bootify web application artifact for project $project.name..."

    apply plugin: "org.springframework.boot"

    sourceSets {
        bootRun {
            resources {
            }
        }
    }

    springBoot {
        mainClassName = project.ext.mainClassName
        buildInfo()
    }

    if (plugins.hasPlugin("war")) {
        
        javadoc {
            enabled false
        }

        bootWar {
            mainClass.set(project.ext.mainClassName)
            manifest {
                attributes(
                        "Automatic-Module-Name": project.name,
                        "Implementation-Title": project.name,
                        "Implementation-Vendor": project.group,
                        "Created-By": project.group,
                        "Implementation-Date": java.time.ZonedDateTime.now(),
                        "Specification-Version": "${-> project.ext.has("gitProps") ? project.ext.gitProps['git.commit.id'] : 'N/A'}",
                        "Implementation-Version": project.version)
            }
            from "${buildDir}/resources/main", { into "WEB-INF/classes" }
            duplicatesStrategy = DuplicatesStrategy.EXCLUDE
            launchScript()
        }
    
    } else {
        bootJar {
            mainClass.set(project.ext.mainClassName)
            from "${buildDir}/resources/main", { into "WEB-INF/classes" }
            launchScript()
        }

        /*
         * Gradle performs a sanity check before uploading, to make sure you don’t upload stale files (files produced by another build).
         * This introduces a problem with Spring Boot applications; the main jar task is disabled by the Spring Boot application,
         * and the component expects it to be present. Because the bootJar task uses the same file as the main jar
         * task by default, releases of Gradle would either publish a stale bootJar artifact or fail if the bootJar task hasn’t been called previously.
         *
         * A workaround is to tell Gradle what to upload. If you want to upload the bootJar, then you
         * need to configure the outgoing configurations to do this:
         */
        configurations {
            [apiElements, runtimeElements].each {
                it.outgoing.artifacts.removeIf { it.buildDependencies.getDependencies(null).contains(jar) }
                it.outgoing.artifact(bootJar)
            }
        }
    }

    bootRun {
        mainClass.set(project.ext.mainClassName)
        
        if (Os.isFamily(Os.FAMILY_WINDOWS)) {
            dependsOn pathingJar
            doFirst {
                classpath = files("$buildDir/classes/main", "$buildDir/resources/main", pathingJar.archivePath)
            }
        }

        sourceResources sourceSets.bootRun
        systemProperties = System.properties
        
        def list = []
        list.add("-XX:TieredStopAtLevel=1")
        list.add("-Xverify:none")
        list.add("--add-modules")
        list.add("java.se")
        list.add("--add-exports")
        list.add("java.base/jdk.internal.ref=ALL-UNNAMED")
        list.add("--add-opens")
        list.add("java.base/java.lang=ALL-UNNAMED")
        list.add("--add-opens")
        list.add("java.base/java.nio=ALL-UNNAMED")
        list.add("--add-opens")
        list.add("java.base/sun.nio.ch=ALL-UNNAMED")
        list.add("--add-opens")
        list.add("java.management/sun.management=ALL-UNNAMED")
        list.add("--add-opens")
        list.add("jdk.management/com.sun.management.internal=ALL-UNNAMED")

        if (rootProject.enableRemoteDebugging) {
            def port = findAvailableTcpPort(5001..5010)
            if (port > 0) {
                println "Listening on port ${port} for remote debugging requests"
                list.add("-Xrunjdwp:transport=dt_socket,address=${port},server=y,suspend=${rootProject.remoteDebuggingSuspend}")
            }
        }
        jvmArgs = list
    }
} else {
    logger.info """
Ignoring application of Spring Boot plugin, since the build is preparing to publish SNAPSHOTs or releases,
or the build is signaled to skip the plugin explicitly.
    """
}

int findAvailableTcpPort(List<Integer> portRange) {
    for (int port : portRange) {
        try {
            logger.debug "Checking port ${port}"
            def serverSocket = new ServerSocket(port)
            serverSocket.close()
            return port
        } catch (Exception e) {
             logger.debug "Port ${port} is not available: ${e.message}"
        }
    }
    return -1
}
