/*
 * Licensed Materials - Property of IBM* and/or HCL**
 * UrbanCode Deploy
 * UrbanCode Build
 * UrbanCode Release
 * AnthillPro
 * (c) Copyright IBM Corporation 2011, 2017. All Rights Reserved.
 * (c) Copyright HCL Technologies Ltd. 2018. 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.ibm.urbancode.zos.common

import com.ibm.jzos.ZFile;
import com.ibm.jzos.ZFileException;
import com.ibm.team.enterprise.deployment.toolkit.manifest.DeployMVS;
import com.ibm.team.enterprise.deployment.toolkit.manifest.BackupMVS;

import java.io.BufferedReader;
import java.io.File;
import java.io.FileReader;
import java.io.IOException;

class DeploymentHelper {
    def static final filesCanBeDeletedFromWorkDir = new HashSet();
    static{
        filesCanBeDeletedFromWorkDir.add(DeploymentConstants.PACKAGE_MANIFEST_FILE_NAME);
        filesCanBeDeletedFromWorkDir.add(DeploymentConstants.ROLLBACK_MANIFEST_FILE_NAME);
        filesCanBeDeletedFromWorkDir.add(DeploymentConstants.DEPLOY_DELTA_FILE_NAME);
        filesCanBeDeletedFromWorkDir.add(DeploymentConstants.DEPLOY_BACKUP_FILE_NAME);
        filesCanBeDeletedFromWorkDir.add(DeploymentConstants.CONTAINER_MAPPER_FILE_NAME);
        filesCanBeDeletedFromWorkDir.add(DeploymentConstants.CHECK_ACCESS_DS_FILE_NAME_BACKUP);
        filesCanBeDeletedFromWorkDir.add(DeploymentConstants.CHECK_ACCESS_DS_FILE_NAME);
        filesCanBeDeletedFromWorkDir.add(DeploymentConstants.PACKAGE_FILE_NAME);
    }

    static getStringInput(input) {
        return input?.trim();
    }

    static getBooleanInput(input) {
        return Boolean.valueOf(input?.trim());
    }

    //Repository path not located in the component name folder, we need to add this folder on the path
    static getVersionDirPathInRepository(basePath, componentName, versionName){
        def repobasePathToVersion = basePath + File.separator + componentName + File.separator + versionName;
        return(repobasePathToVersion);
    }

    static getFilePathInRepository(basePath, componentName, versionName, fileName) {
        def patToFileInRepo = getVersionDirPathInRepository(basePath,componentName,versionName) + File.separator + fileName;
        return(patToFileInRepo);
    }

    //compatible=true, the return path will first check if <basepath>/deploy/component/version exists. and then the new path since v18
    //compatible=false, the return path will return the new path since v18, which is <basepath>/deploy/resourceId/component/version
    static getVersionPathInDeployBasePath(basePath, resourceId, componentName, versionName, compatible){
        def basePathToVersion = basePath + File.separator +
                DeploymentConstants.ADDITIONAL_FOLDER_FOR_BASE_PATH + File.separator +
                resourceId + File.separator +
                componentName + File.separator +
                versionName;

        if (compatible && !directoryExist(basePathToVersion)) {
            basePathToVersion = basePath + File.separator +
                    DeploymentConstants.ADDITIONAL_FOLDER_FOR_BASE_PATH + File.separator +
                    componentName + File.separator +
                    versionName;
        }

        return(basePathToVersion);
    }

    static getComponentPathInDeployBasePath(basePath, resourceId, componentName, compatible){
        def basePathToComponent = basePath + File.separator +
                DeploymentConstants.ADDITIONAL_FOLDER_FOR_BASE_PATH + File.separator +
                resourceId + File.separator +
                componentName

        if (compatible && !directoryExist(basePathToComponent)) {
            basePathToComponent = basePath + File.separator +
                    DeploymentConstants.ADDITIONAL_FOLDER_FOR_BASE_PATH + File.separator +
                    componentName;
        }

        return(basePathToComponent);
    }

    static getFilePathInDeployBasePath(versionPathInBasePah, fileName) {
        def patToFileInBasePath = versionPathInBasePah + File.separator + fileName;
        return(patToFileInBasePath);
    }

    //Working copy default path is located in the component folder
    static getVersionDirPathInWorkingDir(basePath, versionname, resourceId){
        return(basePath + File.separator + resourceId + File.separator + versionname);
    }

    static getFilePathInWorkingDir(basePath, versionname, resourceId, fileName){
        def pathToFileInWorkDir = getVersionDirPathInWorkingDir(basePath, versionname, resourceId) + File.separator + fileName
        return(pathToFileInWorkDir);
    }

    static getPath4ExecScriptInToolkit(toolkitHome, execName){
        return(toolkitHome + File.separator + DeploymentConstants.ZOS_TOOLKIT_EXEC_FOLDER_NAME + File.separator + execName);
    }

    static getPath4Checkaccess(toolkitHome, execName){
        def agentBinHome= DeploymentHelper.getStringInput(System.getenv().get("AGENT_BIN_HOME"));
        if (agentBinHome) {
            //6.2.5 new agent checkaccess is installed to <agent bin home>/auth/checkaccess
            return(agentBinHome + "/auth/checkaccess");
        }else if(new File(toolkitHome + "/auth/checkaccess").exists()){
            //upgrade to 6.2.5
            return (toolkitHome + "/auth/checkaccess");
        }else{
            //before 6.2.5, checkaccess is installed to <agent home>/bin/checkaccess
            return getPath4ExecScriptInToolkit(toolkitHome, execName);
        }
    }

    //Get ant script,will depends on our pacakge structure
    static getAntScriptFolderPath(){
        final def pluginHome = System.getenv("PLUGIN_HOME");
        return(pluginHome + File.separator + "scripts" + File.separator + "deployment");
    }

    //Validation methods
    static validateFileExist(filePath, errorInfo){
        def file = new File(filePath);
        if( !(file.exists() && file.isFile()) ){
            throw new IllegalArgumentException(errorInfo);
        }
    }

    static validateDirectoryExist(filePath, errorInfo){
        def file = new File(filePath);
        if( !(file.exists() && file.isDirectory()) ){
            throw new IllegalArgumentException(errorInfo);
        }
    }

    static directoryExist(directoryPath){
        def file = new File(directoryPath);
        if(file.exists() && file.isDirectory()){
            return(true);
        }

        return(false);
    }

    static fileExists(filePath){
        def fileExist = false;
        def file = new File(filePath);
        if( file.exists() && file.isFile() ){
            fileExist = true;
        }

        return(fileExist);
    }

    //Handle user's input rule mapping pair
    static handleMappingRule(pdsMapping, Map src2TargetMap, Map wildcardSrc2TargetMap) {
        if(pdsMapping && pdsMapping.size() > 0){
            def pdsMappingStr = "";
            pdsMappingStr = pdsMapping.trim();
            pdsMappingStr.replaceAll(DeploymentConstants.SYSTEM_LINE_SEPARATOR, DeploymentConstants.SRC_TO_TARGET_DELIMETER);
            def pdsMappingPairs = pdsMappingStr.split(DeploymentConstants.SRC_TO_TARGET_DELIMETER);
            pdsMappingPairs.each {pair->
                pair = pair.trim();
                if(pair.length() > 0){//Allow empty line which will be ignore
                    def onePair = pair.split(DeploymentConstants.MAPPING_RULER_DELIMETER);
                    onePair = onePair*.trim();
                    if(onePair.size() == 2){
                        if(containsWildcardCharactor(onePair[0])) {
                            def wildcardExpressKey = getKeyRegularExpressPattern(onePair[0]);
                            if(!wildcardSrc2TargetMap.containsKey(wildcardExpressKey)){
                                wildcardSrc2TargetMap.put(wildcardExpressKey, onePair[1]);
                            }else{
                                println "Warning - The mapping rule ${onePair[0]}, ${onePair[1]} is ignored,which was already set by ${onePair[0]}, ${src2TargetMap[onePair[0]]}";
                            }
                        } else {
                            if(!src2TargetMap.containsKey(onePair[0])){
                                src2TargetMap.put(onePair[0], onePair[1]);
                            }else{
                                println "Warning - The mapping rule ${onePair[0]}, ${onePair[1]} is ignored,which was already set by ${onePair[0]}, ${src2TargetMap[onePair[0]]}";
                            }
                        }
                    }else{//abc,cc,dd or abc, or ,cc will be treat as illegal input
                        throw new IllegalArgumentException("The format of ${pair} PDS mapping field is not correctly!");
                    }
                }
            }
        }
    }

    //We just support "*" now
    static containsWildcardCharactor(str) {
        return(str.contains("*") || str.contains("%"));
    }

    //return an pattern which removed "." and change the "*" to ".*"
    static getKeyRegularExpressPattern(str) {
        def pattern = ~str.replaceAll(/\./, "\\\\.").replaceAll(/\*/, ".*").replaceAll(/\%/, ".");
        return(pattern);
    }
    //Check access

    static checkAccess4Input(Set targetDsSet, dsListFile4BackUpPath, checkAccessExePath) {


        def InputTargetVolumeMap = [:];
        targetDsSet.each { datasetName->
            def dsn = "'" + datasetName + "'";
            def exists = dsExist(dsn);
            try {
                def vols = ZFile.locateDSN(dsn);
                if(null == vols || vols.size()<1){
                    printProcessOutput("Warning - Input dataset ${dsn} is not found!")
                }else if( vols.size() > 1){
                }else{
                    InputTargetVolumeMap.put(datasetName, vols[0]);
                }
            }catch(Exception locateException){
                //Can't find the the dsn
            }
        }
        //Generate the datasetlist file to call native checkaccess for deploy
        new File(dsListFile4BackUpPath).withWriter('IBM-1047'){ out ->
            InputTargetVolumeMap.each {datasetName,volume->
                out.println("${DeploymentConstants.CHECK_ACCESS_DS_INPUT_LEADER} ${datasetName} ${volume}");
            }
        }
        //Call native script to get the access code
        callCheckAccess(checkAccessExePath, dsListFile4BackUpPath, "input")
    }

    static checkAccess4Output(Set targetDsSet, dsListFilePath, checkAccessExePath) {

        def OutPutTargetVolumeMap = [:];
        targetDsSet.each { datasetName->
            def dsn = "'" + datasetName + "'";
            def exists = dsExist(dsn);
            if(!exists){
                OutPutTargetVolumeMap.put(datasetName, "******");
            }else{
                def vols = null;
                try{
                    vols = ZFile.locateDSN(dsn);
                }catch(Exception locateException){
                    println "The volumn of ${dsn} was not found for OUTPUT!";
                }

                if(null == vols || vols.size()<1){
                    OutPutTargetVolumeMap.put(datasetName, "******");
                }else if( vols.size() > 1){
                }else{
                    OutPutTargetVolumeMap.put(datasetName, vols[0]);
                }
            }
        }
        //Generate the datasetlist file to call native checkaccess for deploy
        new File(dsListFilePath).withWriter('IBM-1047'){ out ->
            OutPutTargetVolumeMap.each {datasetName,volume->
                out.println("${DeploymentConstants.CHECK_ACCESS_DS_OUTPUT_LEADER} ${datasetName} ${volume}");
            }
        }
        callCheckAccess(checkAccessExePath, dsListFilePath, "output")
    }

    static checkDatasetsExist(Set targetDsSet) {
        def allExist = true;
        targetDsSet.each { datasetName->
            def dsn = "'" + datasetName + "'";
            def exists = dsExist(dsn);
            if(!exists){
                allExist = false;
            }
        }
        return (allExist);
    }

    static int getInstalledVersion() {
        def agentHome= DeploymentHelper.getStringInput(System.getenv().get("AGENT_HOME"));
        def agentInstalledVersion=agentHome+File.separator+"conf"+File.separator+"installed.version";
        File file = new File(agentInstalledVersion)
        String utf8Content = file.getText("UTF-8");
        int indexOfequalTo= utf8Content.indexOf("=");
        String temp=utf8Content.substring(indexOfequalTo+1);
        String temp2 = temp.replaceAll("[.]","");
        String installedVersion=temp2.substring(0, 3);
        int installedVersionNum=Integer.parseInt(installedVersion);
        return installedVersionNum;

    }

    static callCheckAccess(checkAccessExePath, dsListFilePath, ioType) {

        def accessResultProcess = ["${checkAccessExePath}", "-d", "${dsListFilePath}"].execute();
        accessResultProcess.waitFor();
        printProcessOutput(accessResultProcess.text);
        def accessResult = accessResultProcess.exitValue();
        if(accessResult != 0){
            printActionTitle("checkaccess:rc=${accessResult}");
            if(137 == accessResult){
                printProcessOutput("checkaccess utility failed. Please verify that checkaccess utility is set as APF authorized by running command \"extattr +a checkaccess\"");
                System.exit(137);
            }
            throw new IOException("These Datasets you listed in PDS mapping can't be used as the ${ioType} of the program!");
        }
    }

    static dsExist(singleQuotedDsName){
        def isExist = false;
        def f = null;
        try {
            f = new ZFile("//" + singleQuotedDsName, "r");

            isExist = true;
        } catch(ZFileException e) {
            if(e.getErrno() == 92) {
                printProcessOutput("Data set $singleQuotedDsName could not be opened for read.")
            }else if (e.getErrno() == 49) {
                printProcessOutput("Data set $singleQuotedDsName could not be located.")
            }else{
                e.printStackTrace();
            }
            // Assume that the file does not exist in this case.
        } finally {
            if (null != f) {
                f.close();
            }
        }

        return(isExist);
    }

    //Clean up after deploy or rollback
    static cleanWorkDir(workdir){
        def versionWorkingDirectory = new File(workdir);
        if (versionWorkingDirectory.exists() && versionWorkingDirectory.isDirectory()) {
            versionWorkingDirectory.deleteDir();
        }
    }

    //Clean up back up data
    static cleanBackUpData(deployBasePath) {
        cleanWorkDir(deployBasePath);
    }

    //Verify copy/ftp result
    static verifyGetArtifectResult(filePath, errorInfo){
        def file = new File(filePath);
        if(!file.exists() || !file.isFile()){
            throw new IOException(errorInfo);
        }
    }

    //Not null or empty input checking
    static inputNotEmptyCheck(input, errorInformation) {
        if (null == input || input.length()<=0) {
            throw new IllegalArgumentException(errorInformation);
        }
    }


    //Added for HFS files
    //Handle user's input rule mapping pair
    static handleMappingRuleHfsTagrgetDir(hfsTargetDir, src2TargetMapHfsTagrgetDir, containsWildcardMapHfsTagrgetDir) {
        if(hfsTargetDir && hfsTargetDir.size() > 0){
            def hfsTargetDirStr = "";
            hfsTargetDirStr = hfsTargetDir.trim();
            hfsTargetDirStr.replaceAll(DeploymentConstants.SYSTEM_LINE_SEPARATOR, DeploymentConstants.SRC_TO_TARGET_DELIMETER);
            def hfsMappingPairs = hfsTargetDirStr.split(DeploymentConstants.SRC_TO_TARGET_DELIMETER);
            hfsMappingPairs.each {pair->
                pair = pair.trim();
                if(pair.length() > 0){//Allow empty line which will be ignore
                    def onePair = pair.split(DeploymentConstants.MAPPING_RULER_DELIMETER);
                    onePair = onePair*.trim();
                    if(onePair.size() == 2){
                        // if onePair[0] doesnt start with /, add / to it.
                        // if onePair[1] doesnt start with /, add / to it.
                        if (onePair[0].charAt(0)=='/')
                        {
                            onePair[0]=onePair[0];
                        }
                        else{
                            onePair[0]='/'+onePair[0];
                        }

                        if (onePair[1].charAt(0)=='/')
                        {
                            onePair[1]=onePair[1];
                        }
                        else{
                            onePair[1]='/'+onePair[1];
                        }
                        if(containsWildcardCharactor(onePair[0])) {
                            def wildcardExpressKey = getKeyRegularExpressPattern(onePair[0]);
                            if(!containsWildcardMapHfsTagrgetDir.containsKey(wildcardExpressKey)){
                                containsWildcardMapHfsTagrgetDir.put(wildcardExpressKey, onePair[1]);
                            }else{
                                println "Warning - HFS The mapping rule ${onePair[0]}, ${onePair[1]} is ignored,which was already set by ${onePair[0]}, ${src2TargetMapHfsTagrgetDir[onePair[0]]}";
                            }
                        } else {
                            if(!src2TargetMapHfsTagrgetDir.containsKey(onePair[0])){
                                src2TargetMapHfsTagrgetDir.put(onePair[0], onePair[1]);
                            }else{
                                println "Warning - HFS The mapping rule ${onePair[0]}, ${onePair[1]} is ignored,which was already set by ${onePair[0]}, ${src2TargetMapHfsTagrgetDir[onePair[0]]}";
                            }
                        }
                    }else{//abc,cc,dd or abc, or ,cc will be treat as illegal input
                        throw new IllegalArgumentException("The format of ${pair} HFS mapping field is not correctly!");
                    }
                }
            }
        }
    }

    static doDeploymentAction(deployBasePath, tempDsnPrefix, resourceId, componentName, versionName, versioinType, zosScriptPath, componentVersionWorkingDir, backupPds, hfsTargetDir, versionMerge,oldVersionIdentifier,deleteCurrentContents) {

        def versionPathInDeployBase = getVersionPathInDeployBasePath(deployBasePath, resourceId, componentName, versionName, false);
        //Prepare to deploy
        preDeploy(versionPathInDeployBase, versionName);
        //Back Up Pds before deploy current version for rollback
        if (!"FULL".equalsIgnoreCase(versioinType)) {
            backUpPdsBeforeDeployment(zosScriptPath, componentVersionWorkingDir, DeploymentConstants.ZOS_TOOLKIT_TRACE_ON, backupPds, hfsTargetDir,oldVersionIdentifier);
        } else {
            printProcessOutput("Backup is skipped for full version");
        }
        //Do the deployment
        deploy(zosScriptPath, componentVersionWorkingDir, DeploymentConstants.ZOS_TOOLKIT_TRACE_ON, tempDsnPrefix, hfsTargetDir, versionMerge,oldVersionIdentifier,deleteCurrentContents);
        //Post deployment
        postDeploy(componentVersionWorkingDir, versionPathInDeployBase, backupPds,oldVersionIdentifier);
    };




    //Prepare to deploy
    static preDeploy(versionDeploybasePath, versionName) {
        printActionTitle("PreDeploy:");
        if(!directoryExist(versionDeploybasePath)) {
            printProcessOutput("Creating directory:" + versionDeploybasePath);
            new File(versionDeploybasePath).mkdirs();
        }
        println "";
    }

    //Call this to do pds back up before deployment, and the backup result will be stored in the working directory
    static backUpPdsBeforeDeployment(ispfScriptPath, componentVersionWorkingDir, traceOn, backupPds, hfsTargetDir,oldVersionIdentifier) {

        printActionTitle("Backup:");
        if(backupPds){
            def manifestFilePath = componentVersionWorkingDir + File.separator + DeploymentConstants.PACKAGE_MANIFEST_FILE_DEPLOY_NAME;
            def containerMapperFilePath = componentVersionWorkingDir + File.separator + DeploymentConstants.CONTAINER_MAPPER_FILE_NAME;
            def containerMapperHFSFilePath = componentVersionWorkingDir + File.separator + DeploymentConstants.CONTAINER_MAPPER_HFS_FILE_NAME;
            def timestamp = new Date().getTime().toString();
            def pkgManifestHelper = new PackageManifestXMLHelper();
            def hasDataSets = pkgManifestHelper.hasDataSets(manifestFilePath);
            def hasHFSFiles = pkgManifestHelper.hasHFSFiles(manifestFilePath);

            /* boolean isSecondVer = false;
             isSecondVer = isSecondVersion(manifestFilePath); */


            if(hasDataSets){
                def backUpPdsProcess = ["${ispfScriptPath}", "${DeploymentConstants.ZOS_TOOLKIT_BACKUP_COMMAND}", "${manifestFilePath}", "${componentVersionWorkingDir}", "${DeploymentConstants.DEPLOY_BACKUP_FILE_NAME}", "${containerMapperFilePath}", "${DeploymentConstants.ZOS_TOOLKIT_BUILDER_VERSION}", "${DeploymentConstants.ZOS_TOOLKIT_RUNTIME_MINIMAL_VERSION}", "${timestamp}", "${traceOn}"].execute();

                backUpPdsProcess.waitFor();

                def backUpPdsResult = backUpPdsProcess.exitValue();

                if(backUpPdsResult > 0) {
                    printProcessOutput(backUpPdsProcess.text);
                    printProcessOutput("The backup failed to complete. Look at the CDATA section in previous messages for the failure reason. Check for REASON-CODE entries if they exist.");
                    System.exit(1);
                } else {
                    printProcessOutput(backUpPdsProcess.text);
                }
            }
            /* else {
             BackupMVS backupMVS = new BackupMVS(manifestFilePath, componentVersionWorkingDir,
             containerMapperFilePath,DeploymentConstants.DEPLOY_BACKUP_FILE_NAME);
             backupMVS.BackupTargetFiles(); 
             } */

            if(hasHFSFiles){
                def ant = new AntBuilder();
                ant.taskdef(name:"backUpHFSFiles", classname:"com.ibm.team.enterprise.deployment.hfs.BackUpHFSFilesTask");
                //ant.property( name: "team.enterprise.deployment.hfs.runtimeRoot.dir", hfsTargetDir );

                def int installedVersion=getInstalledVersion();

                if(installedVersion<703){
                    try {
                        ant.backUpHFSFiles (
                                manifest: manifestFilePath,
                                outputDir: componentVersionWorkingDir,
                                outputFile : DeploymentConstants.DEPLOY_BACKUP_FILE_NAME,
                                rootDir: hfsTargetDir
                                );

                    }
                    catch (Exception e) {
                        e.printStackTrace();
                        System.exit(1);
                    }
                }



                else if (installedVersion==703 || installedVersion ==704) {

                    try {
                        ant.backUpHFSFiles (
                                manifest: manifestFilePath,
                                outputDir: componentVersionWorkingDir,
                                outputFile : DeploymentConstants.DEPLOY_BACKUP_FILE_NAME,
                                rootDir: hfsTargetDir,
                                containerMapperFileBkp:containerMapperHFSFilePath
                                );
                    }
                    catch (Exception e) {
                        e.printStackTrace();
                        System.exit(1);
                    }
                }


                else {
                    try {
                        ant.backUpHFSFiles (
                                manifest: manifestFilePath,
                                outputDir: componentVersionWorkingDir,
                                outputFile : DeploymentConstants.DEPLOY_BACKUP_FILE_NAME,
                                rootDir: hfsTargetDir,
                                containerMapperFileBkp:containerMapperHFSFilePath,
                                oldVersionIdentifier:oldVersionIdentifier

                                );
                    }
                    catch (Exception e) {
                        e.printStackTrace();
                        System.exit(1);
                    }
                }



            }
        }else{
            printProcessOutput("Backup is skipped. ");
        }
        println ""
    }

    //Call this method to do deploy, which will use those artifacts of a version and thos mapping file to move member into target datset
    static deploy(ispfScriptPath, componentVersionWorkingDir, traceOn, tempDsnPrefix, hfsTargetDir, versionMerge,oldVersionIdentifier,deleteCurrentContents) {
        printActionTitle("Deploy:");
        println "";
        def manifestFilePath = componentVersionWorkingDir + File.separator + DeploymentConstants.PACKAGE_MANIFEST_FILE_DEPLOY_NAME;
        def packageZipFilePath = componentVersionWorkingDir + File.separator + DeploymentConstants.PACKAGE_FILE_NAME;
        def containerMapperFilePath = componentVersionWorkingDir + File.separator + DeploymentConstants.CONTAINER_MAPPER_FILE_NAME;
        def containerMapperHFSFilePath = componentVersionWorkingDir + File.separator + DeploymentConstants.CONTAINER_MAPPER_HFS_FILE_NAME;
        def uninstallFile = "";//Just align with the interface used in zosdeploy.xml
        def pkgManifestHelper = new PackageManifestXMLHelper();
        def hasDataSets = pkgManifestHelper.hasDataSets(manifestFilePath);
        def hasHFSFiles = pkgManifestHelper.hasHFSFiles(manifestFilePath);

        if(hasDataSets){
            printActionTitle("Deploy data sets:");
            def result = 0;
            if(versionMerge) {
                //extract every original version
                def zipFile = new java.util.zip.ZipFile(packageZipFilePath)
                zipFile.entries().each {
                    //deploy each manifest.
                    if(it.name.startsWith("packageManifest_")) {
                        def endpos = it.name.indexOf(".xml");
                        def versionID = it.name.substring(16, endpos);
                        def innerManifestFileEntry = it
                        def innerPackageZipFileName = "package_" +versionID + ".zip"
                        def InnComponentVersionWorkingDir = componentVersionWorkingDir + File.separator + versionID
                        new File(InnComponentVersionWorkingDir).mkdirs();
                        def innerManifestFilePath = InnComponentVersionWorkingDir + File.separator + "packageManifest.xml"

                        printProcessOutput("Deploying data sets from original version : ${versionID} ");
                        def input
                        def output

                        input = zipFile.getInputStream(new java.util.zip.ZipEntry(innerPackageZipFileName));
                        if(input) { // some may not have package.zip, for example a version that only do deletes
                            output = (new File(InnComponentVersionWorkingDir,"package.zip").newDataOutputStream())
                            output << input
                            output.close()
                            input.close()
                        }
                        input = zipFile.getInputStream(innerManifestFileEntry);
                        output = (new File(InnComponentVersionWorkingDir,"packageManifest.xml").newDataOutputStream())
                        output << input
                        output.close()
                        input.close()

                        //compare with parent to keep deltas only
                        pkgManifestHelper.calculateIncrementalManifestForChild(manifestFilePath, InnComponentVersionWorkingDir + File.separator + "packageManifest.xml");
                        hasDataSets = pkgManifestHelper.hasDataSets(InnComponentVersionWorkingDir + File.separator + "packageManifest.xml");

                        if(hasDataSets){
                            result = deployOnePackage(ispfScriptPath, innerManifestFilePath, InnComponentVersionWorkingDir, containerMapperFilePath, uninstallFile, traceOn, tempDsnPrefix);
                            if(result > 0) {
                                System.exit(1);
                            }
                        }else{
                            printProcessOutput("Nothing to deploy from original version : ${versionID} \n ");

                        }
                    }
                }

            }else {
                result = deployOnePackage(ispfScriptPath, manifestFilePath, componentVersionWorkingDir, containerMapperFilePath, uninstallFile, traceOn, tempDsnPrefix);
            }
            if(result > 0) {
                System.exit(1);
            }
        }

        if(hasHFSFiles){
            printActionTitle("Deploy HFS files:");
            def ant = new AntBuilder();
            ant.taskdef(name:"deployHFSFiles", classname:"com.ibm.team.enterprise.deployment.hfs.DeployHFSFilesTask");
            //ant.property( name: "team.enterprise.deployment.hfs.runtimeRoot.dir", hfsTargetDir );
            def unpackDir = componentVersionWorkingDir + File.separator + DeploymentConstants.HFS_UNPACK_DIR;

            if(new File(packageZipFilePath).exists()){
                //unpack package.zip if exists
                ant.delete(dir  : unpackDir, quiet : "true");
                ant.mkdir (dir  : unpackDir);
                ant.untar (dest : unpackDir,
                src  : packageZipFilePath,
                overwrite : "true" );
            }
            def int installedVersion=getInstalledVersion();

            if(installedVersion<703){
                try {
                    ant.deployHFSFiles (
                            manifest: manifestFilePath,
                            outputDir: unpackDir,
                            targetDir:hfsTargetDir
                            );
                }
                catch (Exception e) {
                    e.printStackTrace();
                    System.exit(1);
                }
            }
            else if (installedVersion==703 || installedVersion ==704) {
                try {
                    ant.deployHFSFiles (
                            manifest: manifestFilePath,
                            outputDir: unpackDir,
                            containerMapperFile:containerMapperHFSFilePath,
                            vesrionIdentity:oldVersionIdentifier
                            );
                }
                catch (Exception e) {
                    e.printStackTrace();
                    System.exit(1);
                }
            } else {
                try {

                    ant.deployHFSFiles (
                            manifest: manifestFilePath,
                            outputDir: unpackDir,
                            containerMapperFile:containerMapperHFSFilePath,
                            oldVersionIdentifier:oldVersionIdentifier,
                            deleteCurrentContents:deleteCurrentContents
                            )
                }
                catch (Exception e) {
                    e.printStackTrace();
                    System.exit(1);
                }
            }

            ant.delete(dir  : unpackDir, quiet : "true");
            println "";
        }

    }



    static deployOnePackage(ispfScriptPath, manifestFilePath, componentVersionWorkingDir, containerMapperFilePath, uninstallFile, traceOn, tempDsnPrefix) {

        boolean isSecondVer = false;
        isSecondVer = isSecondVersion(manifestFilePath);

        if (!isSecondVer) {
            def deployPdsProcess = ["${ispfScriptPath}", "${DeploymentConstants.ZOS_TOOLKIT_DEPLOY_COMMAND}", "${manifestFilePath}", "${componentVersionWorkingDir}", "${DeploymentConstants.PACKAGE_FILE_NAME}", "${containerMapperFilePath}", "${DeploymentConstants.ZOS_TOOLKIT_BUILDER_VERSION}", "${DeploymentConstants.ZOS_TOOLKIT_RUNTIME_MINIMAL_VERSION}", "${uninstallFile}", "${traceOn}", "${tempDsnPrefix}"].execute();

            deployPdsProcess.waitFor();
            def deployPdsResult = deployPdsProcess.exitValue();
            if(deployPdsResult>0) {
                printProcessOutput(deployPdsProcess.text);
                printProcessOutput("The deployment failed to complete. Look at the CDATA section in previous messages for the failure reason. Check for REASON-CODE entries if they exist.");
                return deployPdsResult;
            }else{
                printProcessOutput(deployPdsProcess.text);
            }
            println "";
        } else {
            try {
                DeployMVS deployMVS = new DeployMVS(manifestFilePath,componentVersionWorkingDir,
                        containerMapperFilePath,tempDsnPrefix);
                String tempFileName = deployMVS.deployArtifacts();

                def deployPdsProcess = ["${ispfScriptPath}", "${DeploymentConstants.ZOS_TOOLKIT_DEPLOY_V2_COMMAND}", "${tempFileName}"].execute();

                deployPdsProcess.waitFor();
                def deployPdsResult = deployPdsProcess.exitValue();
                if(deployPdsResult>0) {
                    printProcessOutput(deployPdsProcess.text);
                    printProcessOutput("The deployment failed to complete. Look at the CDATA section in previous messages for the failure reason. Check for REASON-CODE entries if they exist.");
                    return deployPdsResult;
                }else{
                    printProcessOutput(deployPdsProcess.text);
                }
                println "";

            } catch (Exception e) {
                e.printStackTrace();
                System.exit(8);
            }

        }



    }

    //Copy those result file into deploy base path, which will be used to do rollback etc
    static postDeploy(componentVersionWorkingDir, versionDeploybasePath, backupPds,oldVersionIdentifier) {
        printActionTitle("PostDeploy:");

        if (backupPds) {
            def deployDeltaFile = new File(componentVersionWorkingDir + File.separator + DeploymentConstants.DEPLOY_DELTA_FILE_NAME);
            if(deployDeltaFile.exists()){
                //Print out the deploy result file contents
                printActionTitle("Deploy Report:");
                def deployResult = deployDeltaFile.getText(DeploymentConstants.ENCODING);
                printProcessOutput(deployResult);
            }
        }
        else {//The delta file generated by backup, so there will no delta file if no back up action taken.
            def packageManifestFile = new File(componentVersionWorkingDir + File.separator + DeploymentConstants.PACKAGE_MANIFEST_FILE_NAME);
            if(packageManifestFile.exists()){
                //Print out the deploy result file contents
                printActionTitle("Deployed Artifacts:");
                def deployArtifacts = packageManifestFile.getText(DeploymentConstants.ENCODING);
                printProcessOutput(deployArtifacts);
            }
        }


        println "";

        printActionTitle("Store deploy results:");
        def ant = new AntBuilder();
        copyFile(ant, componentVersionWorkingDir, versionDeploybasePath, DeploymentConstants.PACKAGE_MANIFEST_FILE_NAME, true);
        copyFile(ant, componentVersionWorkingDir, versionDeploybasePath, DeploymentConstants.DEPLOY_DELTA_FILE_NAME, DeploymentConstants.ROLLBACK_MANIFEST_FILE_NAME, true);
        copyFile(ant, componentVersionWorkingDir, versionDeploybasePath, DeploymentConstants.CONTAINER_MAPPER_FILE_NAME, true);
        copyFile(ant, componentVersionWorkingDir, versionDeploybasePath, DeploymentConstants.CHECK_ACCESS_DS_FILE_NAME, true);
        //Copy backup.zip
        copyFile(ant, componentVersionWorkingDir, versionDeploybasePath, DeploymentConstants.DEPLOY_BACKUP_FILE_NAME, true);
        //Copy containerMapperHFS
        if(!oldVersionIdentifier) {
            copyFile(ant, componentVersionWorkingDir, versionDeploybasePath, DeploymentConstants.TEMP_FILE_NEW_VERSION, true);
        }
        copyFile(ant, componentVersionWorkingDir, versionDeploybasePath, DeploymentConstants.CONTAINER_MAPPER_HFS_FILE_NAME, true);

        println "";
    }

    static postDeployForGenericOnly(deployBasePath, resourceId, componentName, versionName, componentVersionWorkingDir) {
        printActionTitle("PostDeploy:");
        def versionPathInDeployBase = getVersionPathInDeployBasePath(deployBasePath, resourceId, componentName, versionName, false);

        def ant = new AntBuilder();
        copyFile(ant, componentVersionWorkingDir, versionPathInDeployBase, DeploymentConstants.PACKAGE_MANIFEST_FILE_NAME, true);

        println "";
    }

    //Call this method to do deploy, which will use those artifacts of a version and thos mapping file to move member into target datset
    static rollback(ispfScriptPath, componentVersionWorkingDir, traceOn, tempDsnPrefix,oldVersionIdentifier,hfsTargetDir) {
        printActionTitle("Rollback:");
        def rollbackManifestFilePath = componentVersionWorkingDir + File.separator + DeploymentConstants.ROLLBACK_MANIFEST_FILE_NAME;
        def containerMapperFilePath = componentVersionWorkingDir + File.separator + DeploymentConstants.CONTAINER_MAPPER_FILE_NAME;
        def containerMapperHFSFilePath = componentVersionWorkingDir + File.separator + DeploymentConstants.CONTAINER_MAPPER_HFS_FILE_NAME;
        def uninstallFile = "";//Just align with the interface used in zosdeploy.xml
        def backupZipFilePath = componentVersionWorkingDir + File.separator + DeploymentConstants.DEPLOY_BACKUP_FILE_NAME;
        def boolean tempDeleteCurrFlag=false;



        printProcessOutput(new File(rollbackManifestFilePath).getText(DeploymentConstants.ENCODING));
        println "";

        def pkgManifestHelper = new PackageManifestXMLHelper();
        def hasDataSets = pkgManifestHelper.hasDataSets(rollbackManifestFilePath);
        def hasHFSFiles = pkgManifestHelper.hasHFSFiles(rollbackManifestFilePath);

        if(hasDataSets){
            printActionTitle("Rollback data sets:");
            def rollbackPdsProcess = ["${ispfScriptPath}", "${DeploymentConstants.ZOS_TOOLKIT_ROLLBACK_COMMAND}", "${rollbackManifestFilePath}", "${componentVersionWorkingDir}", "${DeploymentConstants.DEPLOY_BACKUP_FILE_NAME}", "${containerMapperFilePath}", "${DeploymentConstants.ZOS_TOOLKIT_BUILDER_VERSION}", "${DeploymentConstants.ZOS_TOOLKIT_RUNTIME_MINIMAL_VERSION}", "${uninstallFile}", "${traceOn}", "${tempDsnPrefix}"].execute();

            rollbackPdsProcess.waitFor();
            def deployPdsResult = rollbackPdsProcess.exitValue();
            if(deployPdsResult>0) {
                printProcessOutput(rollbackPdsProcess.text);
                printProcessOutput("The rollback failed to complete. Look at the CDATA section in previous messages for the failure reason. Check for REASON-CODE entries if they exist.");
                System.exit(1);
            } else {
                printProcessOutput(rollbackPdsProcess.text);
            }
            println "";
        }

        if(hasHFSFiles){
            printActionTitle("Rollback HFS files:");
            def ant = new AntBuilder();
            ant.taskdef(name:"deployHFSFiles", classname:"com.ibm.team.enterprise.deployment.hfs.DeployHFSFilesTask");
            //ant.property( name: "team.enterprise.deployment.hfs.runtimeRoot.dir", hfsTargetDir );
            def unpackDir = componentVersionWorkingDir + File.separator + DeploymentConstants.HFS_UNPACK_DIR;


            if(new File(backupZipFilePath).exists()){
                //unpack package.zip if exists
                ant.delete(dir  : unpackDir, quiet : "true");
                ant.mkdir (dir  : unpackDir);
                ant.untar (dest : unpackDir,
                src  : backupZipFilePath,
                overwrite : "true" );
            }

            def int installedVersion=getInstalledVersion();
            try {
                if(installedVersion<703){
                    ant.property( name: "team.enterprise.deployment.hfs.runtimeRoot.dir", hfsTargetDir );
                    ant.deployHFSFiles (
                            manifest: rollbackManifestFilePath,
                            outputDir: unpackDir
                            );

                }
                else if (installedVersion==703 || installedVersion ==704) {
                    ant.deployHFSFiles (
                            manifest: rollbackManifestFilePath,
                            outputDir: unpackDir,
                            containerMapperFile:containerMapperHFSFilePath,
                            vesrionIdentity:oldVersionIdentifier
                            );
                } else {
                    ant.deployHFSFiles (
                            manifest: rollbackManifestFilePath,
                            outputDir: unpackDir,
                            containerMapperFile:containerMapperHFSFilePath,
                            oldVersionIdentifier:oldVersionIdentifier ,
                            deleteCurrentContents:tempDeleteCurrFlag
                            )
                }
            }
            catch (Exception e) {
                e.printStackTrace();
                System.exit(1);
            }

            ant.delete(dir  : unpackDir, quiet : "true");
            println "";
        }
    }



    private static printProcessOutput(text) {
        text.eachLine { it->
            println("\t" + it);
        }
    }

    static printActionTitle(text) {
        println "  "+text;
    }

    static copyFile(antbuilder, componentVersionWorkingDir, versionDeploybasePath, fileName,verbose) {
        def src = componentVersionWorkingDir + File.separator + fileName;
        def target = versionDeploybasePath + File.separator + fileName;
        if(fileExists(src)){
            antbuilder.copy(verbose: verbose, file:"${src}", tofile:"${target}",overwrite: "true");
        }
    }

    static copyFile(antbuilder, componentVersionWorkingDir, versionDeploybasePath, fileName, targetFilename, verbose) {
        def src = componentVersionWorkingDir + File.separator + fileName;
        def target = versionDeploybasePath + File.separator + targetFilename;
        if(fileExists(src)){
            antbuilder.copy(verbose: verbose, file:"${src}", tofile:"${target}", overwrite:"true" );
        }
    }

    static boolean isSecondVersion(String manifestFilePath) {
        File verFile=new File(manifestFilePath);
        FileReader fr = new FileReader(verFile);
        BufferedReader br = new BufferedReader(fr);
        boolean secondVersion = false;
        String splitStr;
        String[] words=null;
        String input="<manifest type=\"MANIFEST_LIST\" version=\"v2_0\"";
        while((splitStr=br.readLine())!=null)  {
            words=splitStr.split(">");
            for (String word : words)
            {
                if (word.contains(input)) {
                    secondVersion = true;
                    break;
                }
            }
        }
        fr.close();
        return secondVersion;
    }
}

