import java.util.*;

import com.urbancode.air.AirPluginTool
import com.urbancode.ud.client.VersionClient
import com.ibm.urbancode.zos.deploy.common.DataSet;
import com.ibm.urbancode.zos.deploy.common.DeploymentResultHelper;
import com.ibm.urbancode.zos.deploy.common.Member;
import com.ibm.urbancode.zos.common.DeploymentHelper;
import com.ibm.urbancode.zos.common.DeploymentConstants;

/*
* 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{
	def apTool = new AirPluginTool(this.args[0], this.args[1])
	final def workDir = new File('.').canonicalFile
	def props = apTool.getStepProperties();

	final def loopType 			= DeploymentHelper.getStringInput(props['loopType']);  //PDS, Member
	final def deployTypeName 		= DeploymentHelper.getStringInput(props['deployTypeName']);
	final def srcDatasetName 		= DeploymentHelper.getStringInput(props['srcDatasetName']);
	final def targetDatasetName 		= DeploymentHelper.getStringInput(props['datasetName']);
	final def memberName 		= DeploymentHelper.getStringInput(props['memberName']);
	final def custProperties 	= DeploymentHelper.getStringInput(props['custProperties']);
	final def template	 		= props['templateText']; //We want to keep all space, tabs and newlines in template
	final def orderBy 	= DeploymentHelper.getStringInput(props['orderBy']);  //SHIPLIST, ASC, DESC
	
	//hidden
	final def deployBasePath 	= DeploymentHelper.getStringInput(props['deployBasePath']);
	final def componentName 	= DeploymentHelper.getStringInput(props['componentName']);
	final def versionName 		= DeploymentHelper.getStringInput(props['versionName']);
	final def failOnEmpty       = DeploymentHelper.getBooleanInput(props['failOnEmpty']);
	final def resourceId        = DeploymentHelper.getStringInput(props['resourceId']);

	if(componentName == null || componentName.trim().length()==0){
		throw new Exception("Component Name must be set.");
	}

	if(deployBasePath == null || deployBasePath.trim().length()==0){
		throw new Exception("Deploy Base Path must be set.");
	}
	
	if(versionName == null || versionName.trim().length()==0){
		throw new Exception("Version Name must be set.");
	}
	
	if(template == null || template.trim().length()==0){
		throw new Exception("Template must be set.");
	}
	DeploymentHelper.inputNotEmptyCheck(resourceId, "Resource Id is empty.");
	//DeploymentHelper.inputNotEmptyCheck(orderBy, "Order By is empty");
	if(orderBy == null || orderBy.trim().length() <=0){
		orderBy = "ASC";
	}

	//process customProperties
	Map properties = [:];
	custProperties.eachLine {
		if (it && it.indexOf('=') > 0) {
			def index = it.indexOf('=')
			def propName = it.substring(0, index).trim()
			def propValue = index < (it.length() - 1) ? it.substring(index + 1) : ""
			properties.put(propName, propValue)
			//println 'custom property: ' + propName + ':' + propValue
		}
		else if (it) {
			properties.put(it,"")
			//println 'custom property: ' + it + ':'
		}
	}

	println("Filter: [container:${srcDatasetName} target dataset:${targetDatasetName} resource:${memberName} deployType:${deployTypeName}] ${properties}")
	
	Set<DataSet> containers;
	//Read version artifact model
	if (DeploymentResultHelper.checkVersionDeployed(deployBasePath, resourceId, componentName, versionName)) {
		containers = DeploymentResultHelper.getDeployedDataSets(deployBasePath, resourceId, componentName, versionName);
	} else {
		//Read the not deployed meta info from UCD Server side
		def udUser = apTool.getAuthTokenUsername();
		def udPass = apTool.getAuthToken();
		def weburl = System.getenv("AH_WEB_URL");
		VersionClient envClient = new VersionClient(new URI(weburl), udUser, udPass);
		com.urbancode.air.XTrustProvider.install();
		
		def versionWorkingDir = DeploymentHelper.getVersionDirPathInWorkingDir(workDir.canonicalPath, versionName, resourceId);
		def versionManifestPath = DeploymentHelper.getFilePathInWorkingDir(workDir.canonicalPath, versionName, resourceId, DeploymentConstants.PACKAGE_MANIFEST_FILE_NAME);
		
		PrintStream oriOut = System.out;
		System.setOut(new PrintStream(new OutputStream() {
			@Override 
			public void write(int b) throws IOException {
			}
		}));
		try {
			envClient.downloadFiles(componentName, versionName, versionWorkingDir, DeploymentConstants.PACKAGE_MANIFEST_FILE_NAME);
		} catch (Exception e) {
			System.setOut(oriOut);
			println "Failed to locate metadata for version.";
			println "Generate artifact information works for deployed versions or versions created with UCD 6.1.1.6 and later.";
			System.exit(1);
		} finally {
			System.setOut(oriOut);
		}
		
		containers = DeploymentResultHelper.getNotDeployedDataSets(versionManifestPath);

		if(targetDatasetName != ""){		
			println "Warning: Target Data Set Name Filter ignored because version ${versionName} is not deployed";
			targetDatasetName = "";
		}
	}
	
	//containers is a LinkedHasSet which is in the orginal insert order (shiplist order)
	if(orderBy == "ASC"){
		containers = new TreeSet(containers);
	}else if(orderBy == "DESC"){
		containers = new TreeSet(containers).descendingSet();
	}
	
	println()
	
	def text = ""
	def count = 0
	for(currentContainer in containers){
			//loop at container level
			if(loopType in ["PDS", "DeletedPDS", "Sequential", "DeletedSequential", "GenericArtifactGroup", "Directory"]){
				//filter the container types and if it is a delete
				if(loopType == "PDS"){
					if(currentContainer.getType()!="PDS" || currentContainer.isDelete() ){
						continue
					}
				}else if(loopType == "DeletedPDS"){
					if(currentContainer.getType()!="PDS" || !currentContainer.isDelete() ){
						continue
					}
				}else if(loopType == "Sequential"){
					if(currentContainer.getType()!="sequential" || currentContainer.isDelete() ){
						continue
					}
				}else if(loopType == "DeletedSequential"){
					if(currentContainer.getType()!="sequential" || !currentContainer.isDelete() ){
						continue
					}
				}else if(loopType == "GenericArtifactGroup"){
					if(currentContainer.getType().toUpperCase()!="GENERIC" || currentContainer.isDelete() ){
						continue
					}
				}else if(loopType == "Directory"){
					if(currentContainer.getType()!="directory"){
						continue
					}
				}
							
				def itemSrcDsName = currentContainer.getSrcDataSetName();
				def itemDsName = currentContainer.getName();
				def itemMemberName = "";
				def itemType = currentContainer.getType();
				def itemDeployType = currentContainer.getDeployType();
				def itemCustomerProperties = currentContainer.getCustomerProperties();
				
				if(filter(itemSrcDsName, itemDsName, itemMemberName, itemDeployType, itemCustomerProperties,
					deployTypeName, properties, srcDatasetName, targetDatasetName, memberName)){
					Map vars = [:];
					
					if(null!=itemCustomerProperties){
						vars.putAll(itemCustomerProperties);
					}
					vars.put("sourceDataset", itemSrcDsName);
					vars.put("dataset", itemDsName);
					vars.put("member", itemMemberName);
					vars.put("deployType", itemDeployType);
					if(loopType == "GenericArtifactGroup"){
						vars.put("artifactGroup", itemSrcDsName);
						vars.put("artifact", itemMemberName);
					}else if(loopType == "Directory"){
						vars.put("directory", itemSrcDsName);
						vars.put("file", itemMemberName);
					}
					
					
					try {
						def String line = envCommand(template, vars)
						text = text + line
						count ++
					} catch(MissingPropertyException noSuchProperty) {
						println (noSuchProperty.message);
						throw new IllegalArgumentException("Missing property for data set ${itemDsName}. Please make sure the property is set on the data set.");
					}
				}
			//loop at resource level
			}else if(loopType in ["Member", "DeletedMember", "GenericArtifact","File","DeletedFile"]){
				//filter the container types and if it is a delete
				if(loopType == "Member"){
					if(currentContainer.getType()!="PDS" || currentContainer.isDelete() ){
						continue
					}
				}else if(loopType == "DeletedMember"){
					if(currentContainer.getType()!="PDS" || !currentContainer.isDelete() ){
						continue
					}
				}else if(loopType == "GenericArtifact"){
					if(currentContainer.getType().toUpperCase()!="GENERIC" || currentContainer.isDelete() ){
						continue
					}
				}else if(loopType == "File"){
					if(currentContainer.getType()!="directory" || currentContainer.isDelete() ){
						continue
					}
				}else if(loopType == "DeletedFile"){
					if(currentContainer.getType()!="directory" || !currentContainer.isDelete() ){
						continue
					}
				}
			
				def pdsCustomerProperties = currentContainer.getCustomerProperties();
				if( pdsCustomerProperties == null){
					pdsCustomerProperties = [:]
				}

				Set<Member> deployedMembersInCurrentDataSet = currentContainer.getMembers();
				//members is a LinkedHasSet which is in the orginal insert order (shiplist order)
				if(orderBy == "ASC"){
					deployedMembersInCurrentDataSet = new TreeSet(deployedMembersInCurrentDataSet);
				}else if(orderBy == "DESC"){
					deployedMembersInCurrentDataSet = new TreeSet(deployedMembersInCurrentDataSet).descendingSet();
				}
				
				for(member in deployedMembersInCurrentDataSet){
					def itemSrcDsName = member.getSrcDataSetName();
					def itemDsName = member.getDataSetName();
					def itemMemberName = member.getName();
					def itemMemberType = member.getType();
					def itemDeployType = member.getDeployType();
					def itemCustomerProperties = member.getCustomerProperties();
					if( itemCustomerProperties == null){
						itemCustomerProperties = [:]
					}
	
					itemCustomerProperties.putAll(pdsCustomerProperties);
						
					if(filter(itemSrcDsName, itemDsName, itemMemberName, itemDeployType, itemCustomerProperties,
						deployTypeName, properties, srcDatasetName, targetDatasetName, memberName)){
						Map vars = [:];

						if(null!=itemCustomerProperties){
							vars.putAll(itemCustomerProperties);
						}

						vars.put("sourceDataset", itemSrcDsName);
						vars.put("dataset", itemDsName);
						vars.put("member", itemMemberName);
						vars.put("deployType", itemDeployType);
						if(loopType == "GenericArtifact"){
							vars.put("artifactGroup", itemSrcDsName);
							vars.put("artifact", itemMemberName);
						}else if(loopType == "File" || loopType == "DeletedFile" ){
							vars.put("directory", itemSrcDsName);
							vars.put("file", itemMemberName);
						}

						try {
							def String line = envCommand(template, vars)
							text = text + line
							count ++ 
						} catch(MissingPropertyException noSuchProperty) {
							println (noSuchProperty.message);
							throw new IllegalArgumentException("Missing property for data set ${itemDsName}. Please make sure the property is set on the data set.");
						}
					}
				}
			}
		}
	println()
	println("Output text:")
	println "========================================================================================================================"
	println (text)
	println "========================================================================================================================"
	apTool.setOutputProperty("text",text)
	apTool.setOutputProperty("count", count.toString())
	apTool.storeOutputProperties()
	
	if(failOnEmpty && text.trim().length()==0){
		println("Step failed because Fail On Empty is checked and the generation result is empty.")
		System.exit(1);
	}
	
} catch (Exception e) {
	println "Error: ${e.message}";
	e.printStackTrace();
	System.exit(1);
}


def envCommand(String s, Map vars) {
	Binding bind = new Binding(vars)
	GroovyShell gs = new GroovyShell(bind)
	return gs.evaluate("\"\"\"${s}\"\"\"")
}

def filter(String srcDsName, String dsName, String memberName, String deployType, Map customerProperties, 
		   String filterDeployType, Map filterProperties,String filterSrcDsName, String filterDsName, String filterMemberName){

	println( "Apply filter to ${srcDsName} - ${dsName}(${memberName}) ${customerProperties}")
	
	//filter deploy type
	if(filterDeployType !=""){
		if(filterDeployType.startsWith("/") && filterDeployType.endsWith("/")){
			def pattern = ~filterDeployType.substring(1,filterDeployType.length()-1)
			if(!(deployType ==~ pattern)){
				println("  Not selected because deployType=${filterDeployType} does not match")
				return false
			}

		}else{
			if(deployType != filterDeployType ){
				println("  Not selected because deployType=${filterDeployType} does not match")
				return false
			}
		}
	}
	
	//filter data set name
	if(filterDsName !=""){
		if(filterDsName.startsWith("/") && filterDsName.endsWith("/")){
			def pattern = ~filterDsName.substring(1,filterDsName.length()-1)
			if(!(dsName ==~ pattern)){
				println("  Not selected because target data set name=${filterDsName} does not match")
				return false
			}

		}else{
			if(dsName != filterDsName ){
				println("  Not selected because target data set name=${filterDsName} does not match")
				return false
			}
		}
	}
	
	//filter source data set name
	if(filterSrcDsName !=""){
		if(filterSrcDsName.startsWith("/") && filterSrcDsName.endsWith("/")){
			def pattern = ~filterSrcDsName.substring(1,filterSrcDsName.length()-1)
			if(!(srcDsName ==~ pattern)){
				println("  Not selected because container name=${filterSrcDsName} does not match")
				return false
			}

		}else{
			if(srcDsName != filterSrcDsName ){
				println("  Not selected because container name=${filterSrcDsName} does not match")
				return false
			}
		}
	}
	
	//filter member name
	if(filterMemberName !=""){
		if(filterMemberName.startsWith("/") && filterMemberName.endsWith("/")){
			def pattern = ~filterMemberName.substring(1,filterMemberName.length()-1)
			if(!(memberName ==~ pattern)){
				println("  Not selected because resource name=${filterMemberName} does not match")
				return false
			}

		}else{
			if(memberName != filterMemberName ){
				println("  Not selected because resource name=${filterMemberName} does not match")
				return false
			}
		}
	}
	
	for(Map.Entry<String,String> entry : filterProperties.entrySet()){
		def filterKey = entry.getKey();
		def filterValue = entry.getValue();
		def match = false
		
		if(customerProperties){
			customerProperties.each {
				custKey, custValue ->
				if(custKey == filterKey){
					if(filterValue == ""){
						//A filter without value checks if the customer property exists 
						match = true
					}else{
						if(filterValue.startsWith("/") && filterValue.endsWith("/")){
							def pattern = ~filterValue.substring(1,filterValue.length()-1)
							if(custValue ==~ pattern){
								match = true
							}
						}else{
							if(custValue == filterValue ){
								match = true
							}
						}
					}
				}
			}
		}
		
		if(!match){
			println("Not selected because custom property ${filterKey}=${filterValue} does not match")
			return false
		}
	}
	
	return true;
}
System.exit(0);

