plugins {
  id 'base'
  id 'java'
  id 'com.github.node-gradle.node' version '7.1.0'
}

tasks.build.dependsOn 'assemble'

node {
  version = '20.9.0'
  download = true
}

allprojects {
  apply plugin: 'java'

  repositories {
    mavenCentral()
  }

  project.ext.distVersion = { ->
    def json = new groovy.json.JsonSlurper()
    def packageData = json.parse(file("${rootProject.projectDir}/package.json"))
    return packageData.version;
  }

  project.ext.antvilleBuildDir = "${rootProject.buildDir}/tmp/antville"
  project.ext.antvilleInstallDir = "${rootProject.buildDir}/install/antville"

  project.ext.antvilleDistFiles = copySpec {
    from fileTree(antvilleBuildDir).matching {}
  }

  // Hide some purely Java-related tasks
  project.tasks.buildDependents.group = null;
  project.tasks.buildNeeded.group = null;
  project.tasks.classes.group = null;
  project.tasks.jar.group = null;
  project.tasks.javadoc.group = null;
  project.tasks.testClasses.group = null;
}

version = distVersion()

configurations {
  lessCss
}

dependencies {
  implementation 'org.commonmark:commonmark:0.24.0'
  implementation 'org.commonmark:commonmark-ext-autolink:0.24.0'
  implementation 'org.commonmark:commonmark-ext-gfm-strikethrough:0.24.0'
  implementation 'org.commonmark:commonmark-ext-gfm-tables:0.24.0'
  implementation 'org.jsoup:jsoup:1.19.1'
  implementation 'rome:rome:1.0'

  lessCss('org.lesscss:lesscss:1.7.0.1.1') {
    exclude group: 'org.mozilla', module: 'rhino'
    exclude group: 'org.slf4j', module: 'slf4j-api'
    exclude group: 'org.slf4j', module: 'slf4j-simple'
  }
}

assemble {
  dependsOn 'installAntville'
  dependsOn 'installJars'
  dependsOn 'buildStaticFiles'

  doFirst {
    copy {
      from fileTree(antvilleBuildDir).matching {
        exclude 'node_modules'
        exclude 'package.json'
        exclude 'tests'
        exclude 'tools/client'
        exclude 'tools/antclick'
        exclude 'package-lock.json'
      } into antvilleInstallDir
    }
  }
}

tasks.register('installAntville') {
  description 'Clone the Antville repository and remove all unnecessary files.'
  group 'installation'

  outputs.dirs antvilleBuildDir

  doLast {
    def date = new Date().format('d MMM yyyy')
    def gitOutput = new ByteArrayOutputStream()

    exec {
      commandLine 'git', 'rev-parse', '--short', 'HEAD'
      standardOutput = gitOutput
      errorOutput = new ByteArrayOutputStream()
      ignoreExitValue = true
    }

    def tag = gitOutput.toString().trim()

    copy {
      from "${project.projectDir}/code/app.properties"
      into "$antvilleBuildDir/code"
      filter { line -> line.replaceAll('(version =) 0.0.0', "\$1 $version.$tag")
        .replaceAll('(buildDate =) 18 Jun 2001', "\$1 $date")
      }
    }

    copy {
      from fileTree(project.projectDir).matching {
        include 'claustra/**'
        include 'code/**'
        exclude 'code/app.properties'
        include 'compat/**'
        include 'db/**'
        include 'i18n/**'
        exclude 'i18n/*.po*'
        include 'static/**'
        include 'tools/**'
        include 'CHANGES.md'
        include 'INSTALL.md'
        include 'README.md'
      } into antvilleBuildDir
    }
  }
}

tasks.register('installJars', Copy) {
  description 'Download required JAR libraries.'
  group 'installation'
  dependsOn 'installAntville'

  def outputDir = "$antvilleBuildDir/lib"

  outputs.dir outputDir

  from configurations.runtimeClasspath
  into outputDir
}

tasks.register('installNodeModules', NpmTask) {
  description 'Download required Node modules.'
  group 'build'
  dependsOn 'installAntville'

  inputs.files "${project.projectDir}/package-lock.json"
  outputs.dir "${project.projectDir}/node_modules"

  args = ['--silent', 'install']

  execOverrides {
    it.workingDir = antvilleBuildDir
  }
}

tasks.register('buildStaticFiles', Copy) {
  description 'Build fonts, client-side scripts and stylesheets.'
  group 'build'

  dependsOn 'installAntville'
  dependsOn 'installNodeModules'
  dependsOn 'buildMainScript'
  dependsOn 'buildMainStyles'
  dependsOn 'buildEditorScript'
  dependsOn 'buildEditorStyles'
  dependsOn 'buildGalleryScript'
  dependsOn 'buildLicenses'

  def inputDir = "${project.projectDir}/node_modules/uikit/dist/fonts"
  def outputDir = "$antvilleBuildDir/static/fonts"

  inputs.dir inputDir
  outputs.dir outputDir

  from inputDir
  into outputDir
}

['main', 'editor', 'gallery'].each { name ->
  tasks.register("build${name.capitalize()}Script", NpmTask) {
    description "Build the ${name} client-side scripts."
    group 'build'
    dependsOn 'installNodeModules'

    def inputFile = "${project.projectDir}/tools/client/${name}.js"
    def outputFile = "$antvilleBuildDir/static/scripts/${name}.min.js"

    inputs.files inputFile
    outputs.files outputFile

    args = [
      '--silent',
      'exec', '--',
      'browserify', inputFile,
      '--outfile', outputFile,
      '-g', 'uglifyify'
    ]
  }
}

['main', 'editor'].each { name ->
  tasks.register("build${name.capitalize()}Styles", NpmTask) {
    description "Build the ${name} stylesheet."
    group 'build'
    dependsOn 'installNodeModules'

    def inputFile = "${project.projectDir}/tools/client/${name}.less"
    def outputFile = "$antvilleBuildDir/static/styles/${name}.min.css"

    inputs.files inputFile
    outputs.files outputFile

    args = [
      '--silent',
      'exec', '--',
      'lessc',
      '--clean-css',
      inputFile,
      outputFile
    ]
  }
}

tasks.register('buildLicenses', NpmTask) {
  description 'Build licenses file from client-side dependencies.'
  group 'build'
  dependsOn 'installNodeModules'

  def outputFile = "$antvilleBuildDir/static/licenses.txt"

  inputs.file "${project.projectDir}/package-lock.json"
  outputs.file outputFile

  args = [
    '--silent',
    'exec', '--',
    'generate-license-file',
    '--input', "${project.projectDir}/package.json",
    '--output', outputFile,
    '--overwrite',
    '--eol', 'lf'
  ]

  execOverrides {
    it.workingDir = antvilleBuildDir
    it.standardOutput = new FileOutputStream(outputFile)
  }
}

tasks.register('jsdoc', NpmTask) {
  description 'Generates JavaScript API documentation for the main source code.'
  group 'documentation'
  dependsOn 'installNodeModules'

  def inputDir = "$antvilleBuildDir/code"
  def outputDir = "${project.buildDir}/docs/jsdoc"

  inputs.dir inputDir
  outputs.dir outputDir

  args = [
    '--silent',
    'exec', '--',
    'jsdoc',
    '--recurse',
    '--destination',
    outputDir,
    inputDir
  ]

  execOverrides {
    it.workingDir = antvilleBuildDir
  }
}

tasks.register('assembleDist') {
  description 'Creates the Antville download packages.'
  group 'distribution'

  dependsOn 'assemble'
  dependsOn 'distZip'
  dependsOn 'distTar'
}

tasks.register('distZip', Zip) {
  description 'Creates the Antville download package as Zip file.'
  group 'distribution'
  dependsOn 'assemble'

  def version = project.distVersion()
  def outputDir = "${project.buildDir}/distributions"
  def outputFile = "antville-${version}.zip"

  inputs.dir antvilleInstallDir
  outputs.file "$outputDir/$outputFile"

  from (antvilleInstallDir) {
    into "antville-${version}"
  }

  destinationDirectory = file(outputDir)
  archiveFileName = outputFile
}

tasks.register('distTar', Tar) {
  description 'Creates the Antville download package as compressed archive file.'
  group 'distribution'
  dependsOn 'assemble'

  def version = project.distVersion()
  def outputDir = "${project.buildDir}/distributions"
  def outputFile = "antville-${version}.tgz"

  inputs.dir antvilleInstallDir
  outputs.file "$outputDir/$outputFile"

  from (antvilleInstallDir) {
    into "antville-${version}"
  }

  compression = Compression.GZIP
  destinationDirectory = file(outputDir)
  archiveFileName = outputFile
}

// Task to patch the lesscss JAR file
// This task modifies the lessc-rhino-1.7.0.js file in the JAR to comment out an annoying console.log statement (“done”)
def patchLessCssJar = tasks.register('patchLessCssJar') {
  def name = 'lesscss-1.7.0.1.1'
  def targetFile = 'META-INF/lessc-rhino-1.7.0.js' // File to patch
  def tempDir = file("${buildDir}/${name}") // Temporary directory for extraction
  def patchedJar = file("${buildDir}/${name}-patched.jar") // Output patched JAR

  outputs.file patchedJar

  doLast {
    if (tempDir.exists()) {
      tempDir.deleteDir()
    }
    tempDir.mkdirs()

    def jarFile = configurations.lessCss.find {
      it.name.contains(name)
    }

    println "Patching $jarFile…"
    ant.unzip(src: jarFile, dest: tempDir)

    def targetFilePath = new File(tempDir, targetFile)
    def content = targetFilePath.text
    content = content.replace('console.log("done")', '// console.log("done")')
    targetFilePath.text = content

    ant.zip(destfile: patchedJar) {
      fileset(dir: tempDir)
    }
  }
}

// Ensure the patchLessCssJar task runs before the classes task
tasks.named('build').configure {
  dependsOn patchLessCssJar
}

// Exclude the original lesscss JAR from the runtime classpath
// and include the patched JAR instead
configurations.runtimeClasspath {
  exclude group: 'org.lesscss', module: 'lesscss'

  dependencies {
    runtimeOnly(patchLessCssJar.map { it.outputs.files })
  }
}
