import de.aaschmid.gradle.plugins.cpd.Cpd
import groovy.json.JsonOutput
import io.miret.etienne.gradle.sass.CompileSass
import org.apache.tools.ant.taskdefs.condition.Os
import org.docstr.gradle.plugins.gwt.AbstractGwtActionTask
import org.docstr.gradle.plugins.gwt.GwtDev
import org.geogebra.gradle.ProjectExtensionsKt

plugins {
    alias libs.plugins.geogebra.java
    alias libs.plugins.geogebra.gwt
    alias libs.plugins.geogebra.pmd
    alias libs.plugins.geogebra.checkstyle
    alias libs.plugins.geogebra.spotbugs
    alias libs.plugins.geogebra.webjars
    alias libs.plugins.geogebra.sourcesets
    alias libs.plugins.geogebra.javadoc.workaround
    alias libs.plugins.geogebra.app.specs
    alias libs.plugins.cpd
    alias libs.plugins.sass
    alias libs.plugins.geogebra.version apply false
}

description 'Parts of GeoGebra related to web platforms'

compileJava.options.sourcepath = files(processResources.destinationDir).builtBy(processResources)

dependencies {
    implementation project(':carota-web')
    implementation project(':web-dev')
    implementation project(':web-common')
    implementation project(':editor-web')
    implementation project(':keyboard-web')
    implementation libs.pdfJs
    implementation libs.multiplayer
    implementation files(file("build/generated/sources/annotationProcessor/java/main/"))

    testImplementation libs.junit
    testImplementation libs.gwt.user
    testImplementation libs.gwt.mockito

    annotationProcessor project(":gwt-generator")
    annotationProcessor libs.gwt.resources.processor
}

def devModule = project.findProperty("gmodule") ?: 'org.geogebra.web.SuperWeb'

compileSass {
    // Directory where to output generated CSS:
    outputDir = file("${projectDir}/war/css")

    // Source directory containing sass to compile:
    sourceDir = file("${projectDir}/src/main/resources/scss")

    // Set the output style:
    // Possible values are “expanded” and “compressed”, default is “expanded”.
    style = compressed

    // Source map style:
    //  - file: output source map in a separate file (default)
    //  - embed: embed source map in CSS
    //  - none: do not emit source map.
    sourceMap = none
}

tasks.register('watchSass', CompileSass) {
    outputDir = file("${projectDir}/war/css")
    sourceDir = file("${projectDir}/src/main/resources/scss")
    style = compressed
    sourceMap = embed

    // Watch sass files in sourceDir for changes
    // (Default is to not to watch, compile once and terminate)
    watch()
}

def warDirRel = 'war'

gwt {
    if (project.hasProperty("gmodule")) {
        modules = project.getProperty("gmodule").split(",")
    } else {
        modules = ['org.geogebra.web.SuperWeb', 'org.geogebra.web.WebSimple', 'org.geogebra.web.Web',
                   'org.geogebra.web.Tablet3D']
    }

    devModules devModule
    sourceLevel = "11"

    dev {
        bindAddress = project.findProperty("gbind") ?: 'localhost'
        superDevMode = true
    }
}

tasks.withType(AbstractGwtActionTask).configureEach {
    jvmArgs '-Xss512M'
    useToolchain = true
}


def outputs = ['web', 'web3d', 'webSimple', 'tablet3d', 'tabletWin']

tasks.register('cleanWar', Delete) {
    doLast {
        outputs.each { folder ->
            delete warDirRel + "/$folder"
        }
    }
}

apply from: "templates.gradle"

def createHtml = { inlineCss ->
    delete {
        delete fileTree(dir: 'war', include: "*.html")
    }

    copy {
        from file("../common/src/main/resources/giac")
        into warDirRel
    }
    copy {
        from file("src/main/resources/org/geogebra/web/resources/war")
        into warDirRel
    }

    appSpecs.forEach { app ->
        def replaceHtml = app.id == "classic" ? classicHtml : appHtml
        file("war/${app.id}-offline.html").text = english(replaceHtml(app, false, true, inlineCss), app.title, "")
        file("war/${app.id}.html").text = english(replaceHtml(app, true, false, inlineCss), app.title, "en")
        if (app.vendor == "ByCS") {
            file("war/${app.id}-template.html").text = replaceHtml(app, false).replace("<!--CODEBASE-->", "/")
        }
    }
    file("war/calculator.html").text = file("war/suite.html").text
    file("war/notes-murokdev.html").text = file("war/notes.html").text.replace('<!--LANGUAGE LINKS-->',
            '<script src="http://localhost:8080/murok.js"></script><script src="http://localhost:8080/canvas2pdf.js"></script><script src="http://localhost:8080/canvas-to-svg.umd.js"></script>')
}

tasks.register('copyHtml') {
    dependsOn 'compileSass'
    description 'Generate app.html, app-release.html, graphing.html and others'
    group 'application'

    doLast {
        createHtml(true)
    }
}

tasks.register('copyDevHtml') {
    dependsOn 'compileSass'
    description 'Generate app.html, app-release.html, graphing.html and others without inlined css'
    group 'application'

    doLast {
        createHtml(false)
    }
}

tasks.register('copyHandlebars') {
    dependsOn 'compileSass'
    doLast {
        def handlebarsDir = project.getProperty("handlebarsDir")
        appSpecs.forEach { app ->
            def replaceHtml = app.id == "classic" ? classicHtml : appHtml

            def templatePath = "${handlebarsDir}/${app.id}-template.handlebars"
            file(templatePath).text = handlebars(replaceHtml(app, false, false, true))
        }
    }
}

tasks.register('prepareS3Upload') {
    dependsOn('compileGwt', 'copyHtml')
    doLast {
        ["web3d", "webSimple", "editor"].each { module ->
            def dir = file("build/gwt/out/$module")
            if (dir.exists()) {
                fileTree(dir).each { f ->
                    def outFile = file("$f".replace(dir.absolutePath, "build/s3/$module/"))
                    file(outFile.parent).mkdirs()
                    ant.gzip(src: f, zipfile: outFile)
                }
            }
            def srcMaps = file("build/gwt/out/WEB-INF/deploy/$module/symbolMaps/")
            file("build/symbolMapsGz/$module").mkdirs()
            if (srcMaps.exists()) {
                fileTree(srcMaps).each { f ->
                    def outFile = file("$f".replace(srcMaps.absolutePath, "build/symbolMapsGz/$module/"))
                    ant.gzip(src: f, zipfile: outFile)
                }
            }
        }
    }
}

tasks.register('deployIntoWar', Copy) {
    dependsOn('cleanWar', 'copyHtml')
    description 'Copies/updates the GWT production compilation directory (web3d, ...) to the war directory.'
    from compileGwt
    into warDirRel

    into("web3d/sourcemaps/") {
        from "$warDirRel/WEB-INF/deploy/web3d/symbolMaps"
    }

}

tasks.register('mergeDeploy') {
    description 'merge deploy.js and web3d.nocache.js'
    mustRunAfter 'compileGwt'
    doLast {
        def firstFolder = { files -> files.listFiles() ? files.listFiles()[0].name : "" };
        def web3dPermutation = firstFolder(file("./build/gwt/out/web3d/deferredjs"))
        def webSimplePermutation = firstFolder(file("./build/gwt/out/webSimple/deferredjs"))
        def templateJs = "src/main/resources/org/geogebra/web/resources/js/deployggb-template.js"
        def deployText = file(templateJs).text.replace('%WEB3D_PERMUTATION%', web3dPermutation)
                .replace('%WEBSIMPLE_PERMUTATION%', webSimplePermutation)
        def currentVersion = ProjectExtensionsKt.getCommonVersion(project)
        def localPath = project.findProperty("deployggbRoot") ?: './'
        file(warDirRel + "/deployggb.js").text = deployText.replace('%MODULE_BASE%', localPath)
        file(warDirRel + "/deployggb-latest.js").text = deployText.replace('%MODULE_BASE%', 'https://www.geogebra.org/apps/latest/')
        file(warDirRel + "/deployggb-${currentVersion}.js").text = deployText.replace('%MODULE_BASE%', "https://www.geogebra.org/apps/${currentVersion}/")
    }
}

def gwtGlobalCache = file('build/gwt/devModeCache')

class ReadableDev extends GwtDev {
    protected void addArgs() {
        super.addArgs();
        args("-style", "PRETTY");
    }
}

tasks.register('run', ReadableDev) {
    dependsOn('jar', 'copyDevHtml')
    war = file(warDirRel)
    maxHeapSize = '4096m'
    logLevel = 'TRACE'
    cacheDir = gwtGlobalCache
    extraJvmArgs = ["-Djava.io.tmpdir=${gwtGlobalCache.absolutePath}"]
    description 'Starts a codeserver, and a simple webserver for development'
    Process sassCompilation
    doFirst {
        delete {
            delete gwtGlobalCache
        }
        gwtGlobalCache.mkdirs()
        ProcessBuilder pb = new ProcessBuilder(Os.isFamily(Os.FAMILY_WINDOWS)
                ? ['cmd', '/c', 'START', '/MIN', file('..\\..\\..\\gradlew.bat').absolutePath, 'watchSass']
                : [file('../../../gradlew').absolutePath, 'watchSass'])
        pb.directory(file("."))
        sassCompilation = pb.start()
    }
    doLast {
        sassCompilation.destroy()
    }
}

tasks.register('createDraftBundleZip', Zip) {
    dependsOn 'compileSass'
    description 'Creates the Math Apps Bundle file.'
    destinationDirectory = file(warDirRel)
    archiveBaseName = "geogebra-bundle"
    from compileGwt {
        include("web3d/**")
        into "GeoGebra/HTML5/5.0"
    }
    from("war/css") {
        include "**"
        into "css"
    }
}

tasks.register('minifyLibs') {
    doLast {
        def libDir = "src/main/resources/org/geogebra/web/"
        def libDirCommon = "../web-common/$libDir"
        def libs = ["$libDirCommon/resources/js/rewrite_pHYs", "$libDir/pub/js/canvas2pdf", "$libDir/pub/js/whammy"]
        libs.each({ lib ->
            def command = ['npx', 'terser', "${lib}.js", "-o", "${lib}.min.js",
                           '--compress', '--mangle', '--comments', '/license/']
            exec { commandLine Os.isFamily(Os.FAMILY_WINDOWS) ? ['cmd', '/c'] + command : command }
        })
    }
}

tasks.register('declareAppSpecs') {
    doLast {
        def file = layout.buildDirectory.file("objects/appSpecs").get().asFile
        file.parentFile.mkdirs()
        def fos = new FileOutputStream(file)
        def oos = new ObjectOutputStream(fos);
        oos.writeObject(appSpecs)
        oos.close()
        fos.close()
    }
}

tasks.register('collectFaIcons') {
    description 'Collect all parameters of the instantiations of the FaIconSpec class'
    doLast {
        def srcDirs = [file('src/main/java'), file('../web-common/src/main/java')]
        def pattern = /new\s+FaIconSpec\((['"])(.*?)\1\)/
        def parameters = []

        srcDirs.each { srcDir ->
            srcDir.eachFileRecurse { file ->
                if (file.name.endsWith('.java')) {
                    def content = file.text
                    def matcher = content =~ pattern
                    matcher.each { match ->
                        def param = match[2] as String
                        if (param.startsWith("fa-")) {
                            param = param.substring(3)
                        }
                        parameters << param
                    }
                }
            }
        }

        if (parameters.isEmpty()) {
            logger.error("No instances for FaIconSpec found!")
        } else {
            def styles = ["light", "regular", "solid", "thin"]
            def file = new File("$projectDir/build/fontAwesomeIcons.txt")
            file.text = JsonOutput.prettyPrint(JsonOutput.toJson(["${styles[0]}":parameters]).toString())
            logger.lifecycle("File \"fontAwesomeIcons.txt\" created at: ${file.absolutePath}")
        }
    }
}

test {
    ignoreFailures = System.getenv("CI") != null
    maxHeapSize = "1024m"
}

cpd {
    ignoreFailures = System.getenv("CI") != null
    minimumTokenCount = 100
}

tasks.withType(Cpd).configureEach {
    exclude "**/*.html"
    exclude "**/*.jj"
    exclude "**/*.xml"
    exclude "**/*.svg"
}
