/*
* Licensed Materials - Property of IBM Corp.
* IBM UrbanCode Deploy
* (c) Copyright IBM Corporation 2011, 2017. All Rights Reserved.
*
* U.S. Government Users Restricted Rights - Use, duplication or disclosure restricted by
* GSA ADP Schedule Contract with IBM Corp.
*/

package com.urbancode.air.plugin.docker

import com.urbancode.air.CommandHelper
import com.urbancode.air.AirPluginTool

import org.apache.tools.ant.types.Commandline



public class DockerHelper {

    CommandHelper ch
    AirPluginTool apTool

    public DockerHelper(AirPluginTool apTool) {
        this.ch = new CommandHelper(new File("."))
        this.apTool = apTool
    }

    public static String escapeContainerName(String unescapedName) {
        // replace all of the un representable characters with an underscore
        String escapedName = (unescapedName =~ /[^a-zA-Z0-9_.-]/).replaceAll("_");
        // the first character is even more restrictive
        return (escapedName =~ /(^[^a-zA-Z0-9])/).replaceFirst('a$1')
    }

    public static String getImageTag(String dockerRegistryName, String image, String tag) {
        String result = ""
        if (dockerRegistryName) {
            result += dockerRegistryName + '/'
        }
        result += image
        if (tag) {
            result += ':' + tag
        }
        return result
    }

    // Login to IBM Container Services by running 'bx login' and 'bx cr login' processes
    public def icsLogin(Properties props) {
        def username = props['dockerRegistryUserName']
        def password = props['dockerRegistryUserPassword']
        def icsApi = props['icsApi']
        def icsSpace = props['icsSpace']
        def icsOrg = props['icsOrg']
        def icsApiKey = props['icsApiKey']
        def command = ["bx", "login"]

        if (icsApi) {
            command << "-a"
            command << icsApi
        }
        if (icsSpace) {
            command << "-s"
            command << icsSpace
        }
        if (icsOrg) {
            command << "-o"
            command << icsOrg
        }

        // Need to add support for '-c AccountID' at some point.
        // Would need changes to ImageRegistry resource role, Promote Image step,
        // and Docker source plugin.

        // Are we using API Key or user/password?
        if (icsApiKey) {
            command << "--apikey"
            command << icsApiKey
        }
        else {
            command << "-u"
            command << username
            command << "-p"
            command << password
        }

        CommandHelper bxLoginCH = new CommandHelper(new File("."))
        def raw
        bxLoginCH.runCommand("Logging into Bluemix", command, { proc ->
            proc.out.close() // close stdin
            proc.consumeProcessErrorStream(System.out) // forward stderr
            raw = proc.text.trim();
        })

        // Login to Bluemix Container Regsitry now that we're logged into Bluemix
        command = ["bx", "cr", "login"]
        CommandHelper bxCRLoginCH = new CommandHelper(new File("."))

        bxCRLoginCH.runCommand("Logging into Bluemix Container Registry", command, { proc ->
            proc.out.close() // close stdin
            proc.consumeProcessErrorStream(System.out) // forward stderr
            raw = proc.text.trim();
        })

    }

    public def runDockerCommand (String dockerClientCommand, Properties props) {
        def globalOptions = props['globalOptions']?.trim() //These options, such as -H, go before the command
        String envVars = props['envVars']
        def commandOptions = props['commandOptions']
        def registry = props['registry']

        def container = props['container']
        def containerCommand = props['containerCommand']

        def image = props['image']
        def tag = props['tag']

        def email = props['dockerRegistryUserEmail']
        def username = props['dockerRegistryUserName']
        def password = props['dockerRegistryUserPassword']

        def args = ['docker']
        Map<String, Object> commandArgs = new HashMap<String, Object>()

        if (globalOptions) {
            args.addAll(splitArguments(globalOptions))
        }

        args << dockerClientCommand

        if (commandOptions) {
            args.addAll(splitArguments(commandOptions))
        }

        if (envVars) {
            LinkedHashMap parsedEnvVars = parseNLDelimitedKeyValuePairs(envVars)
            setEnvVars(parsedEnvVars)
        }

        switch (dockerClientCommand) {
            //Usage: docker run [OPTIONS] IMAGE [COMMAND] [ARG...]
            case "run":
                if (container) {
                    args.addAll(["--name", container])
                }

                args << image
                if (containerCommand) {
                    args.addAll(splitArguments(containerCommand))
                }
                break

            //Usage: docker tag [OPTIONS] IMAGE[:TAG] [REGISTRYHOST/][USERNAME/]NAME[:TAG]
            case "tag":
                args << image
                args << tag
                break

            //Usage: docker pull [OPTIONS] NAME[:TAG|@DIGEST]
            //Usage: docker push [OPTIONS] NAME[:TAG]
            case ["push", "pull"]:
                args << image
                break

            //Usage: docker login [OPTIONS] [SERVER]
            case "login":
                args.addAll(["-u", username, "-p", password])
                if (email) {
                    args << "-e"
                    args << email
                }
                if (registry) {
                    args << registry
                }
                // Redundant unless logged in via AWS
                commandArgs.put("printCommand", args*.replace(password, "****"))
                break

            //Usage: docker logout [SERVER]
            case "logout":
            if (registry) {
                args << registry
            }
            break

            //Usage: docker stop [OPTIONS] CONTAINER [CONTAINER...]
            //Usage: docker start [OPTIONS] CONTAINER [CONTAINER...]
            //Usage: docker rm [OPTIONS] CONTAINER [CONTAINER...]
            case ["stop", "start", "rm"]:
                args.addAll(splitArguments(container))
                break

        }

        commandArgs.put("message", "Executing docker command...")
        commandArgs.put("command", args)
        ch.runCommand(commandArgs)
        println "Command executed with a successful exit code"
    }

    private def splitArguments (def rawString) {
        return Commandline.translateCommandline(rawString.trim())
    }

    /**
     * Adds onto CommandHelper's environment variables
     *
     * @param   envVars     Accepts any LinkedHashMap
     */
    private void setEnvVars(LinkedHashMap envVars) {
        for (envVar in envVars) {
            String key = envVar.key.toString()
            String value = envVar.value.toString()
            if (key) {
                try {
                    ch.addEnvironmentVariable(key, value)
                } catch(UnsupportedOperationException e) {
                    println("Enviroment variable, name:${key} value:${value},"
                    + ' not loaded. System may not allow modifications to'
                    + ' environment variables or may forbid certain variable'
                    + ' names or values.')
                    System.exit(1)
                } catch(IllegalArgumentException e) {
                    println("Enviroment variable, name:${key} value:${value},"
                    + ' not loaded. System may not allow modifications to'
                    + ' environment variables or may forbid certain variable'
                    + ' names or values.')
                    System.exit(1)
                } catch(SecurityException e) {
                    println("Enviroment variables not loaded. A security"
                    + ' manager exists and its checkPermission method does not'
                    + ' allow access to the process environment.')
                    System.exit(1)
                }
            }
            else {
                println("Enviroment variable, name:${key} value:${value}, not loaded."
                + ' Must specify a name. For example: name=value or name:')
                System.exit(1)
            }
        }
    }

    /**
     * [WSP] *CHAR [WSP] '=' [WSP] *CHAR [WSP] parsed into a LinkedHashMap
     *
     * @param   textArea     Accepts any [WSP] *CHAR [WSP] '=' [WSP] *CHAR [WSP]
     * @return               A linked hashmap of all the parsed value key. Values may be null.
     */
    private LinkedHashMap parseNLDelimitedKeyValuePairs (
        String nlSerperatedValueEqualKey) {
        def result = [:]
        nlSerperatedValueEqualKey.eachLine { text ->
            def mappy = text.split('=', 2)
            if (mappy[0].trim() && mappy.size() == 2) {
                result[mappy[0].trim()] = mappy[1].trim()
            } else {
                println 'Please use \'=\' for name value seperation. For example: HOME=/user/'
                system.exit(1)
            }
        }
        return result
    }
}
