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

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

    apply plugin: "org.springframework.boot"

    sourceSets {
        /**
         * Make resources become reloadable.
         * Application’s static resources are loaded from their source location.
         */
        bootRun {
            if (project.hasProperty("casWebApplication") && project.ext.casWebApplication) {
                resources {
                    srcDirs new File("/etc/cas/templates/"),
                            new File("${project(":webapp").getProjectDir()}/cas-server-webapp-resources/src/main/resources/"),
                            new File("${project(":support").getProjectDir()}/cas-server-support-thymeleaf/src/main/resources/"),
                            new File("${project(":support").getProjectDir()}/cas-server-support-themes-collection/src/main/resources/")
                }
            }
        }
    }


    springBoot {
        mainClassName = project.ext.mainClassName
        buildInfo {
            properties {
                if (!rootProject.generateTimestamps) {
                    excludes = ['time']
                }
            }
        }
    }

    if (plugins.hasPlugin("war")) {
        tasks.named('javadoc') {
            enabled = false
        }

        tasks.named('bootWar') {
            def propertiesFile = project.layout.buildDirectory.file("resources/main/git.properties").get().asFile
            def properties = new Properties()
            if (propertiesFile.exists()) {
                propertiesFile.withInputStream { stream ->
                    properties.load(stream)
                }
            }
            def projectSpecVersion = properties.getProperty('git.commit.id', 'N/A')

            mainClass.set(project.ext.mainClassName)
            manifest {
                attributes(
                        "Automatic-Module-Name": project.name.replace("-", "."),
                        "Implementation-Title": project.name,
                        "Implementation-Vendor": project.group,
                        "Created-By": project.group)
                if (rootProject.generateTimestamps) {
                    attributes("Implementation-Date": java.time.ZonedDateTime.now())
                }

                attributes(
                        "Specification-Version": "${-> projectSpecVersion}",
                        "Implementation-Version": project.version)
            }
            from "${project.layout.buildDirectory.getAsFile().get().path}/resources/main", { into "WEB-INF/classes" }
            if (!rootProject.skipBootifulLaunchScript) {
                launchScript()
            }
            duplicatesStrategy = DuplicatesStrategy.EXCLUDE
        }

    } else {
        tasks.named('bootJar') {
            mainClass.set(project.ext.mainClassName)
            if (project.ext.has("archiveFileName")) {
                archiveFileName = project.ext.archiveFileName
            }
            from "${project.layout.buildDirectory.getAsFile().get().path}/resources/main", { into "WEB-INF/classes" }
            if (!rootProject.skipBootifulLaunchScript) {
                launchScript()
            }
            duplicatesStrategy = DuplicatesStrategy.EXCLUDE
        }

        /*
         * 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)
            }
        }
    }

    tasks.named("processBootRunResources") {
        duplicatesStrategy = DuplicatesStrategy.EXCLUDE
    }
    
    tasks.named('bootRun') {
        mainClass.set(project.ext.mainClassName)

        if (Os.isFamily(Os.FAMILY_WINDOWS)) {
            dependsOn pathingJar
            doFirst {
                classpath = files("$project.layout.buildDirectory.getAsFile().get().path/classes/main", "$project.layout.buildDirectory.getAsFile().get().path/resources/main", "${project(":webapp").getProjectDir()}/cas-server-webapp-resources/src/main/resources/", pathingJar.archivePath)
            }
        }

        sourceResources sourceSets.bootRun
        systemProperties = System.properties

        def list = []
        list.add("-Djava.net.preferIPv4Stack=true ")
        list.add("-Djava.net.preferIPv4Addresses=true")
        list.add("-XX:TieredStopAtLevel=1")
        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.util.concurrent=ALL-UNNAMED")
        list.add("--add-opens")
        list.add("java.base/java.util.concurrent.locks=ALL-UNNAMED")
        list.add("--add-opens")
        list.add("java.base/java.util=ALL-UNNAMED")
        list.add("--add-opens")
        list.add("java.base/java.time=ALL-UNNAMED")
        list.add("--add-opens")
        list.add("java.base/java.net=ALL-UNNAMED")
        list.add("--add-opens")
        list.add("java.base/sun.security.action=jdk.crypto.ec")
        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(5000..5010)
            if (port > 0) {
                ansi.green "Port ${port} will be available for remote debugging requests with suspend=${rootProject.remoteDebuggingSuspend}"
                list.add("-Xrunjdwp:transport=dt_socket,address=${port},server=y,suspend=${rootProject.remoteDebuggingSuspend}")
            }
        }

        jvmArgs = list

        def appArgList = []
        appArgList.add("--spring.thymeleaf.cache=false")
        args = appArgList
    }
} 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
}
