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

ext.jreVersion = "1.8.0_05"
ext.dependenciesDir = new File(buildDir, 'dependencies')
ext.pluginsDir = new File(buildDir, 'plugins')
ext.versionString = "${version_major}.${version_minor}.${version_revision}"

allprojects {
    apply plugin: 'java'
    sourceCompatibility = 1.8

    repositories {
        mavenCentral()
        maven { //MigLayout 5.0-SNAPSHOT
            url 'https://oss.sonatype.org/content/repositories/snapshots/'
        }
        maven { //JRE
            url 'http://anathema.butatopanto.de:8081/maven/releases/'
        }
    }
}

apply plugin: 'idea'
idea { project { languageLevel = '1.8' } }

subprojects {
    apply from: "../gradle/subprojectPresets.gradle"
}

apply from: "gradle/publishUpdate.gradle"
apply from: "gradle/publishFullRelease.gradle"
apply from: "gradle/createModule.gradle"
apply from: 'gradle/copyDependencies.gradle'
apply from: 'gradle/gitTag.gradle'

configurations {
    launch4j
    nsis
    scp
    bundleapp
    jreWin
    jreMac
}
dependencies {
    launch4j files(
            "./Development_Distribution/Windows/Binaries/launch4j/launch4j.jar",
            "./Development_Distribution/Windows/Binaries/launch4j/xstream.jar",
    )
    nsis files("./Development_Distribution/Windows/Binaries/NSIS/nsisant-1.3.jar")
    scp 'org.apache.ant:ant-jsch:1.8.4', 'com.jcraft:jsch:0.1.48'
    bundleapp files("./Development_Distribution/Mac/Binaries/appbundler-1.0.jar")
    jreWin "oracle:jre-win32:${jreVersion}@zip"
    jreMac "oracle:jre-mac64:${jreVersion}@zip"
}

task wrapper(type: Wrapper) {
    gradleVersion = '2.2.1'
}

def os = operatingSystem()

String operatingSystem() {
    def os = System.getProperty('os.name').toLowerCase()
    def result = ""
    if (os.indexOf("win") >= 0) result = "win"
    if (os.indexOf("mac") >= 0) result = "mac"
    if (os.indexOf("nix") >= 0         //  unix
            || os.indexOf("nux") >= 0) result = "linux"
    if (os.indexOf("sunos") >= 0) result = "solaris"
    return result
}

// Release Variables
ext.projectName = "Anathema"
ext.releaseZipName = "${projectName}_Zip_v${versionString}.zip"
ext.releaseWinName = "${projectName}_Win_v${versionString}.exe"
ext.releaseMacName = "${projectName}_Mac_v${versionString}.zip"
ext.macAppDir = "${projectName}.app"
ext.releaseDirName = "${projectName} ${versionString}"
if ("${version_revision}" == '0') releaseDirName
ext.releaseDir = new File("${buildDir}/${releaseDirName}")

// All Releases
task generateChangelog << {
    // grab the latest updates for this release from versions.md and put into readme.md for sourceforge
    String delim = "## Release ${versionString}"
    String delim2 = "## Release "
    String releaseNotes = new File("Development_Documentation/Distribution/English/versions.md").getText('UTF-8')
    try {
        new File("${releaseDir}/readme.md").write(delim + releaseNotes.split(delim)[1].split(delim2)[0], 'UTF-8')
    } catch (java.lang.ArrayIndexOutOfBoundsException e) {
        out.println("Unable to generate readme.md, the version number in gradle.properties is not found in versions.md")
    }
}

generateChangelog.group = 'Release'

task version << {
    out.println("\nPlease update the version number and name.  Enter nothing to default to the old value.")
    def vNumber = usePropertyOrPrompt("versionNumber", "\n\nVersion Number | old: ${versionString} new: ")
    ext.versionString = vNumber == "" ? versionString : vNumber
    try {
        ext.version_major = versionString.split("\\.")[0]
        ext.version_minor = versionString.split("\\.")[1]
        ext.version_revision = versionString.split("\\.")[2]
    } catch (java.lang.ArrayIndexOutOfBoundsException e) {
        throw new StopExecutionException("Error setting new version number, please use the format major.minor.revision (4.1.2).")
    }
    def p = new Properties()
    def f = new File("gradle.properties")
    new File("gradle.properties").withReader {
        p.load(it)
        p.setProperty("version_major", version_major)
        p.setProperty("version_minor", version_minor)
        p.setProperty("version_revision", version_revision)
    }
    f.withWriter {
        p.store(it, "Please update this file using this command: gradlew version")
    }
    out.println("\nVersion number and name have been updated.")
    out.println("A new build must be run in order to use the new values.")
}

// JRE archive download and explosion into build folder
task prepareWinJRE(type: Copy) {
    def target = new File('build/jre/win32');
    into target
    from { zipTree(configurations.jreWin.singleFile) }
    onlyIf { !target.exists() }
    doLast {
        ant.delete(includeemptydirs: true, quiet: true) {
            fileset(dir: buildDir, includes: "tmp/expandedArchives/jre-win32*/**", defaultexcludes: false)
        }
    }
}

task prepareMacJRE(type: Copy) {
    def target = new File('build/jre/mac64');
    into target
    from { zipTree(configurations.jreMac.singleFile) }
    onlyIf { !target.exists() }
    doLast {
        ant.delete(includeemptydirs: true, quiet: true) {
            fileset(dir: buildDir, includes: "tmp/expandedArchives/jre-mac64*/**", defaultexcludes: false)
        }
    }
}

task prepareJRE dependsOn(prepareWinJRE, prepareMacJRE)

// Extracts the JRE for a Mac.  This only runs on a Mac, and requires the current release JDK be installed.
// This only need be run by a release manager upgrading the bundled JRE used by Anathema.
task extractMacJRE(type: Zip) {
    archiveName = "jre-mac64-${jreVersion}.zip"
    destinationDir = buildDir
    from("/Library/Java/JavaVirtualMachines/jdk${jreVersion}.jdk/Contents") {
        into "jre${jreVersion}/Contents"
        include 'Info.plist'
    }
    from("/Library/Java/JavaVirtualMachines/jdk${jreVersion}.jdk/Contents/Home/jre") {
        into "jre${jreVersion}/Contents/Home/jre"
        exclude 'bin/',
                'lib/deploy/',
                'lib/deploy.jar',
                'lib/javaws.jar',
                'lib/libdeploy.dylib',
                'lib/libnpjp2.dylib',
                'lib/plugin.jar',
                'lib/security/javaws.policy'
    }
}
extractMacJRE.onlyIf { os.equals("mac") }

// Zip Release
task assembleZip(type: Zip) {
    archiveName = releaseZipName
    destinationDir = releaseDir
    from 'Anathema/build/libs'
    into('lib') {
        from(dependenciesDir)
        from(pluginsDir)
    }
    from("Development_Distribution/Linux") {
        include 'anathema.sh'
        filter(FixCrLfFilter.class,
                eol: FixCrLfFilter.CrLf.newInstance("lf"))
        fileMode = 0755
    }
    from("build/launcher") {
        include 'anathema.exe'
    }
    from("Development_Documentation/Distribution/English") {
        include 'license.txt'
        include 'versions.md'
        rename 'versions.md', 'release_notes.txt'
    }
}

// Windows Release
task compileLauncherWin << {
    def launch4jPath = "Development_Distribution/Windows/Binaries/launch4j/bin_" + os

    mkdir("./build/launcher")
    def exeVersion = "${versionString}.0"
    def versionText = "${versionString}"
    ant.taskdef(
            name: 'launch4j',
            classname: 'net.sf.launch4j.ant.Launch4jTask',
            classpath: configurations.launch4j.asPath
    )
    ant.launch4j(
            bindir: launch4jPath
    ) {
        config(
                outfile: 'build/launcher/anathema.exe',
                jarPath: 'anathema.jar',
                dontwrapjar: true,
                headerType: 'gui',
                icon: 'Development_Distribution/Windows/anathema.ico'
        ) {
            versionInfo(
                    fileVersion: exeVersion,
                    txtFileVersion: versionText,
                    fileDescription: "Anathema Windows Launcher",
                    copyright: "anathema.github.com",
                    productVersion: exeVersion,
                    txtProductVersion: versionText,
                    productName: "$projectName",
                    internalName: "anathema",
                    originalFilename: "anathema.exe"
            )
            jre(
                    path: "./jre",
                    minVersion: jreVersion
            )
        }
    }
}

task assembleWin << {
    def nsisPath = "Development_Distribution/Windows/Binaries/NSIS/bin_" + os

    ant.taskdef(name: "nsis",
            classname: "com.danielreese.nsisant.Task",
            classpath: configurations.nsis.asPath)
    ant.nsis(script: "Development_Distribution/Windows/NSIS/anathema.nsi", path: nsisPath) {
        define(name: "RELEASE_DIR", value: "${releaseDir}")
        define(name: "RELEASE_FILE", value: "${releaseWinName}")
        define(name: "RELEASE_VERSION", value: "${versionString}")
        define(name: "VERSION", value: "${versionString}.0")
        define(name: "JRE_PATH", value: "build\\jre\\win32\\jre${jreVersion}")
    }
}

// Mac Release
task compileLauncherMac << {
    ant.taskdef(
            name: "bundleapp",
            classname: "com.oracle.appbundler.AppBundlerTask",
            classpath: configurations.bundleapp.asPath
    )
    ant.bundleapp(
            outputdirectory: "${buildDir}",
            name: "Anathema",
            displayname: "Anathema",
            identifier: "net.sf.anathema",
            icon: "Development_Distribution/Mac/sungear.icns", //opt
            shortversion: "${versionString}", //opt
            signature: "????", //opt
            //copyright: blah , //opt
            applicationCategory: "public.app-category.utilities", //opt
            mainclassname: "net/sf/anathema/AnathemaBootLoader"
    ) {

        runtime(dir: "build/jre/mac64/jre${jreVersion}/Contents/Home")  //opt

        classpath(dir: "Anathema/build/libs/")
        classpath(file: "Anathema/src/main/resources/icons/core/AnathemaSplashNew.png")
        classpath(dir: "${buildDir}/dependencies/")
        classpath(dir: "${buildDir}/plugins/")

        option(value: "-splash:\$APP_ROOT/Contents/Java/AnathemaSplashNew.png")
        option(value: "-Dapple.laf-useScreenMenuBar=true")
        option(value: "-Ddefaultrepository=%USER_HOME%/Library/Application Support/Anathema")
    }
}

task assembleMac(type: Zip) {
    archiveName = releaseMacName
    destinationDir = releaseDir
    into("${projectName}") {
        from("${buildDir}")
        include "${macAppDir}/**"
        exclude "${projectName}/${macAppDir}/Contents/MacOS/JavaAppLauncher" // Skipping this because...
    }
    into("${projectName}/${macAppDir}/Contents/MacOS/") {                    // We include it here with the execute bit set.
        from("${buildDir}/${macAppDir}/Contents/MacOS/")                       // Necessary when building on a windows system
        include 'JavaAppLauncher'
        fileMode = 0755
    }
    into("${projectName}") {
        from("Development_Documentation/Distribution/English")
        include 'license.txt'
        include 'versions.md'
        rename 'versions.md', 'release_notes.txt'
        include 'installation_instructions.webloc'
    }
}

// Release Dependencies
[compileLauncherWin, compileLauncherMac, assembleZip].each {
    it.dependsOn ':Anathema:jar'
    it.dependsOn copyExternalDependencies
    it.dependsOn copyAnathemaModules
    it.dependsOn generateChangelog
}

compileLauncherWin.dependsOn prepareWinJRE
compileLauncherMac.dependsOn prepareMacJRE

assembleZip.dependsOn compileLauncherWin
assembleWin.dependsOn compileLauncherWin
assembleMac.dependsOn compileLauncherMac

task assembleAll dependsOn(assembleZip, assembleWin, assembleMac)

//SSH Support
def usePropertyOrPrompt(property, prompt) {
    if (project.hasProperty(property)) {
        return project.property(property);
    }
    def propertyValue = System.console().readLine("\n${prompt}: ")
    ext[property] = propertyValue
    return propertyValue
}

def findKeyFile() {
    def keyfile = "${System.properties['user.home']}/.ssh/id_rsa"
    while (!new File(keyfile).exists()) {
        keyfile = System.console().readLine("\nCould not find the ssh private keyfile: ${keyfile}\nEnter your keyfile pathname: ")
        keyfile = keyfile.replace("~", System.properties['user.home'])
    }
    return keyfile
}

void runSshExecOnBuildServer(command) {
    def host = "butatopanto.de"
    def user = "anathema"
    def keyfile = findKeyFile()
    def passphrase = usePropertyOrPrompt('anathemaPassphrase', "Anathema SSH Key Passphrase")
    sshExec(host, user, keyfile, passphrase, command)
}

void sshExec(String host, String user, keyfile, passphrase, command) {
    ant.taskdef(
            name: "sshexec",
            classname: "org.apache.tools.ant.taskdefs.optional.ssh.SSHExec",
            classpath: configurations.scp.asPath
    )
    ant.sshexec(
            host: host,
            username: user,
            keyfile: keyfile,
            passphrase: "${passphrase}",
            trust: "true",
            command: command
    )
}

def scp(connectString, keyfile, passphrase, Closure fileset) {
    out.println("\nPlease be patient, it takes a few minutes to upload the releases")
    out.println("Unfortunately, feedback during the upload is unavailable (unless you use the -i option)")

    ant.taskdef(
            name: "scp2",
            classname: "org.apache.tools.ant.taskdefs.optional.ssh.Scp",
            classpath: configurations.scp.asPath
    )
    ant.scp2(
            todir: connectString,
            keyfile: keyfile,
            passphrase: "${passphrase}",
            trust: "true",
            verbose: "true", // only visible at the info logging level
            sftp: "true"
    ) {
        fileset()
    }
}
