import groovy.io.FileType
apply plugin: 'java'
apply plugin: 'groovy'
apply plugin: 'eclipse'
apply plugin: 'idea'

apply plugin: 'checkstyle' //for java code conventions
apply plugin: 'jdepend' //for dependency analysis
apply plugin: 'findbugs' //for static code analysis
apply plugin: 'pmd'

defaultTasks 'clean', 'build'

sourceCompatibility = '1.6'
targetCompatibility = '1.6'

project.ext {

  //App Config
  appConfigUrl = new URL("file:///${projectDir}/AppConfig.groovy")
  appConfig = new ConfigSlurper('configuration').parse(appConfigUrl).configuration
  manifest = project.ext.appConfig.product.distribution.jar.manifest.details
  project.archivesBaseName = project.ext.appConfig.product.distribution.name
  previousArchiveName = project.ext.appConfig.product.distribution.previousArchiveName

  //libs
  mongoDriver = 'org.mongodb:mongo-java-driver:2.12.2'
  groovy = 'org.codehaus.groovy:groovy-all:2.0.5'
  commonsCli = 'commons-cli:commons-cli:1.1'
  functionaljava = 'org.functionaljava:functionaljava:3.1'
  log4j = 'log4j:log4j:1.2.17'

  //test-libs
  junit = 'junit:junit:4.8.1'
  hamcrest = 'org.hamcrest:hamcrest-all:1.3'
  mockito = 'org.mockito:mockito-all:1.9.0'
  cglib = 'cglib:cglib-nodep:2.2'
  spock = 'org.spockframework:spock-core:0.7-groovy-2.0'
  unmanagedTestLibDir = "$projectDir/test-lib"

  //code coverage lib
  coberturaLib = 'net.sourceforge.cobertura:cobertura:1.9.4.1'
  instrumentedClassesDir = "$project.buildDir/classes/instrumented"
  coverageReportsDir = "$reporting.baseDir/cobertura"
  coverageDataFile = 'cobertura.ser'
  lineCoverageBaseline = 85
  branchCoverageBaseline = 70

  //dependency analysis
  jdepend = 'jdepend:jdepend:2.9.1'

  //Distribution settings
  distsTemplateDir = 'distribution-template'
  distsBinDir = "$distsDir/bin"

  //Acceptance Tests
  acceptanceTestDir = 'acceptance-tests'
  acceptanceTestStoriesDir = "$projectDir/$acceptanceTestDir/stories/played"
  acceptanceTestReportsDir = "$reporting.baseDir/$acceptanceTestDir"
  smokeTestDir = "$acceptanceTestStoriesDir/cud"
  tocTemplateFile = new File("$acceptanceTestDir/toc.html")
  exploratoryTestSrcDir = "$projectDir/exploratory-tests/test/groovy"

  explodedDir = "$project.buildDir/exploded"

  DEFAULT_PORT_PREFIX = 270
  portPrefix = System.getProperty('portPrefix', "$DEFAULT_PORT_PREFIX")

  deployScriptsDir = 'deploy-scripts'
  deployScriptsZip = "${project.archivesBaseName}-deployScripts"
  backupDeployScriptsDir = new File(System.getProperty('backupDeployScriptsTo', "$project.buildDir/rollback-deploy-scripts"))
  defaultDeployDir = 'deployed'
  deployDir = new File(System.getProperty('deployTo', "$project.buildDir/$defaultDeployDir"))
}

repositories {
  mavenCentral()
  maven {
    url 'http://bits.netbeans.org/maven2/'
    url 'http://functionaljava.googlecode.com/svn/maven/'
    url 'http://code.google.com/p/google-maven-repository'
  }
}

configurations {
  cobertura
  compile
  tests
}

dependencies {
  compile(project.ext.mongoDriver, project.ext.groovy, project.ext.commonsCli, project.ext.functionaljava, project.ext.log4j)
  testCompile(project.ext.hamcrest, project.ext.junit, project.ext.mockito, project.ext.spock, project.ext.cglib, project.ext.jdepend)
  testCompile fileTree (dir: "$unmanagedTestLibDir", includes: ['*.jar'])
  cobertura(project.ext.coberturaLib)
  testRuntime(project.ext.coberturaLib)
}

jar {
  manifest {
    attributes.putAll(project.ext.manifest)
  }

  doLast {
        copy {
            from (configurations.compile.asPath.split(File.pathSeparator))
            into "$libsDir"
        }
    }
}

sourceSets {
test {
    groovy {
      srcDir "$exploratoryTestSrcDir"
    }
  }
}

clean << {
  ant.delete(file:"$coverageDataFile")
}

test {
  systemProperties['portPrefix'] = project.ext.portPrefix
  def originalClasspath = getClasspath()
  doFirst {
        println "Running tests for Mongods with port prefix $project.ext.portPrefix"
        ant.taskdef(resource:'tasks.properties', classpath:configurations.cobertura.asPath)
    //Note the classpath order: instrumented classes are before the original
    //uninstrumented classes.  This is important
    setClasspath(files("$instrumentedClassesDir") + originalClasspath)

    ant.'cobertura-instrument'(todir:project.ext.instrumentedClassesDir) {
      ignore(regex:"org.apache.*, com.mongodb.*")
      fileset(dir:"${project.buildDir}/classes/main", includes:'**/*.class', excludes:'**/*Specs.class')
    }
  }

  exclude ('**/exploratory/*.class')

  doLast {
    ant.'cobertura-report'(destDir: project.ext.coverageReportsDir, format:'xml') {
      sourceSets.main*.java.srcDirs*.each { fileset(dir:it) }
      sourceSets.main*.groovy.srcDirs*.each { fileset(dir:it) }
    }
    ant.'cobertura-report'(destDir: project.ext.coverageReportsDir, format:'html') {
      sourceSets.main*.java.srcDirs*.each { fileset(dir:it) }
      sourceSets.main*.groovy.srcDirs*.each { fileset(dir:it) }
    }
    ant.'cobertura-check'(totalbranchrate:project.ext.branchCoverageBaseline, totallinerate:project.ext.lineCoverageBaseline)
    setClasspath(originalClasspath)
  }
}

// For individual use assumes you have binaries existing already.
task ('smoke-test', dependsOn: 'unzip') << {
    println "Running FIT Smoke Test..."
    runAcceptanceTests(project.ext.smokeTestDir, project.ext.acceptanceTestReportsDir)
}

// For individual use assumes you have binaries existing already.
task ('acceptance-test', dependsOn: 'unzip') << {
    println "Running FIT Acceptance Tests..."
    runAcceptanceTests(project.ext.acceptanceTestStoriesDir, project.ext.acceptanceTestReportsDir)
}

def runAcceptanceTests(String srcFolder, String reportsFolder) {
    def zipFile = file("${project.distsDir}/${project.archivesBaseName}.zip")
    if(!zipFile.exists()) {
        throw new RuntimeException(">>> Please run gradle to build the binary distributable ${project.archivesBaseName}.zip first!")
    }
    def runtimeClasspath = fileTree(dir: "${project.ext.explodedDir}/libs", include: '*.jar')
    runtimeClasspath = runtimeClasspath + fileTree(dir: project.ext.unmanagedTestLibDir, include: '*.jar')
    runtimeClasspath = runtimeClasspath + fileTree(dir: project.distsDir, include: '*.jar')

    def accTestReportsDir = new File("$project.ext.acceptanceTestReportsDir")
  if(!accTestReportsDir.exists()) {
    accTestReportsDir.mkdirs()
  }
  javaexec {
        systemProperties['portPrefix'] = project.ext.portPrefix
      main = 'fitlibrary.runner.FolderRunner'
      classpath = runtimeClasspath
      args = ["$srcFolder", "$reportsFolder"]
  }
}

task createTestJar(type: Jar, dependsOn: [test]) {
    baseName = "${project.archivesBaseName}-test"
      destinationDir = project.distsDir
      from sourceSets.test.output
}

task createDeployZip(type: Zip) {
  baseName = project.ext.deployScriptsZip
  destinationDir = project.distsDir
  from (project.ext.deployScriptsDir)
}

task unzip(type: Copy) {
  def zipFile = file("${project.distsDir}/${project.archivesBaseName}.zip")
      def outputDir = file(project.ext.explodedDir)
      from zipTree(zipFile)
  into outputDir
}

task ('exploratory-test', dependsOn:'unzip') << {
  def zipFile = file("${project.distsDir}/${project.archivesBaseName}.zip")
  if(!zipFile.exists()) {
    throw new RuntimeException(">>> Please run gradle to build the binary distributable ${project.archivesBaseName}.zip first!")
  }
  def runtimeClasspath = fileTree(dir: "${project.ext.explodedDir}/libs", include: '*.jar')
  runtimeClasspath = runtimeClasspath + fileTree(dir: project.distsDir, include: '*.jar')
  runtimeClasspath = runtimeClasspath + fileTree(dir: "$projectDir", include: 'connection.properties')
  runtimeClasspath = runtimeClasspath + files(configurations.testCompile.asPath.split(File.pathSeparator))

  javaexec {
    systemProperties['portPrefix'] = project.ext.portPrefix
    main = 'com.ee.tayra.exploratory.ExploratoryTestRunner'
    classpath = runtimeClasspath
  }
}

task distribute(type:Zip, dependsOn: [assemble, createDeployZip]) {
  doFirst {
    ant.mkdir(dir:"$distsBinDir")
    prepareExecutables()
    prepareTOCForDocumentation()
  }
  from project.ext.distsBinDir
  from (project.ext.distsTemplateDir) {
    include ('**/*.txt')
    exclude ('demo', '**/*.document')
  }
  from (project.ext.distsTemplateDir) { include ('demo', '**/*.document') into ('docs') }
  from ("$libsDir") { into ('libs') }
    from (project.ext.acceptanceTestDir) {
      into ('docs')
      exclude ('stories/not-played', 'stories/cud/files', '**/toc.html')
    }
}

findbugs {
  toolVersion = "2.0.1"
  reportsDir = file("$reporting.baseDir/findbugs")
  effort = "max"
  reportLevel = "high"
}

tasks.withType(FindBugs) {
  excludeFilter = file("${projectDir}/config/findbugs/excludeFilter.xml")
}

checkstyleTest {
  exclude('**/*/*Specs.java')
}

findbugsTest.enabled = false

def prepareExecutables() {
  def allLibFiles = new File("$libsDir").list().collect { "libs/$it" }
  new File("$distsTemplateDir").list().each { filename ->
    if(filename.startsWith('backup') || filename.startsWith('restore')) {
      def contents = new File("$distsTemplateDir/$filename").text
      new FileWriter("$distsBinDir/$filename").withWriter { writer ->
        if(filename.endsWith('.sh')) {
          writer.append contents.replace('%%JAVA_CLASS_PATH%%', allLibFiles.collect({"\$TAYRA_HOME/$it"}).join(":"))
        }
        if(filename.endsWith('.bat')) {
          writer.append contents.replace('%%JAVA_CLASS_PATH%%', allLibFiles.collect({"%TAYRA_HOME%/$it"}).join(";"))
        }
      }
    }
  }
}

task document << {
    prepareTOCForDocumentation()
}

def prepareTOCForDocumentation() {
  def toc = new StringWriter()
  def html = new groovy.xml.MarkupBuilder(toc)
  def tocLineNumber = 1
  new File(acceptanceTestStoriesDir).eachFileRecurse(FileType.FILES) { file ->
    if (file.isFile() && file.name.endsWith('html')) {
      def indexEntry = toSentence(file.name.replaceFirst('\\.html', ''))
      html.h4 {
            span tocLineNumber++
            a (href:"stories/played/$file.parentFile.name/$file.name") {
              text indexEntry
            }
      }
    }
  }
  new File("$acceptanceTestDir/index.html").withWriter { writer ->
      def content = project.ext.tocTemplateFile.text.replace('##TABLE_OF_CONTENTS##', toc.toString())
      writer.write content
  }
}

def toSentence(String name) {
  def sentence = new StringBuilder()
  name.each { character ->
     if (character.toCharArray()[0].isUpperCase()) {
       sentence << ' '
     }
     sentence << character
  }
  sentence.toString()
}

task setupDeploy << {
  ant.mkdir(dir: project.ext.backupDeployScriptsDir)
  ant.mkdir(dir: project.ext.deployDir)
}

task backupCurrentDeployScripts(type: Zip, dependsOn: setupDeploy) {
  doFirst{
      killFirst('com.ee.tayra.runner.Runner "backup"')
      println "Backing up existing deploy scripts from ${project.ext.deployDir}"
      println "To Dir ${project.ext.backupDeployScriptsDir}"
      println "With Archive name: ${project.ext.previousArchiveName}.zip"
  }
  baseName = project.ext.previousArchiveName
  destinationDir = project.ext.backupDeployScriptsDir
  from project.ext.deployDir
}

task deploy (dependsOn: backupCurrentDeployScripts) {
  doLast {
    def zipFile1 = file("${project.distsDir}/${project.archivesBaseName}.zip")
  def zipFile2 = file("${project.distsDir}/${project.ext.deployScriptsZip}.zip")
    copy {
      from zipTree(zipFile1)
    from zipTree(zipFile2)
      into project.ext.deployDir
    }
    ant.chmod (perm : 'a+x') {
      fileset (dir: project.ext.deployDir, includes : '*.sh')
    }
  }
}

def killFirst(def processName) {
  def os = System.getProperty("os.name")
  def processIdToKill = getProcessId(processName, os)
  if(!processIdToKill.isEmpty()) {
    println "Killing the process $processName with processId: $processIdToKill"
    def killProcessCmd = os ==~ /Win.*/ ? "taskkill /pid $processIdToKill /f" : "kill -15 $processIdToKill"
    println "Executing $killProcessCmd on $os..."
    killProcessCmd.execute().in.eachLine { println it }
  } else {
    println "Nothing to kill by name $processName"
  }
}

def getProcessId(def processName, def os) {
  def winProcessCmd = "wmic process where \"name='java.exe' and commandLine like '%$processName%'\" get processid"
  def processCmd = os ==~ /Win.*/ ? "$winProcessCmd " : 'ps -ef'
  def killableProcessId = null
  println "Executing $processCmd on $os..."
  killableProcessId = os ==~ /Win.*/ ?
        processCmd.execute().in.readLines().each { it.trim() }.join(" ")
      : processCmd.execute().in.filterLine {it.contains("$processName".replaceAll('\"',''))
    }
  def args = "$killableProcessId".split() as List
  def processIdToKill = ''
  if(args) {
    processIdToKill = args[1]
  }
  processIdToKill
}

build.dependsOn = ['check', 'createTestJar', 'distribute', 'smoke-test']
