plugins {
  id 'application'
  id 'com.github.jk1.dependency-license-report' version '2.5'
}

import org.apache.tools.ant.filters.FixCrLfFilter

def jettyLogLevel = '-Dorg.eclipse.jetty.LEVEL=WARN'

// Suppress menu bar and default icon being shown in macos dock (Radar #5754483)
// See https://developer.apple.com/library/content/releasenotes/Java/JavaLeopardUpdate1RN/ResolvedIssues/ResolvedIssues.html
def suppressMacosDockIcon = '-Dapple.awt.UIElement=true'

// This list is used to determine which files need processing of line endings
def textFiles = ['**/*.hac', '**/.html', '**/*.js', '**/*.md', '**/*.properties', '**/*.skin', '**/*.txt', '**/*.xml']

allprojects {
  apply plugin: 'java'

  compileJava {
    sourceCompatibility = JavaVersion.VERSION_11
    targetCompatibility = JavaVersion.VERSION_11
  }

  repositories {
    mavenCentral()
  }
}

version = new Date().format("yyyyMMdd")

tasks.build.dependsOn javadoc, 'jsdoc', 'generateLicenseReport'
tasks.compileJava.dependsOn 'processSource'

// Disable DocLint for now
// See <https://blog.joda.org/2014/02/turning-off-doclint-in-jdk-8-javadoc.html>
if (JavaVersion.current().isJava8Compatible()) {
  allprojects {
    tasks.withType(Javadoc) {
      options.addStringOption('Xdoclint:none', '-quiet')
    }
  }
}

sourceSets {
  main {
    java {
      // Sources in `src` will be available here after processing
      srcDirs = ["$buildDir/src/main/java"]
    }
  }
}

configurations {
  // Wrapping implementation because it does not allow access to its files
  // (i.e. cannot be resolved)
  library.extendsFrom implementation
}

dependencies {
  implementation 'com.google.code.gson:gson:2.10.1'
  implementation 'commons-codec:commons-codec:1.15'
  implementation 'commons-fileupload:commons-fileupload:1.5'
  implementation 'commons-logging:commons-logging:1.2'
  implementation 'commons-net:commons-net:3.9.0'
  implementation 'com.sun.mail:javax.mail:1.6.2'
  implementation 'javax.servlet:javax.servlet-api:4.0.1'
  implementation 'org.ccil.cowan.tagsoup:tagsoup:1.2.1'
  implementation 'org.eclipse.jetty:jetty-servlet:9.4.51.v20230217'
  implementation 'org.eclipse.jetty:jetty-xml:9.4.51.v20230217'
  implementation 'org.mozilla:rhino:1.7.13'
  implementation 'org.sejda.imageio:webp-imageio:0.1.6'
  implementation 'xerces:xercesImpl:2.12.2'
  implementation 'xmlrpc:xmlrpc:2.0.1'
}

def rhinoJar = configurations.library.files.find { jar ->
  jar.name.startsWith('rhino')
}

startScripts {
  applicationName = 'helma'
  classpath = files('../launcher.jar')
  mainClass = 'helma.main.launcher.Main'

  defaultJvmOpts = [jettyLogLevel, suppressMacosDockIcon]

  doLast {
    // Work-around to make the classpath above work (launcher.jar is located outside of `lib` dir)
    // See https://discuss.gradle.org/t/classpath-in-application-plugin-is-building-always-relative-to-app-home-lib-directory/2012
    def unixScriptFile = file getUnixScript()
    def windowsScriptFile = file getWindowsScript()
    unixScriptFile.text = unixScriptFile.text.replace('$APP_HOME/lib', '$APP_HOME')
    windowsScriptFile.text = windowsScriptFile.text.replace('%APP_HOME%\\lib', '%APP_HOME%')
  }
}

distributions {
  main {
    contents {
      from project(':launcher').jar
    }
  }
}

application {
  applicationDistribution.from(projectDir) {
    include 'modules/**'
    include 'LICENSE.md'
    include 'README.md'
    include 'start.*'
  }

  applicationDistribution.from(javadoc.destinationDir) {
    include '**'
    into 'docs/javadoc'
  }

  applicationDistribution.from("${project.buildDir}/docs/jsdoc") {
    include '**'
    into 'docs/jsdoc'
  }

  applicationDistribution.from("${project.buildDir}/reports/dependency-license") {
    include '**'
    into 'licenses'
  }
}

distTar {
  dependsOn ':generateLicenseReport', ':javadoc', ':jsdoc'
  compression = Compression.GZIP

  filesMatching(textFiles) {
    filter(FixCrLfFilter.class, eol: FixCrLfFilter.CrLf.newInstance("lf"))
  }
}

distZip {
  dependsOn ':generateLicenseReport', ':javadoc', ':jsdoc'

  filesMatching(textFiles) {
    filter(FixCrLfFilter.class, eol: FixCrLfFilter.CrLf.newInstance("crlf"))
  }
}

installDist {
  dependsOn build
  finalizedBy 'update'
}

run {
  classpath = files('launcher.jar')
  jvmArgs jettyLogLevel, suppressMacosDockIcon
}

task processSource(type: Sync) {
  def date = new Date().format("MMMM dd, yyyy")
  def gitOutput = new ByteArrayOutputStream()

  exec {
    commandLine 'git', 'describe'
    standardOutput = gitOutput
    errorOutput = new ByteArrayOutputStream()
    ignoreExitValue = true
  }

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

  // TODO: Implement extended description in Java code
  if (tag) description = "$tag; $description"

  from 'src'

  filter {
    line -> line.replaceAll('__builddate__', date)
  } into "${project.buildDir}/src"
}

task update {
  def rsyncArgs = ['--archive', '--exclude', 'backups']

  def confirm = {
    ant.input(message: 'Update this installation?', validargs: 'y,n', addproperty: 'continue')
    return ant.continue == 'y'
  }

  onlyIf { confirm() }

  doFirst {
    def backupDir = 'backups/' + new Date().format('yyyyMMdd-HHmmss')

    mkdir backupDir

    exec {
      // Using rsync instead of a CopyTask because the latter chokes on multi-byte characters
      // See https://github.com/gradle/gradle/issues/789
      executable 'rsync'
      args rsyncArgs
      args "$projectDir/", backupDir
    }
  }

  doLast {
    exec {
      // Using rsync instead of installDist task because it does not overwrite the project directory
      executable 'rsync'
      args rsyncArgs
      args '--exclude', 'bin'
      args "${installDist.destinationDir}/", projectDir
    }
  }
}

task jsdoc(type: Exec) {
  description 'Generates JSDoc API documentation for the included JavaScript modules.'
  group 'Documentation'

  def sources = ['modules/core', 'modules/helma', 'modules/jala/code']
  def destination = "${project.buildDir}/docs/jsdoc"

  sources.each { dir -> inputs.dir dir }
  outputs.dir destination

  executable 'npx'
  args = ['jsdoc', '-d', "$destination"].plus(sources)

  // As of writing jsdoc throws the following error:
  // Unable to parse /home/tobi/Projects/helma/current/modules/jala/code/Database.js: Unexpected token, expected "(" (844:10)
  // Seems to be related to the `for each` syntax but did not find any further references.
  ignoreExitValue true
}

task xgettext(type: JavaExec) {
  description 'Extracts translatable message strings from source code.'
  group 'i18n'

  classpath = files('launcher.jar')
  mainClass = 'helma.main.launcher.Commandline'

  // TODO: Decouple from Antville app
  args = [
    // Root.extractMessages is currently located in antville/code/Global/i18n.js
    'antville.extractMessages',
    'modules/jala/util/HopKit/scripts/MessageParser.js',
    'code compat',
    'apps/antville/i18n/antville.pot'
  ]
}

task po2js(type: JavaExec) {
  description 'Converts translated message strings from PO format to JavaScript.'
  group 'i18n'

  classpath = files(rhinoJar)
  mainClass = 'org.mozilla.javascript.tools.shell.Main'

  // TODO: Decouple from Antville app
  args = [
    'modules/jala/util/HopKit/scripts/PoParser.js',
    'apps/antville/i18n',
    'apps/antville/i18n'
  ]
}

task rhinoShell(type: JavaExec) {
  description 'Runs the interactive Rhino JavaScript shell.'
  group 'Application'

  classpath = files(rhinoJar)
  mainClass = 'org.mozilla.javascript.tools.shell.Main'

  standardInput = System.in
}

// Call this task with a function definition using the `-P` parameter, e.g.
// `./gradlew commandLine -Pfunction=manage.getAllApplications`
task commandLine(type: JavaExec) {
  description 'Runs a function in a Helma application with `-Pfunction=app.functionName`.'
  group 'Application'

  classpath = files('launcher.jar')
  mainClass = 'helma.main.launcher.Commandline'
  args '-h', projectDir, function
}
