import org.tmatesoft.svn.core.wc.*

//includeTargets << grailsScript("Init")

/**
* Compare and update the app.vcsRevision property in application.properties and metadata.
* May be run directly by "grails update-rev" or from _Events.groovy by eventCompileStart.
*/
target(updateVcsRevision: "Update the app.vcsRevision property in application.properties and metadata.") {

    def result = [:]

    def fail = { Map m ->
        result.error = [ code: m.code, args: m.args ]
        println "Error: UpdateRev script: " + result.error
        return result
    }

    def url = basedir + "/application.properties"

    def propertiesFile = new File(url)
        if(!propertiesFile.isFile())
            return fail(code:"application.properties.file.not.found", args:[url])

    def appRevision = getAppRevision(propertiesFile)
    if(appRevision.error)
        return fail(code: appRevision.error.code, args: appRevision.error.args)

    def svnRevision = getSvnRevision()
    if(svnRevision.error)
        return fail(code: svnRevision.error.code, args: svnRevision.error.args)

    // Compare and update.
    if(appRevision.revision != svnRevision.revision) {

        println "app.vcsRevision = "+appRevision.revision +', SVN Revision = '+svnRevision.revision

        // Update metadata if already loaded.
        if(binding.variables.containsKey('metadata')) {
            //binding.variables.each { println it.key } // print available.
            def metadata = binding.variables['metadata']
            metadata['app.vcsRevision'] = '$Rev: '+svnRevision.revision+' $'
        }

        // Update application.properties file.
        def writeResult = writeVcsRevision(propertiesFile, svnRevision.revision)
        if(writeResult.error)
            return fail(code: writeResult.error.code, args: writeResult.error.args)

    } // if(rev != rev)
    else {
        println "VCS Revisions match: app.vcsRevision = "+appRevision.revision +', SVN Revision = '+svnRevision.revision +'.'
    }

    // Success.
    return result

} // updateVcsRevision()

/**
* Get the app.vcsRevision property from properties file.
* @retuns A map containing revision and lineNumber otherwise an error map.
*/
def getAppRevision(propertiesFile) {
    def result = [:]

    def fail = { Map m ->
        result.error = [ code: m.code, args: m.args ]
        return result
    }

    propertiesFile.eachLine { line, lineNumber ->
        // app.vcsRevision=$Rev: NUM $
        if ( line =~ '^app.vcsRevision.' ) {
            if(line.size() > 23) {
                result.revision = line[22..-3]
                result.lineNumber = lineNumber
            }
        }
    }

    if(!result.revision || !result.lineNumber)
        return fail(code:"app.vcsRevision.not.found")

    // Success.
    return result

} // getAppRevision()

/**
* Get the working copy's base revision from SVN.
* @retuns A map containing revision otherwise an error map.
*/
def getSvnRevision() {
    def result = [:]

    def fail = { Map m ->
        result.error = [ code: m.code, args: m.args ]
        return result
    }

    def wc = new File(basedir)
        if(!wc.isDirectory())
            return fail(code:"vcs.working.copy.not.found", args:[basedir])

    // Use svnkit to get the base revision.
    def clientManager = SVNClientManager.newInstance()
    def wcClient = clientManager.getWCClient()
    try {
        result.revision = wcClient.doInfo(wc, SVNRevision.BASE).getRevision().toString()
    }
    catch(org.tmatesoft.svn.core.SVNException e) {
        fail(code:"vcs.exception", args:[e])
    }

    // Success.
    return result
} // getSvnRevision()

/**
* Write revision to properties file.
* @retuns An error map if any errors.
*/
def writeVcsRevision(propertiesFile, revision) {
    def result = [:]

    def fail = { Map m ->
        result.error = [ code: m.code, args: m.args ]
        return result
    }

    def revisionString = 'app.vcsRevision=\\$Rev: '+revision+' \\$'
    println "Updating application.properties with: ${revisionString}"

    def processFileInplace = { file, Closure processText ->
        def text = file.text
        file.write(processText(text))
    }

    processFileInplace(propertiesFile) { text ->
        text.replaceAll('app.vcsRevision.*', revisionString)
    }

    // Success.
    return result

} // writeVcsRevision()
