/*
* 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 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} PDS 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);
        } 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);
    };
    
    
    

    //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) {

        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 );
                
                ant.backUpHFSFiles (
                    manifest: manifestFilePath,
                    outputDir: componentVersionWorkingDir,
                    outputFile : DeploymentConstants.DEPLOY_BACKUP_FILE_NAME,
                    rootDir: hfsTargetDir ,
					containerMapperFileBkp:containerMapperHFSFilePath
					
					);

            }
        }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" );
            }

            ant.deployHFSFiles (  
                manifest: manifestFilePath,
                outputDir: unpackDir,
                containerMapperFile:containerMapperHFSFilePath,
                vesrionIdentity:oldVersionIdentifier,
                deleteCurrentContents:deleteCurrentContents
            )

            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) {
        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
		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) {
        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 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" );
            }
             
     ant.deployHFSFiles (
                manifest: rollbackManifestFilePath,
                outputDir: unpackDir,
                containerMapperFile:containerMapperHFSFilePath,
                vesrionIdentity:oldVersionIdentifier,
                deleteCurrentContents:tempDeleteCurrFlag
            )

            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;
		 }

    
}