/*
 * Licensed Materials - Property of IBM* and/or HCL**
 * UrbanCode Deploy
 * (c) Copyright IBM Corporation 2011, 2017. All Rights Reserved.
 * (c) Copyright HCL Technologies Ltd. 2019. All Rights Reserved.
 *
 * U.S. Government Users Restricted Rights - Use, duplication or disclosure restricted by
 * GSA ADP Schedule Contract with IBM Corp.
 *
 * * Trademark of International Business Machines
 * ** Trademark of HCL Technologies Limited
 */

 package com.urbancode.air.plugin.websphere.bpm

import com.urbancode.air.CommandHelper
import java.io.File
import java.io.BufferedWriter
import java.io.FileWriter
import java.text.SimpleDateFormat
import java.util.Date


class WebSphereBPMHelper {

    private final static TEMP_FILE_NAME = 'temp.py'

    static def verbose = false // Added for extra output during runWsadmin() calls
    /**
     * @param verbose Boolean for extra output setting
     * @return Sets the static verbose variable to display extra output
     */
    public static setVerbose(def verbose) {
        this.verbose = verbose
    }

    /**
     * @param props Contains the port, user, host, password, connType, profileRoot,
     *        and useSOAPProps properties specified by the user
     * @param isWindows Boolean determining whether the agent is windows or not
     * @return The result String List used to connect and run a BPM command on the BPM server
     */
    public static ArrayList createCommandArgs(def props, def isWindows) {
        setVerbose(Boolean.valueOf(props['verbose']))

        def wsadmin = isWindows ? "wsadmin.bat" : "wsadmin.sh"
        def port = props['port']
        def user = props['user']
        def host = props['host']
        def password = props['password']
        def useSOAPProps = Boolean.valueOf(props['useSOAPProps'])
        def connType = props['connType']
        def profHome = props['profileRoot']
        if (!(profHome.endsWith("\\") || profHome.endsWith("/"))) {
            profHome = profHome + File.separator
        }
        def commandPath = profHome + "bin" + File.separator + wsadmin
        def result = [
            commandPath,
            "-lang",
            "jython"
        ];
        result << "-conntype"
        result << connType.trim()
        result << "-port"
        result << port;

        if (useSOAPProps && (user || password)) {
            println "[Warning] Ignoring provided username '${user}' and password '****' because 'Use soap.client.props file' is enabled."
        }

        if (!useSOAPProps) {
            if (user && password) {
                result << "-user"
                result << user
                result << "-password"
                result << password
            }
            else {
                println "[Warning] 'Use soap.client.props file' is disabled, but User Name and Password were not provided."
            }
        }

        if (host) {
            result << "-host"
            result << host
        }

        result << "-f"
        result << TEMP_FILE_NAME

        return result
    }

    /**
     * @param invoke The BPMCommand to be run
     * @param commandArgs Connect to the BPM server and run the BPMCommand
     * @param isWindows Add specific Windows variable is necessary
     * @param ch Command Helper object used to run the BPMCommand
     * @return The success or failure status of the step
     */
    public static int invokeCommand(String invoke, def commandArgs, def isWindows, def ch) {
        int result = 0

        try {
            File tempFile = new File(TEMP_FILE_NAME)
            tempFile.deleteOnExit()
            BufferedWriter temp = new BufferedWriter(new FileWriter(tempFile))
            temp.write(invoke, 0, invoke.length())
            println "invoke: " + invoke
            temp.close()
            if (isWindows) {
                ch.addEnvironmentVariable("PROFILE_CONFIG_ACTION", "true")
            }
            ch.runCommand(invoke, commandArgs)
        }
        catch (IOException ioe) {
            result = 1
            ioe.printStackTrace()
        }
        catch (Exception e) {
            e.printStackTrace()
            result = -1
        }

        return result
    }

    /**
     * @param container The name of the process application
     * @param commandArgs The args for the wsadmin command
     * @return The acronym/short name of the application
     *         If the acronym itself is passed to the method, it is returned as is.
     */
    public static String getContainerAcronym(def container, def commandArgs) {
        def command = "AdminTask.BPMListProcessApplications()"
        return getAcronym(command, commandArgs, container)
    }

    /**
     * @param snapshot The name of the process application snapshot/track
     * @param container The name of the process application
     * @param commandArgs The args for the wsadmin command
     * @return The acronym/short name of the application snapshot/track.
     *         If the acronym itself is passed to the method, it is returned as is.
     */
    public static String getSnapshotTrackAcronym(def snapshot, def container, def commandArgs) {
        def command = "AdminTask.BPMShowProcessApplication('[-containerAcronym " + container + "]')"
        return getAcronym(command, commandArgs, snapshot)
    }

    /**
     * @param command The wsadmin command to invoke to find the acronym
     * @param commandArgs The args for the wsadmin command
     * @param name The name of the process application/snapshot/track
     * @return The acronym/short name of the application/snapshot/track.
     *         If the acronym itself is passed to the method, it is returned as is.
     */
    private static String getAcronym(def command, def commandArgs, def name) {
        String result = name
        println "\n[Action] Searching for '${name}'..."
        def wsadminOutput = runWsadmin(command, commandArgs)
        def pattern = "Name:\\s*(${name})[\\\\n|\\n|\\\\t|\\t|\\s]+(Track\\s*)?Acronym:\\s*([^\\\\|\\n|\\t|\\s]+)[\\\\|\\n|\\t|\\s]+"
        def matcher = wsadminOutput =~ pattern
        if (matcher.find()) {
            if (matcher.group(1) == name) {
                result = matcher.group(3)
                println "[OK] Acronym '${result}' found using '${name}'."
            }
        }
        else {
            pattern = "Acronym:\\s*(${name})[\\\\|\\n|\\t|\\s]+"
            matcher = wsadminOutput =~ pattern
            if (matcher.find()) {
                if (matcher.group(1) == name) {
                    println "[OK] Acronym '${name}' confirmed."
                }
            } else {
                println "[Warning] The acronym for name '${name}' was not found."
            }
        }

        return result
    }

    /**
     *
     * @param command The wsadmin command to invoke to find the acronym
     * @param commandArgs The args for the wsadmin command
     * @return Runs the wsadmin command and returns its output logs
     */
    private static String runWsadmin(def command, def commandArgs) {
        File tempFile = new File(TEMP_FILE_NAME)
        tempFile.deleteOnExit();
        BufferedWriter temp = new BufferedWriter(new FileWriter(tempFile))
        temp.writeLine("print " + command)
        temp.close()

        def systemOutput = ""

        CommandHelper ch = new CommandHelper(new File("."))
        ch.runCommand(command, commandArgs, {Process proc ->
            def out = new PrintStream(System.out, true)
            def outputStream = new StringBuilder()
            try {
                proc.waitForProcessOutput(outputStream, out)
            }
            finally {
                out.flush()
            }
            systemOutput = outputStream.toString()
        })

        if (verbose) {
            println "=========================================="
            println "[Verbose Output] wsadmin.sh Call... " + commandArgs.join(" ").toString()
            println "=========================================="
            println "[Verbose Output] ${TEMP_FILE_NAME} Contents... " + tempFile.text
            println "=========================================="
            println "[Verbose Output] Command Line Output... " + systemOutput
            println "=========================================="
            File saveOutput = File.createTempFile("wsadmin", ".txt", new File("."))
            saveOutput << systemOutput
            println "Saving command line output to file: ${saveOutput.getAbsolutePath()}"
        }
        tempFile.delete()
        return systemOutput
    }

    /**
     * @param inputFile User input for the location of file
     * @param workDir The specified working directory
     * @return Full path of where the File exists, else return empty string
     */
    private static String getFilePath(def inputFile, def workDir) {
        def result = ""

        def workDirString = workDir.toString() + File.separator + inputFile
        File workDirFile = new File(workDirString)

        File absFile = new File(inputFile)

        if (workDirFile.isFile()) {
            result = workDirString
            println "${inputFile} found in the working directory."
        }
        else if (absFile.isFile()) {
            result = inputFile
            println "${inputFile} found using the absolute path."
        }
        else {
            println "${inputFile} as not found in the working directory nor using its absolute path."
        }
        return result
    }

    /**
     * @param inputFile User input for the file name
     *        Assumes file is located in working directory
     * @param workDir The specified working directory
     * @return Full path of where the file may have been
     */
    private static String removeFileReturnPath(def inputFile, def workDir) {
        File f = new File(inputFile)
        if (f.isFile()) {
            f.delete()
        }
        return f.getCanonicalPath()
    }

    /**
     * @param inputFile User input for the file name
     *        Assumes file is being placed in working directory
     * @param workDir The specified working directory
     * @return Full inputFile path with date appended
     */
    private static String createUniqueFileName(def inputFile, def workDir) {
        Date d = new Date()
        SimpleDateFormat format = new SimpleDateFormat("MM-dd-YYYY_hh:mm:ss")
        def path = workDir.toString() + File.separator + inputFile + "_" + format.format(d)
        return path
    }
}
