/*
* 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
*/
import com.ibm.urbancode.zos.common.DeploymentConstants;
import com.ibm.urbancode.zos.common.DeploymentHelper;
import com.ibm.urbancode.zos.common.PackageManifestXMLHelper;
import com.ibm.urbancode.zos.deploy.common.DeploymentResultHelper;
import com.urbancode.air.AirPluginTool
import com.urbancode.ud.client.ResourceClient

import org.codehaus.jettison.json.JSONObject
import org.codehaus.jettison.json.JSONArray


try {
    final def workDir = new File('.').canonicalFile
	def apTool = new AirPluginTool(this.args[0], this.args[1])
	def props = apTool.getStepProperties();

	//Plugin input parameters
    final def resourceId     = DeploymentHelper.getStringInput(props['resourceId']);
    final def componentId    = DeploymentHelper.getStringInput(props['componentId']);
    final def componentName  = DeploymentHelper.getStringInput(props['componentName']);
    final def versionName    = DeploymentHelper.getStringInput(props['versionName']);
    final def versionId    = DeploymentHelper.getStringInput(props['versionId']);
    final def versionType    = DeploymentHelper.getStringInput(props['versionType']);
    final def deployBasePath = DeploymentHelper.getStringInput(props['deployBasePath']);
    final def tempDsnPrefix  = DeploymentHelper.getStringInput(props['tempDsnPrefix']);
    final def pdsMapping     = DeploymentHelper.getStringInput(props['pdsMapping']);
    final def checkAccess    = DeploymentHelper.getBooleanInput(props['checkAccess']);
    final def doBackup       = DeploymentHelper.getBooleanInput(props['doBackup']);
    final def allowCreateDataset  = DeploymentHelper.getBooleanInput(props['allowCreateDataset']);
	final def hfsTargetDir     = DeploymentHelper.getStringInput(props['hfsTargetDir']);
	final def allowCreateDirectory  = DeploymentHelper.getBooleanInput(props['allowCreateDirectory']);
    final def incrementalMode  = DeploymentHelper.getStringInput(props['incrementalMode']);
    final def versionMerge  = DeploymentHelper.getBooleanInput(props['versionMerge']);
	
	def oldVersionIdentifier=false;
    
	//global variables
    //check if hfsTargetDir don't contain ',' means it is old version so append *, before hfsTargetDir
	if(!hfsTargetDir.contains(DeploymentConstants.MAPPING_RULER_DELIMETER))
	{
	hfsTargetDir='*'+DeploymentConstants.MAPPING_RULER_DELIMETER+hfsTargetDir;
	oldVersionIdentifier=true;
	}
	
	def pkgManifestHelper = new PackageManifestXMLHelper();
    final def workDirPath = workDir.canonicalPath;
    final def componentVersionWorkingDir  = DeploymentHelper.getVersionDirPathInWorkingDir(workDirPath,versionName,resourceId);
    final def packageManifestFileNamePath = DeploymentHelper.getFilePathInWorkingDir(workDirPath, versionName, resourceId, DeploymentConstants.PACKAGE_MANIFEST_FILE_NAME);
    final def packageManifestFileDeployNamePath = DeploymentHelper.getFilePathInWorkingDir(workDirPath, versionName, resourceId, DeploymentConstants.PACKAGE_MANIFEST_FILE_DEPLOY_NAME);
    final def packageZipFilePath          = DeploymentHelper.getFilePathInWorkingDir(workDirPath, versionName, resourceId, DeploymentConstants.PACKAGE_FILE_NAME);
	
	//Verify packageManifest.xml exists, otherwise we can not deploy
    DeploymentHelper.validateFileExist(packageManifestFileNamePath, "${packageManifestFileNamePath} not found. Please make sure Copy Artifacts, FTP Artifacts or Download Artifacts for zOS plug-in step is successfully executed before the Deploy Data Set plug-in step.");
	//Verify deployBasePath exists, otherwise we can not deploy
	DeploymentHelper.validateDirectoryExist(deployBasePath, "Deploy Base Path ${deployBasePath} does not exist.");
	
	//we need the deployment tools to deploy data sets
	def toolKitHome = DeploymentHelper.getStringInput(System.getenv().get("BUZ_TOOLKIT_HOME"));
	if (!toolKitHome) {//try to compatible with application build on old plug-in(before v5)
		toolKitHome = DeploymentHelper.getStringInput(props['ucdToolKitHome']);
	}

	//Define path for all the files
	final def conainerMapperXmlFilePath = DeploymentHelper.getFilePathInWorkingDir(workDirPath, versionName, resourceId, DeploymentConstants.CONTAINER_MAPPER_FILE_NAME);
	
	final def conainerMapperHFSXmlFilePath = DeploymentHelper.getFilePathInWorkingDir(workDirPath, versionName, resourceId, DeploymentConstants.CONTAINER_MAPPER_HFS_FILE_NAME);
	final def dsListFile4BackUpPath     = DeploymentHelper.getFilePathInWorkingDir(workDirPath, versionName, resourceId, DeploymentConstants.CHECK_ACCESS_DS_FILE_NAME_BACKUP);
	final def dsListFilePath            = DeploymentHelper.getFilePathInWorkingDir(workDirPath, versionName, resourceId, DeploymentConstants.CHECK_ACCESS_DS_FILE_NAME);
	final def zosScriptPath             = DeploymentHelper.getPath4ExecScriptInToolkit(toolKitHome , DeploymentConstants.ZOS_TOOLKIT_ISPF_SCRIPT_NAME);
	final def checkAccessExePath        = DeploymentHelper.getPath4Checkaccess(toolKitHome , DeploymentConstants.CHECK_ACCESS_EXE);
	
	//Verify ispf gateway script exists
	DeploymentHelper.validateFileExist(zosScriptPath, "Can not find the ispf gateway script:${zosScriptPath}. Please make sure z/OS Deploy Toolkit is installed and z/OS Toolkit Home is set to the correct location.");
	
	
	//A version which only has generic resource has nothing to deploy. We will just copy the pacakgeManifest.xml 
	//file to the deploy directory to indicate this version has been deployed.  
	if (pkgManifestHelper.isGenericOnlyVersion(packageManifestFileNamePath)) {
        DeploymentHelper.printActionTitle("No file artifacts to deploy. Version contains only generic artifacts.");
        DeploymentHelper.postDeployForGenericOnly(deployBasePath, resourceId, componentName, versionName, componentVersionWorkingDir);
        DeploymentHelper.cleanWorkDir(componentVersionWorkingDir);
        System.exit(0);
    } else {
        //calculate incremental and update package manifest file
        if(incrementalMode == "INVENTORY" && pkgManifestHelper.hasDataSets(packageManifestFileNamePath)) {
            DeploymentHelper.printActionTitle("Calculate delta deployment using inventory status:");
     
            def udUser = apTool.getAuthTokenUsername();
            def udPass = apTool.getAuthToken();
            def weburl = System.getenv("AH_WEB_URL");
            ResourceClient resClient = new ResourceClient(new URI(weburl), udUser, udPass);
            com.urbancode.air.XTrustProvider.install();
    
            JSONArray inventoryArtifacts = resClient.intersectArtifactsInTargetResource(resourceId, componentId,versionId);
            pkgManifestHelper.calculateIncrementalManifest(inventoryArtifacts, packageManifestFileNamePath);
        }
		
		//Remove generic resource and proceed to deploy the data set and HFS files. 
		pkgManifestHelper.reFormatXML(packageManifestFileNamePath,packageManifestFileDeployNamePath);
		pkgManifestHelper.removeNodes4Deployment(packageManifestFileNamePath, packageManifestFileDeployNamePath);
    }	
	
	//verify input parameters for HFS file deployment
	final def hasDataSets = pkgManifestHelper.hasDataSets(packageManifestFileNamePath);
	final def hasHFSFiles = pkgManifestHelper.hasHFSFiles(packageManifestFileNamePath);

	if(hasHFSFiles){
		if (null == hfsTargetDir || hfsTargetDir.length()<=0) {
			throw new IllegalArgumentException("Version contains HFS files. HFS target directory is required. ");
		}
		if(!(new File(hfsTargetDir).exists())){
			if(allowCreateDirectory){
				new File(hfsTargetDir).mkdir()
			}else{
				throw new IllegalArgumentException("Deploy failed because HFS target directory(${hfsTargetDir}) does not exist and Allow Creating Directory is set to false.");
			}
			 
		}else if (!(new File(hfsTargetDir).isDirectory())){
			throw new IllegalArgumentException("Deploy failed because HFS target directory(${hfsTargetDir}) is not a directory.");
		}
	}
	
	//TODO Since a version which do <deleted> only may not have package.zip, this should be reconsidered.
	//Verify package.zip exists.   
	//DeploymentHelper.validateFileExist(packageZipFilePath, "${packageZipFilePath} not found. Please make sure a Copy or FTP plug-in step is successfully executed before the Deploy plug-in step.");
    
	//Check is backup is needed based on input parameter and the inventory status. 
	//When it is a re-deploy of the last deployed version, we are not going to 
	//take another backup. Rollback will use the existing backup. 
    if (DeploymentResultHelper.checkVersionDeployed(deployBasePath, resourceId, componentName, versionName)) {
		DeploymentHelper.printActionTitle("Check last deployed version:");
		try{
	        def udUser = apTool.getAuthTokenUsername();
	        def udPass = apTool.getAuthToken();
	        def weburl = System.getenv("AH_WEB_URL");
	        ResourceClient resClient = new ResourceClient(new URI(weburl), udUser, udPass);
	        com.urbancode.air.XTrustProvider.install();
	
	        JSONObject latestVesionJson = resClient.getLatestVersionByResourceAndComponent(resourceId, componentId);
	        if (null != latestVesionJson && latestVesionJson.has("version")) {
	            latestVesionJson = latestVesionJson.getJSONObject("version");
	            if (null != latestVesionJson && latestVesionJson.has("name")) {
	                def latestVersionName = latestVesionJson.getString("name")?.trim();
	                if (latestVersionName == versionName) {
	                    doBackup = false; 
	                    DeploymentHelper.printProcessOutput("A backup is already taken by the previous deploy.");
	                
					}
	        }
		}
		}catch(Exception e){
			DeploymentHelper.printProcessOutput("Warning: check last deployed version failed.");
		}
		DeploymentHelper.printProcessOutput("")
    }

	
	//Deploy data sets
	if(hasDataSets){
		//Calculate data set mapping
	    //Combine the user input and the environment pds mapping
	    def src2TargetMap = [:];
	    //collect those rules with wildcard "*" character, the key will be an regular express pattern
	    def containsWildcardMap = [:];
	
	    //Parse input mapping rules from text into map
	    DeploymentHelper.handleMappingRule(pdsMapping, src2TargetMap, containsWildcardMap);	
	    //Check the mapping rule to see if some datasets were missed
	    def dsNeedToDeploy   = pkgManifestHelper.getAllDeployDatasets(packageManifestFileDeployNamePath);
	    def dsIncludedByUser = src2TargetMap.keySet();
	    def missedDsByUser   = dsNeedToDeploy - dsIncludedByUser;
	
	    def notResolvedByWildcard = dsNeedToDeploy - dsIncludedByUser;
	    if(missedDsByUser && missedDsByUser.size() > 0){
	        missedDsByUser.each { missedDsItem ->
	            containsWildcardMap.keySet().each {keywithWildcardPattern->
	                if(missedDsItem ==~ keywithWildcardPattern) {
	                    if(!src2TargetMap.get(missedDsItem)) {
	                        src2TargetMap.put(missedDsItem, containsWildcardMap.get(keywithWildcardPattern));
	                        notResolvedByWildcard.remove(missedDsItem);
	                    }
	                    return(true);//If find the rule, escape the loop
	                }
	            }
	        }
	    }
	
	/*	println "src2TargetMap : "
		println src2TargetMap*/
	    //#comment it first, will add back later#If the dataset was not set with a.b.c,a1.b1.c1 and not set by *.c,a2.b2.c2. We will use a.b.c,a.b.c rule as the default 
	    if(notResolvedByWildcard && notResolvedByWildcard.size() >0) {
	//        notResolvedByWildcard.each { noRulerItem ->
	//            if(!src2TargetMap.get(noRulerItem)) {
	//                src2TargetMap.put(noRulerItem, noRulerItem);
	//            }
	//        }
	        def dsStr = "";
	        for (ds in notResolvedByWildcard) {
	            dsStr += " " + ds;
	        }
	        DeploymentHelper.printProcessOutput("Missing PDS mapping rules for: ${dsStr}");
	        System.exit(1);
	    }
	
	    def dsToDeploySrc2TargetMap = [:];
	    dsNeedToDeploy.each {ds2Deploy ->
	        dsToDeploySrc2TargetMap.put(ds2Deploy, src2TargetMap.get(ds2Deploy));
	    }
	
/*		println "dsToDeploySrc2TargetMap : "
		println dsToDeploySrc2TargetMap*/
		
	    def targetDsSet =  dsToDeploySrc2TargetMap.values().toSet();
		
/*		println "targetDsSet : "
		println targetDsSet*/
	
	    //Target data sets must already exist if allowCreateDataset=false
	    if(!allowCreateDataset){
	        DeploymentHelper.printActionTitle("Check data sets exist:");
	        def datasetsExist = DeploymentHelper.checkDatasetsExist(targetDsSet)
	        if(!datasetsExist){
	            DeploymentHelper.printProcessOutput("Deploy failed because target data set could not be opened and Allow Creating Data Set is set to false.");
	            System.exit(1);
	        }
	        println "";
	    }
	
	    //If backup is required, check the target datasets can be read
	    if(doBackup && checkAccess){
	        DeploymentHelper.printActionTitle("Check access for backup action:")
	        DeploymentHelper.checkAccess4Input(targetDsSet, dsListFile4BackUpPath, checkAccessExePath)
		
	        println "";
	    }
		
	    //Check target datasets can be written
	    if(checkAccess){
	        DeploymentHelper.printActionTitle("Check access for deployment action:")
	        DeploymentHelper.checkAccess4Output(targetDsSet, dsListFilePath, checkAccessExePath)
	        println "";
	    }
	    
		if(incrementalMode == "RUNTIME" && pkgManifestHelper.hasDataSets(packageManifestFileDeployNamePath)) {
			DeploymentHelper.printActionTitle("Calculate delta deployment using Target Environment status:");
			pkgManifestHelper.calculateTargetIncrementalManifest(packageManifestFileNamePath,dsToDeploySrc2TargetMap,zosScriptPath);
			pkgManifestHelper.reFormatXML(packageManifestFileNamePath,packageManifestFileDeployNamePath);
		}
	    //Generate the containerMapping file logic
	    def pdsXmlStrWriter = new StringWriter();
	    pdsXmlStrWriter.append('<?xml version="1.0" encoding="IBM-1047"?>');
	    def containerMapperXml = new groovy.xml.MarkupBuilder(pdsXmlStrWriter);
	    containerMapperXml.setDoubleQuotes(true);
	    if(dsToDeploySrc2TargetMap.size() > 0){
	        containerMapperXml.maps(){
	            dsToDeploySrc2TargetMap.each { pdsSrc, pdsTarget->
	                map(type:"PDS"){
	                    sourceContainer(name:pdsSrc);
	                    targetContainer(name:pdsTarget);
	                }
	            }
	        }
	    }else{
	        //Handle the issue of no mapping
	        throw new IllegalArgumentException("PDS mapping not found. Make sure you have specified the mapping in the plugin step or in environment.");
	    }
	
	    //Put the content into log
	    DeploymentHelper.printActionTitle("The containerMapper.xml contents:")
	    def pdsXmlStr = pdsXmlStrWriter.toString();
	    DeploymentHelper.printProcessOutput(pdsXmlStr);
	    println "";
	
	    //Generate the containerMapper.xml file
	    new File(conainerMapperXmlFilePath).withWriter('IBM-1047'){ out ->
	        out.print(pdsXmlStrWriter);
	    }
	}	
	if(hasHFSFiles){
	    //Deploy HFS files
        //Calculate HFS mapping
	    //Combine the user input and the environment hfs mapping
	    def src2TargetMapHfsTagrgetDir = [:];
	    //collect those rules with wildcard "*" character, the key will be an regular express pattern
	    def containsWildcardMapHfsTagrgetDir = [:];

	    //Parse input mapping rules from text into map
	    DeploymentHelper.handleMappingRuleHfsTagrgetDir(hfsTargetDir, src2TargetMapHfsTagrgetDir, containsWildcardMapHfsTagrgetDir);
        //Check the mapping rule to see if some directories were missed
	    def hfsNeedToDeploy   = pkgManifestHelper.getAllDeployHFSFiles(packageManifestFileDeployNamePath);
		def hfsIncludedByUserHfs = src2TargetMapHfsTagrgetDir.keySet(); 
		def missedDsByUserHfs   = hfsNeedToDeploy - hfsIncludedByUserHfs;

		def notResolvedByWildcardHfs = hfsNeedToDeploy - hfsIncludedByUserHfs;
		
		//added for HFS files
		if(missedDsByUserHfs && missedDsByUserHfs.size() > 0){
	        missedDsByUserHfs.each { missedDsItem ->
	            containsWildcardMapHfsTagrgetDir.keySet().each {keywithWildcardPattern->
	                if(missedDsItem ==~ keywithWildcardPattern) {
	                    if(!src2TargetMapHfsTagrgetDir.get(missedDsItem)) {
	                        src2TargetMapHfsTagrgetDir.put(missedDsItem, containsWildcardMapHfsTagrgetDir.get(keywithWildcardPattern));
	                        notResolvedByWildcardHfs.remove(missedDsItem);
	                    }
	                    return(true);//If find the rule, escape the loop
	                }
	            }
	        }
	    }
        
		//added for HFS files
		 if(notResolvedByWildcardHfs && notResolvedByWildcardHfs.size() >0) {
	
	        def hfsString = "";
	        for (hfs in notResolvedByWildcardHfs) {
	            hfsString += " " + hfs;
	        }
	        DeploymentHelper.printProcessOutput("Missing HFS mapping rules for: ${hfsString}");
	        System.exit(1);
	    }
	
	  //  def dsToDeploySrc2TargetMap = [:];
		def hfsToDeploySrc2TargetMap = [:];

		hfsNeedToDeploy.each {hfs2Deploy ->
	        hfsToDeploySrc2TargetMap.put(hfs2Deploy, src2TargetMapHfsTagrgetDir.get(hfs2Deploy));
	    }
        def targetHfsSet =  hfsToDeploySrc2TargetMap.values().toSet();
		
      
      	  //Generate the containerMapping file logic
	    def pdsXmlStrWriter = new StringWriter();
	    pdsXmlStrWriter.append('<?xml version="1.0" encoding="IBM-1047"?>');
	    def containerMapperXml = new groovy.xml.MarkupBuilder(pdsXmlStrWriter);
	    containerMapperXml.setDoubleQuotes(true);
	 
	
	//added for HFS
	if(hfsToDeploySrc2TargetMap.size() > 0){
	        containerMapperXml.maps(){
	            hfsToDeploySrc2TargetMap.each { pdsSrc, pdsTarget->
	                map(type:"Directory"){
	                    sourceContainer(name:pdsSrc);
	                    targetContainer(name:pdsTarget);
	                }
	            }
	        }
	    }else{
	        //Handle the issue of no mapping
	        throw new IllegalArgumentException("HFS mapping not found. Make sure you have specified the mapping in the plugin step or in environment.");
	    }
		 //Put the content into log
	    DeploymentHelper.printActionTitle("The containerMapperHFS.xml contents:")
	    def pdsXmlStr = pdsXmlStrWriter.toString();
	    DeploymentHelper.printProcessOutput(pdsXmlStr);
	    println "";
		
		new File(conainerMapperHFSXmlFilePath).withWriter('IBM-1047'){ out ->
	        out.print(pdsXmlStrWriter);
	    }
	
	}

	//deploy
	if(hasHFSFiles || hasDataSets){
		DeploymentHelper.doDeploymentAction(deployBasePath, tempDsnPrefix, resourceId, componentName, versionName, versionType, zosScriptPath, componentVersionWorkingDir, doBackup, hfsTargetDir, versionMerge,oldVersionIdentifier);
		//Clear the working directory to avoid dirty data
		//Just delete those files we created
		DeploymentHelper.cleanWorkDir(componentVersionWorkingDir);
	}else{
		DeploymentHelper.printActionTitle("There is nothing to deploy.");
        if(incrementalMode == "INVENTORY") { 
            //nothing to deploy because nothing is new. We still copy the results
            //to the deploy result directory so generate artifact knows what happens
            def versionPathInDeployBase = DeploymentHelper.getVersionPathInDeployBasePath(deployBasePath, resourceId, componentName, versionName, false);
            DeploymentHelper.postDeploy(componentVersionWorkingDir,versionPathInDeployBase,false);
         }
	}

} catch (Exception e) {
    DeploymentHelper.printActionTitle("Error deploying version. ${e.message}");
//    e.printStackTrace();
    System.exit(1);
}

System.exit(0);