description = 'ELKI - Core Utilities'

def templateDir = new File(projectDir, "src/templates/java")
def generatedSrcDir = new File(buildDir, "src/generated/java")

sourceSets.main.java.srcDirs += generatedSrcDir

// Simple code generator 
def generate(File inp, File out, Map vals) {
	if (out.exists() && out.lastModified() == inp.lastModified()) return;
	def eng = new groovy.text.GStringTemplateEngine();
	try {
	      out.text = eng.createTemplate(inp).make(vals);
	} catch(RuntimeException e) {
	      throw new RuntimeException("${e.getMessage()} when generating $out", e);
	}
	out.setLastModified(inp.lastModified());
}

task generateCodeArrays(description: "Generate code from templates") {
      def pkg = "elki/utilities/datastructures/arraylike/";
      def out = new File(generatedSrcDir, pkg);
      def inp = new File(templateDir, pkg);
      def gen = {
        out.mkdirs()

        // This will run every time (but we check timestamps)
        // Because otherwise, it would not run in eclipse buildship at all.

        //// Primitive arrays
        def tpl = new File(inp, "PArray.javat")
        generate(tpl, new File(out, "IntegerArray.java"),
            [type: "int", boxedtype: "Integer", classname: "IntegerArray"])
        generate(tpl, new File(out, "DoubleArray.java"),
            [type: "double", boxedtype: "Double", classname: "DoubleArray"])

        //// Primitive array adapters
        tpl = new File(inp, "PArrayAdapter.javat")
        generate(tpl, new File(out, "FloatArrayAdapter.java"),
            [type: "float", boxedtype: "Float", classname: "FloatArrayAdapter"])
        generate(tpl, new File(out, "DoubleArrayAdapter.java"),
            [type: "double", boxedtype: "Double", classname: "DoubleArrayAdapter"])
      }
      gen() // For eclipse buildship until supported: https://github.com/eclipse/buildship/issues/266
      doLast gen
      inputs.files fileTree(inp)
      outputs.files fileTree(out)
}
tasks.compileJava.dependsOn generateCodeArrays
tasks.sourceJar.dependsOn generateCodeArrays

task generateCodeHeaps(description: "Generate code from templates") {
      def pkg = "elki/utilities/datastructures/heap/";
      def out = new File(generatedSrcDir, pkg);
      def inp = new File(templateDir, pkg);
      def gen = {
        out.mkdirs()

        // It would be straightforward to generate additional types,
        // but we would rather keep the jar smaller.
        Map tdbl = [type:"double", boxed:"Double", raw:"double", zero: "0.", gend: "", genu:"", cmp: {c,x,y -> "$x $c $y"}]
        Map tint = [type:"int", boxed:"Integer", raw:"int", zero: "0", gend: "", genu:"", cmp: {c,x,y -> "$x $c $y"}]
        Map tlng = [type:"long", boxed:"Long", raw:"long", zero: "0", gend: "", genu:"", cmp: {c,x,y -> "$x $c $y"}]
        Map kobj = [type:"K", boxed:"Object", raw:"Object", zero: "null", gend: "<K>", genu:"<K>", cmp: {c,x,y -> "comparator.compare($x, $y) $c 0"}]
        Map kcom = [type:"K", boxed:"Comparable", raw:"Comparable<Object>", zero: "null", gend: "<K extends Comparable<? super K>>", genu:"<K>", cmp: {c,x,y -> "${x}.compareTo($y) $c 0"}]
        Map vobj = [type:"V", boxed:"Object", raw:"Object", zero: "null", gend: "<V>", genu:"<V>"]
        Map ecmp = [fields:"/**\n   * Comparator\n   */\n  protected java.util.Comparator<Object> comparator;",
                                    param:"@param comparator Comparator",
                                    constructor:"java.util.Comparator<? super K> comparator",
                                    init:"this.comparator = (java.util.Comparator<Object>) comparator;"]

        //// Key-only Heap interfaces
        def tpl = new File(inp, "KHeap.javat")
        generate(tpl, new File(out, "DoubleHeap.java"),
            [classname: "DoubleHeap"] + tdbl)
        generate(tpl, new File(out, "IntegerHeap.java"),
            [classname: "IntegerHeap"] + tint)
        generate(tpl, new File(out, "ObjectHeap.java"),
            [classname: "ObjectHeap"] + kobj)
        //// Key-only min heaps
        tpl = new File(inp, "KMinHeap.javat")
        generate(tpl, new File(out, "DoubleMinHeap.java"),
            [classname: "DoubleMinHeap", extra: null] + tdbl)
        generate(tpl, new File(out, "IntegerMinHeap.java"),
            [classname: "IntegerMinHeap", extra: null] + tint)
        generate(tpl, new File(out, "ComparableMinHeap.java"),
            [classname: "ComparableMinHeap", extra: null] + kcom)
        generate(tpl, new File(out, "ComparatorMinHeap.java"),
            [classname: "ComparatorMinHeap", extra: ecmp] + kobj)
        //// Key-only max heaps
        tpl = new File(inp, "KMaxHeap.javat")
        generate(tpl, new File(out, "DoubleMaxHeap.java"),
            [classname: "DoubleMaxHeap", extra: null] + tdbl)
        generate(tpl, new File(out, "IntegerMaxHeap.java"),
            [classname: "IntegerMaxHeap", extra: null] + tint)
        generate(tpl, new File(out, "ComparableMaxHeap.java"),
            [classname: "ComparableMaxHeap", extra: null] + kcom)
        generate(tpl, new File(out, "ComparatorMaxHeap.java"),
            [classname: "ComparatorMaxHeap", extra: ecmp] + kobj)

        //// Important: The use of non-primitive keys in KV heaps is currently outdated
        //// Because it usually makes more sense to use a ObjectHeap instead.

        //// Key-Value heap interfaces
        tpl = new File(inp, "KVHeap.javat")
        generate(tpl, new File(out, "DoubleIntegerHeap.java"),
            [classname: "DoubleIntegerHeap", key: tdbl, val: tint])
        generate(tpl, new File(out, "DoubleLongHeap.java"),
            [classname: "DoubleLongHeap", key: tdbl, val: tlng])
        generate(tpl, new File(out, "DoubleObjectHeap.java"),
            [classname: "DoubleObjectHeap", key: tdbl, val: vobj])
        generate(tpl, new File(out, "IntegerObjectHeap.java"),
            [classname: "IntegerObjectHeap", key: tint, val: vobj])
        //// Key-Value min heaps
        tpl = new File(inp, "KVMinHeap.javat")
        generate(tpl, new File(out, "DoubleIntegerMinHeap.java"),
            [classname: "DoubleIntegerMinHeap", key: tdbl, val: tint])
        generate(tpl, new File(out, "DoubleLongMinHeap.java"),
            [classname: "DoubleLongMinHeap", key: tdbl, val: tlng])
        generate(tpl, new File(out, "DoubleObjectMinHeap.java"),
            [classname: "DoubleObjectMinHeap", key: tdbl, val: vobj])
        generate(tpl, new File(out, "IntegerObjectMinHeap.java"),
            [classname: "IntegerObjectMinHeap", key: tint, val: vobj])
        //// Key-Value max heaps
        tpl = new File(inp, "KVMaxHeap.javat")
        generate(tpl, new File(out, "DoubleIntegerMaxHeap.java"),
            [classname: "DoubleIntegerMaxHeap", key: tdbl, val: tint])
        generate(tpl, new File(out, "DoubleLongMaxHeap.java"),
            [classname: "DoubleLongMaxHeap", key: tdbl, val: tlng])
        generate(tpl, new File(out, "DoubleObjectMaxHeap.java"),
            [classname: "DoubleObjectMaxHeap", key: tdbl, val: vobj])
        generate(tpl, new File(out, "IntegerObjectMaxHeap.java"),
            [classname: "IntegerObjectMaxHeap", key: tint, val: vobj])
      }
      gen() // For eclipse buildship until supported: https://github.com/eclipse/buildship/issues/266
      doLast gen

      inputs.files fileTree(inp)
      outputs.files fileTree(out)
}
tasks.compileJava.dependsOn generateCodeHeaps
tasks.sourceJar.dependsOn generateCodeHeaps

dependencies {
  api project(':elki-logging')
  api group: 'net.jafama', name: 'jafama', version:'[2.3.0,3)'
  api group: 'it.unimi.dsi', name: 'fastutil', version: '[8,9)'
  testImplementation group: 'junit', name: 'junit', version:'[4.8,)'
}
