import org.jetbrains.kotlin.BuildRegister
import org.jetbrains.kotlin.MPPTools
import org.jetbrains.kotlin.UtilsKt
import org.jetbrains.kotlin.konan.target.DistributionKt
import org.jetbrains.kotlin.konan.target.PlatformManager

buildscript {
    ext.rootBuildDirectory = file('..')

    ext {
        def properties = new java.util.Properties()
        properties.load(new java.io.FileReader(project.file("../../gradle.properties")))
        properties.each { k, v->
            def key = k as String
            set(key, project.findProperty(key) ?: v)
        }
    }
    ext["withoutEmbedabble"] = true
    UtilsKt.kotlinInit(project, findProperty("cacheRedirectorEnabled")?.toString()?.toBoolean())
    ext["bootstrapKotlinRepo"] = BootstrapKt.getBootstrapKotlinRepo(project)
    ext["bootstrapKotlinVersion"] = BootstrapKt.getBootstrapKotlinVersion(project)

    apply from: "$rootBuildDirectory/gradle/loadRootProperties.gradle"

    dependencies {
        classpath("org.jetbrains.kotlin:kotlin-build-gradle-plugin:${kotlinBuildProperties.buildGradlePluginVersion}")
    }
}

plugins {
    id("kotlin.native.build-tools-conventions")
    id("org.jetbrains.kotlin.multiplatform") apply false
}

repositories {
    mavenCentral()
}

def globalProperties = new java.util.Properties()
ext.kotlin_root = project.file("../..").absolutePath
project.logger.info("kotlin_root: $kotlin_root")
globalProperties.load(new java.io.FileReader(project.file("$kotlin_root/gradle.properties")))

def konanDataDir = project.hasProperty('konan.data.dir') ? project.property('konan.data.dir').toString() : null
def platformManager = new PlatformManager(DistributionKt.buildDistribution(UtilsKt.getKotlinNativeDist(project).path, konanDataDir), false)
def kotlinDist = null
if (hasProperty("kotlin_dist")) {
    kotlinDist = file(findProperty("kotlin_dist"))
    ext["notationMapping"] = [':kotlin-stdlib-common' : project.file("${kotlinDist}/kotlinc/lib/kotlin-stdlib.jar").absolutePath,
                              ':kotlin-test'          : project.file("${kotlinDist}/kotlinc/lib/kotlin-test.jar").absolutePath,
                              ':kotlin-stdlib-jdk8'   : project.file("${kotlinDist}/kotlinc/lib/kotlin-stdlib-jdk8.jar").absolutePath]
    ext.targetList = []
}

subprojects { proj ->
    globalProperties.each { k, v->
        def key = k as String
        def value = project.findProperty(key) ?: v
        proj.logger.info("${proj.name}<<<[$key] = $value>>>")
        proj.ext.set(key, value)
    }
    UtilsKt.kotlinInit(proj, findProperty("cacheRedirectorEnabled")?.toString()?.toBoolean() ?: false)
    proj.ext.platformManager = platformManager
    proj.ext["bootstrapKotlinRepo"] = BootstrapKt.getBootstrapKotlinRepo(proj)
    proj.ext["bootstrapKotlinVersion"] = BootstrapKt.getBootstrapKotlinVersion(proj)
    proj.ext["kotlin.native.home"] = UtilsKt.getKotlinNativeDist(project).toString()
    proj.ext["konan.home"] = proj.ext["kotlin.native.home"]
    proj.ext["kotlin.native.enabled"] = true
    proj.logger.info("${proj.name}<<<[kotlin.native.home] = ${proj.ext["kotlin.native.home"]}")
    if (project.hasProperty("kotlin_dist")) {
        proj.ext["kotlin_dist"] = kotlinDist.path
        proj.logger.info("${proj.name}<<<[kotlin_dist] = ${proj.ext["kotlin_dist"]}>>>")
    }

    proj.ext["buildNumber"]  = findProperty("build.number")?.toString() ?: defaultSnapshotVersion
    proj.ext["kotlinVersion"] = findProperty("deployVersion")?.toString()?.identity { deploySnapshotStr ->
                if (deploySnapshotStr != "default.snapshot") deploySnapshotStr else defaultSnapshotVersion
            } ?: proj.ext["buildNumber"]
    proj.logger.info("${proj.name}<<<[kotlinVersion] = ${proj.ext["kotlinVersion"]}>>>")

    proj.repositories {
        mavenCentral()
    }
}

tasks.register("buildAnalyzer") {
    dependsOn(gradle.includedBuild("benchmarksAnalyzer").task(":${getAnalyzerTargetName()}Binaries".toString()))
}

task konanRun {
    subprojects.findAll {
        !it.name.contains("videoplayer")
    }.each {
        dependsOn "${it.path}:konanRun"
    }
}
    
task jvmRun {
    doFirst {
        if (findProperty("kotlin_dist") == null) {
            throw new StopExecutionException("Please specify location of kotlin's dist in 'kotlin_dist' property")
        }
    }
    subprojects.each {
        if (it.name != "endorsedLibraries" && it.name != "kotlinx.cli") {
            dependsOn "${it.path}:jvmRun"
        }
    }
}

task clean {
    subprojects.each {
        if (it.name != "endorsedLibraries") {
            dependsOn "${it.path}:clean"
        }
    }
    doLast {
        delete(layout.buildDirectory)
    }
}

defaultTasks 'konanRun'

private static String getAnalyzerTargetName() {
    // Copy-pasted from tools/benchmarksAnalyzer/build.gradle.
    String target = System.getProperty("os.name")
    if (target == 'Linux') return 'linux'
    if (target.startsWith('Windows')) return 'windows'
    if (target.startsWith('Mac'))
        if (System.getProperty("os.arch") == "aarch64")
            return 'macosArm64'
        else
            return 'macosX64'
    return 'unknown'
}

private String findAnalyzerBinary() {
    String result = "${projectDir}/../${analyzerToolDirectory}/${getAnalyzerTargetName()}/" +
            "${analyzerTool}ReleaseExecutable/${analyzerTool}${MPPTools.getNativeProgramExtension()}"
}

private String getUploadedFile(String fileName) {
    def teamcityConfig = System.getenv("TEAMCITY_BUILD_PROPERTIES_FILE")
    if (teamcityConfig) {
        def buildProperties = new Properties()
        buildProperties.load(new FileInputStream(teamcityConfig))
        def buildNumber = buildProperties.getProperty("build.number")
        def target = System.getProperty("os.name").replaceAll("\\s", "")
        return "${target}/${buildNumber}/${fileName}"
    }
    return null
}

private def uploadBenchmarkResultToArtifactory(String fileName) {
    def teamcityConfig = System.getenv("TEAMCITY_BUILD_PROPERTIES_FILE")
    if (teamcityConfig) {
        def uploadedFile = getUploadedFile(fileName)
        def buildProperties = new Properties()
        buildProperties.load(new FileInputStream(teamcityConfig))
        def password = buildProperties.getProperty("artifactory.apikey")
        MPPTools.uploadFileToArtifactory("${artifactoryUrl}", "${artifactoryRepo}",
                uploadedFile, layout.buildDirectory.file(fileName).get().asFile.toString(), password)
    }
}

// Register external benchmarks reports (e.g. results of libraries benchmarks)
task registerExternalBenchmarks {
    doLast {
        def reports = externalReports.split(';')
        def jsonReports = []
        reports.each {
            def reportFile = layout.buildDirectory.file(it).get().asFile
            if (!reportFile.exists())
                return

            def lines = reportFile.readLines()
            if (lines.size < 4) {
                project.logger.warn("Couldn't use report to register benchmarks. Wrong format.")
                return
            }
            def name = lines[0]
            def options = lines[1]
            if (!options.startsWith("OPTIONS")) {
                project.logger.warn("Couldn't use report to register benchmarks. Wrong format in options description.")
                return
            }
            def optionsList = options.replace("OPTIONS ", "").replaceAll("\\[|\\]", "").split(",\\s*").toList()
            def status = lines[2]
            def compileTime = null
            def codeSize = null
            lines.drop(3).each {
                if (it.startsWith("COMPILE_TIME")) {
                    compileTime = it
                }
                if (it.startsWith("CODE_SIZE")) {
                    codeSize = it
                }
            }
            // Create benchmarks report.
            def properties = [
                    "cpu": System.getProperty("os.arch"),
                    "os": System.getProperty("os.name"),
                    "jdkVersion": System.getProperty("java.version"),
                    "jdkVendor": System.getProperty("java.vendor"),
                    "kotlinVersion": kotlinVersion,
                    "type": "native",
                    "compilerVersion": kotlinVersion,
                    "flags": optionsList,
                    "benchmarks": "[]"]
            if (compileTime != null) {
                def compileBenchmark = MPPTools.toCompileBenchmark(compileTime, status, name)
                properties += ["compileTime": [compileBenchmark]]
            }
            if (codeSize != null) {
                properties += ["codeSize": MPPTools.toCodeSizeBenchmark(codeSize, status, name)]
            }
            def output = MPPTools.createJsonReport(properties)
            def jsonFile = layout.buildDirectory.file(it.replace(".txt", ".json")).get().asFile
            jsonFile.write(output)
            jsonReports.add(jsonFile)
        }
        def merged = MPPTools.mergeReports(jsonReports)
        if (!merged.isEmpty()) {
            mkdir layout.buildDirectory.get().asFile.absolutePath
            layout.buildDirectory.file(externalBenchmarksReport).get().asFile.write(merged)
            uploadBenchmarkResultToArtifactory(externalBenchmarksReport)
        }
    }
}

tasks.register("registerBuild", BuildRegister) {
    onlyBranch = project.findProperty('kotlin.register.branch')
    def uploadedFile = getUploadedFile(nativeJson)
    if (uploadedFile != null) {
        println("Use file from Artifacrory $uploadedFile")
        fileWithResult = uploadedFile
    }
    // Get bundle size.
    bundleSize = null
    if (project.findProperty('kotlin.bundleBuild') != null) {
        def dist = findProperty('kotlin.native.home') ?: 'dist'
        dist = (new File(dist)).isAbsolute() ? dist : "${project.getProjectDir()}/$dist"
        bundleSize = (new File(dist)).directorySize()
    }
    buildNumberSuffix = project.findProperty('buildNumberSuffix')
}

tasks.register("registerExternalBuild", BuildRegister) {
    onlyBranch = project.findProperty('kotlin.register.branch')
    def uploadedFile = getUploadedFile(externalBenchmarksReport)
    if (uploadedFile != null) {
        println("Use file from Artifacrory $uploadedFile")
        fileWithResult = uploadedFile
    } else {
        fileWithResult = externalBenchmarksReport
    }

}

registerExternalBenchmarks.finalizedBy registerExternalBuild

def mergeReports(String fileName) {
    def reports = []
    subprojects.each {
        def reportFile = it.layout.buildDirectory.file(fileName).get().asFile
        if (reportFile.exists()) {
            reports.add(reportFile)
        }
    }
    def output = MPPTools.mergeReports(reports)
    mkdir layout.buildDirectory.get().asFile.absolutePath
    new File("${layout.buildDirectory.get().asFile.absolutePath}/${fileName}").write(output)
}

task mergeNativeReports {
    doLast {
        mergeReports(nativeJson)
        uploadBenchmarkResultToArtifactory(nativeJson)
    }
}

task mergeJvmReports {
    doLast {
        mergeReports(jvmJson)
        uploadBenchmarkResultToArtifactory(jvmJson)
    }
}

subprojects.each {
    if (it.name != "endorsedLibraries" && it.name != "kotlinx.cli") {
        it.afterEvaluate {
            it.jvmJsonReport.finalizedBy mergeJvmReports
            it.konanJsonReport.finalizedBy mergeNativeReports
        }
    }
}

task teamCityStat(type:Exec) {
    def analyzer = findAnalyzerBinary()
    if (getAnalyzerTargetName() == 'windows') {
        def mingwPath = new File(System.getenv("MINGW64_DIR") ?: "C:/msys64/mingw64")
        def pathEnv = getEnvironment()["PATH"]
        environment("PATH", "${mingwPath.toString()}/bin" + (pathEnv ? ";$pathEnv" : ""))
    }
    commandLine analyzer, "-r", "teamcity",
        "--artifactory-url", "https://repo.labs.intellij.net/kotlin-native-benchmarks",
        "--teamcity-url", "http://buildserver.labs.intellij.net",
        "--server-url", findProperty("kotlin.native.performance.server.url")?.toString() ?: "http://localhost:3000",
        "${layout.buildDirectory.file(nativeJson).get().asFile}"
}

task cinterop {
    dependsOn 'clean'
    dependsOn 'cinterop:konanRun'
}

task framework {
    dependsOn 'clean'
    dependsOn 'framework:konanRun'
}

task helloworld {
    dependsOn 'clean'
    dependsOn 'helloworld:konanRun'
}

task objcinterop {
    dependsOn 'clean'
    dependsOn 'objcinterop:konanRun'
}

task ring {
    dependsOn 'clean'
    dependsOn 'ring:konanRun'
}

task numerical {
    dependsOn 'clean'
    dependsOn 'numerical:konanRun'
}

task startup {
    dependsOn 'clean'
    dependsOn 'startup:konanRun'
}

task swiftinterop {
    dependsOn 'clean'
    dependsOn 'swiftinterop:konanRun'
}

task videoplayer {
    dependsOn 'clean'
    dependsOn 'videoplayer:konanRun'
}

task KotlinVsSwift {
    dependsOn 'clean'
    dependsOn 'KotlinVsSwift:konanRun'
}
