/*
 * Licensed to the Apache Software Foundation (ASF) under one or more
 * contributor license agreements.  See the NOTICE file distributed with
 * this work for additional information regarding copyright ownership.
 * The ASF licenses this file to You under the Apache License, Version 2.0
 * (the "License"); you may not use this file except in compliance with
 * the License.  You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

 /* - Scalafmt Toolage -*/
buildscript {
  repositories {
    jcenter()
    mavenCentral()
  }
  dependencies {
    classpath "cz.alenkacz:gradle-scalafmt:${gradle.scalafmt.version}"
  }
}

plugins {
    id 'com.github.blindpirate.gogradle' version '0.10'
}

subprojects {
  apply plugin: 'scalafmt'
  scalafmt.configFilePath = gradle.scalafmt.config
}

golang {
    packagePath = 'github.com/apache/openwhisk-cli' as String
    buildTags = (rootProject.findProperty('goTags')?:'').split(',')
}

// The `gogradle` plugin was designed to work with `govendor` and `godeps` tools
// We must disable its tasks that attempt to "fetch" dependencies
// into a "/vendor" directory and use them to build the project (which will fail)
installDependencies.enabled = false
resolveBuildDependencies.enabled = false
resolveTestDependencies.enabled = false

// Disable "go vet" and "gofmt" as gogradle uses deprecated syntax
goVet.enabled = false
gofmt.enabled = false

/*
    The OpenWhiskPlatform class is a utility class to make the rest of what
    happens with platforms a bit more understandable.  A "Platform" is a tuple
    of an operating system and a processor.  Currently, the OpenWhisk CLI
    supports three OS's:  Linux, Mac/Darwin, and Windows.  It supports x86
    (32-bit or 64-bit) on all OS's.  On Linux, it also support System Z (s390x),
    PowerPC (ppc64le), and ARM (32-bit and 64-bit) architectures.

    Different contexts use different codings to refer to these architectures --
    the class attempts to provide and interpret all needed codings.  Internal
    storage is in "GO" format:

        OS: linux, darwin, windows
        Arch: 386, amd64, s390x, ppc64le, arm

    TODO - It may be appropriate to refactor into a general purpose Platform
           class for all builds, then to extend with specifics needed for
           the OpenWhisk CLI build.
 */
class OpenWhiskPlatform {
    String goOs
    String goArch

    /*
        The 'zipFileName' property is the root file name to use for archives.
     */
    static String zipFileName

    /*
        Create a platform for the local platform
     */
    OpenWhiskPlatform() {
        this(System.properties['os.name'], System.properties['os.arch'])
    }

    OpenWhiskPlatform(String platformSpec) {
        this(*platformSpec.split('-'))
    }

    OpenWhiskPlatform(String inOs, String inArch) {
        goOs=inOs.toLowerCase()
                 .replaceAll(~/^mac.*$/,'darwin')
                 .replaceAll(~/^.*n[ui]x.*$/,'linux')
        goArch=inArch.toLowerCase()
                     .replaceAll('x86_64','amd64')
                     .replaceAll('i386','386')
                     .replaceAll('x86_32','386')
    }

    /**
     * Return the Openwhisk OS for this Platform
     */
    String getOwOs() {
        ((goOs == 'darwin') ? 'mac' : goOs)
    }

    String getGoPlatform() {
        "${goOs}-${goArch}"
    }

    /*
        Everything below here is specific to the CLI build and could be
        factored out into a subclass.
     */
    String getArchiveDirName() {
        "${this.owOs}/${goArch}"
    }

    String getArchiveFileName() {
        String suffix
        switch (goArch) {
            case "386": suffix = '-32bit'; break;
            case "amd64": suffix = ''; break;
            default: suffix = "-${goArch}"; break;
        }
        String archivetype = (goOs == 'linux') ? 'tgz' : 'zip'
        "${zipFileName}-${this.owOs}${suffix}.${archivetype}"
    }
}

/*
    Configuration of OpenWhisk Platform behavior based on environment and defaults
 */
OpenWhiskPlatform.zipFileName =
    System.env['zip_file_name'] ?:
        (rootProject.findProperty('zipFileName') ?: 'OpenWhisk_CLI')

project.ext.packageVersion =
        rootProject.findProperty('packageVersion') ?: 'latest'

project.ext.cliBuildLocation =
        rootProject.findProperty('cliBuildLocation') ?: './build'

project.ext.cliReleaseLocation =
        rootProject.findProperty('cliReleaseLocation') ?: './release'

String buildFileName = System.env['build_file_name'] ?:
        (rootProject.findProperty('buildFileName') ?: 'wsk')

/*
    'platforms' property will be null for a local compile, or a list (comma or
    space-separated) of hyphenated Goos-Goarch pairs.  Some transformation is
    done when parsing to handle misconceptions.

    TODO:  More syntax/validity checking and feedback, perhaps as part of a
    Platform object as proposed above...
*/
rootProject.ext.localPlatform = new OpenWhiskPlatform()

if (rootProject.hasProperty('buildPlatforms')) {
    rootProject.ext.platforms = buildPlatforms.tokenize(' ,').collect {
        new OpenWhiskPlatform(it)
    }
} else {
    if (!rootProject.hasProperty('nativeCompile')) {
        rootProject.ext.platforms = [
            'linux-386', 'linux-amd64',
            'linux-s390x', 'linux-ppc64le', 'linux-arm', 'linux-arm64',
            'darwin-amd64',
            'windows-386', 'windows-amd64'
        ].collect { new OpenWhiskPlatform(it) }
    } else {
        rootProject.ext.platforms = [ rootProject.localPlatform ]
    }
}

/*
    I18n support
 */

//  task getGoPath(type: Exec) {
//     executable = 'echo'
//     args = ["$System.env.GOPATH"]

//     doLast{
//       println commandLine
//     }
// }

task getGoBinData(type: Exec) {
    executable = 'go'
    args = ['get', '-u', 'github.com/jteeuwen/go-bindata/...']

    doLast{
      println commandLine
    }
}

task goI18n(type: Exec) {
    dependsOn 'getGoBinData'
    executable = "$System.env.GOPATH" + '/bin/go-bindata'
    // run '${GOPATH}/bin/go-bindata -pkg wski18n -o wski18n/i18n_resources.go wski18n/resources'
    args = ['-pkg', 'wski18n', '-o', 'wski18n/i18n_resources.go', 'wski18n/resources']

    doLast{
      println commandLine
    }
}

/*
    Checks -- add golint to the checks run prior to build.
       The get step is needed to be sure a golint binary is available to run.
 */

task getGoLint(type: Exec) {
    executable = 'go'
    args = ['get', '-u', 'golang.org/x/lint/golint']

    doLast{
      println commandLine
    }
}

task goLint(type: Exec) {
    dependsOn 'getGoLint'
    executable = 'golint'
    //args = ['./', './commands/']
    args = ['./']

    doLast{
      println commandLine
    }
}

goCheck.dependsOn(goLint)
goPrepare.dependsOn(goI18n)

goBuild {
    targetPlatform = rootProject.platforms*.goPlatform
    def now = new Date().format("yyyy-MM-dd'T'HH:mm:ss.SSSZ")

    // WARNING:  The single quotes are intentional!  The gogradle plugin will
    //           parse the command with the GString engine at execution time.
    go(['build',
      '-ldflags', "-X main.CLI_BUILD_TIME=${now}" as String,
      '-o', cliBuildLocation+'/${GOOS}-${GOARCH}/'+buildFileName+'${GOEXE}',
      golang.packagePath ] as List<String>)
}

gofmt {
    gofmt "-s -w ."
}

task compile(type: Copy, dependsOn: goBuild) {
    destinationDir = file(cliBuildLocation)
    from("${cliBuildLocation}/${rootProject.localPlatform.goOs}-${rootProject.localPlatform.goArch}")
}

task build(type: DefaultTask, dependsOn: compile)

/*
    For each platform, create an individual archive in a platform appropriate
    format (tarball for Linux, zipfile for Mac & Windows).
 */
task individualArchives(
    dependsOn: rootProject.platforms.collect() { p ->
        task("release${p.goOs.capitalize()}${p.goArch.capitalize()}",
            type: (p.goOs == 'linux') ? Tar : Zip, dependsOn: compile) {
                if (p.goOs == 'linux') { compression = Compression.GZIP }
                destinationDir = file(cliReleaseLocation)
                baseName = "${p.zipFileName}-${packageVersion}-${p.owOs}-${p.goArch}"
                from("${cliBuildLocation}/${p.goOs}-${p.goArch}/") {
                    include "${buildFileName}*"
                }
                from("./") {
                    include "LICENSE.txt", "NOTICE.txt", "README.md"
                    exclude "wski18n"
                }
            }
    })

/*
    Create a 'content.json' file representing all that was
    compiled and its appropriate directory in the Tarball that will be created
    for deployment to local Nginx instances.
 */

task index() {
    def content = [:]
    for (p in platforms) {
        def pathObject = [ "path" : "${p.archiveDirName}/${p.archiveFileName}" ]
        content.get(p.owOs,[:])[p.goArch] = pathObject
        // TODO: Default architecture should be configurable as a property
        if (p.goArch == 'amd64') {
            content.get(p.owOs,[:])['default'] = pathObject
        }
    }

    doLast {
        mkdir(cliBuildLocation)
        file("${cliBuildLocation}/content.json").text = groovy.json.JsonOutput.toJson(["cli": content])
    }
}

task releaseBinaries(type: Tar, dependsOn: [individualArchives, index]) {
    compression = Compression.GZIP
    destinationDir = file(cliReleaseLocation)
    baseName = "${OpenWhiskPlatform.zipFileName}-${packageVersion}-all"
    from("${cliBuildLocation}/content.json") { into('.') }
    rootProject.platforms.each() { p ->
        from(cliReleaseLocation) {
            include("${p.zipFileName}-${packageVersion}-${p.owOs}-${p.goArch}.*")
            into p.archiveDirName
            rename { p.archiveFileName }
        }
    }
}

task clean(type: Delete, dependsOn: goClean) {
    delete cliBuildLocation, cliReleaseLocation
}
