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

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

try {
    final def workDir = new File('.').canonicalFile
    final def props = new Properties();
    final def inputPropsFile = new File(args[0]);
    try {
        inputPropsStream = new FileInputStream(inputPropsFile);
        props.load(inputPropsStream);
    }
    catch (IOException e) {
        throw new RuntimeException(e);
    }

	//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 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']);
	
	//global variables
	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.");
	
	//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 {
		//Remove generic resource and proceed to deploy the data set and HFS files. 
		pkgManifestHelper.removeGenericResourceNodes(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{
	        //Initialize the EnvironmentClient help class and pass the auth.
	        def apTool = new AirPluginTool(this.args[0], this.args[1]);
	        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("")
    }

	//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 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.getPath4ExecScriptInToolkit(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.");
	
	
	//Deploy data sets
	if(hasDataSets){
		//Calculate data set mapping
	    //Combile 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
	                }
	            }
	        }
	    }
	
	    //#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));
	    }
	
	    def targetDsSet =  dsToDeploySrc2TargetMap.values().toSet();
	
	    //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 "";
	    }
	
	    //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);
	    }
	}

	//deploy
	if(hasHFSFiles || hasDataSets){
		DeploymentHelper.doDeploymentAction(deployBasePath, tempDsnPrefix, resourceId, componentName, versionName, zosScriptPath, componentVersionWorkingDir, doBackup, hfsTargetDir);
		//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.");
	}

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

System.exit(0);