/*
 * 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.
 */
import java.io.IOException;
import java.security.MessageDigest;
import java.util.Iterator;
import com.ibm.jzos.*;
import com.ibm.urbancode.zos.common.DataSetHelper;
import com.urbancode.air.AirPluginTool;



def apTool = new AirPluginTool(args[0], args[1])
def props=apTool.getStepProperties();
def dirOffset = props['dir']?:'.'
def workDir = new File('.',dirOffset).canonicalPath
def includes = props['includes']
def isWildCardAllowed = Boolean.parseBoolean(props['isWildCardAllowed'])
def failOnTruncate = Boolean.parseBoolean(props['failOnTruncate'])
def excludes = props['excludes']
def startDelimiter = props['startDelimiter']?:''
def endDelimiter = props['endDelimiter']?:''
def propertyPrefix = props['propertyPrefix']
def envPropValues = props['envPropValues']
def explicitTokens = props['explicitTokens']
def propFile = File.createTempFile("replace_tokens",".properties");
def LinkedHashSet<String> fileList = new LinkedHashSet<String>();


try {
	// generate property files
	def Properties properties = new Properties()

	//add properties
	if(envPropValues) {
		if (propertyPrefix) {
			println "Looking for properties starting with $propertyPrefix"
		}
		envPropValues.split("(?<=(^|[^\\\\])(\\\\{2}){0,8}),").each { prop ->
			//split out the name
			def parts = prop.split("(?<=(^|[^\\\\])(\\\\{2}){0,8})=",2);
			def propName = parts[0];
			def propValue = parts.size() == 2 ? parts[1] : "";
			//replace \, with just , and then \\ with \
			propName = propName.replace("\\=", "=").replace("\\,", ",").replace("\\\\", "\\")
			propValue = propValue.replace("\\=", "=").replace("\\,", ",").replace("\\\\", "\\")

			if ((!propertyPrefix || propName.startsWith(propertyPrefix))) {
				properties.setProperty(startDelimiter + propName + endDelimiter, propValue)
			}
		}
	}

	if (explicitTokens) {
		explicitTokens.eachLine {line->
			def parts =line.split("->",2);
			def propName = parts[0];
			def propValue = parts.size()==2?parts[1]:""
			properties.setProperty(propName, propValue)
		}
	}
	propFile.withOutputStream { outStream ->
		properties.store(outStream, 'Auto generated property file')
	}

	//search input file
	fileList.addAll(filter(includes,isWildCardAllowed,excludes,apTool))

	apTool.out.println("Replace token in following files:")
	fileList.each {file->
		apTool.out.println(ZFile.getFullyQualifiedDSN(file));
	}

	def ant = new AntBuilder()
	def MessageDigest md = MessageDigest.getInstance("MD5");
	def Map<String,String> fileStatus =[:]
	if (properties.size() > 0) {
		fileList.each {value->
			//copy files
			RecordReader reader = null;
			def tempFile = new File(workDir,value)
			def writer = null;
			try{
				reader= RecordReader.newReader(value,ZFileConstants.FLAG_DISP_SHR);
				writer= tempFile.newWriter(ZUtil.getDefaultPlatformEncoding());
				byte[] recordBuf = new byte[reader.getLrecl()];
				int bytesRead;
				while ((bytesRead = reader.read(recordBuf)) > 0) {
					writer.writeLine(new String(recordBuf,ZUtil.getDefaultPlatformEncoding()));
				}
			} catch(Exception e) {
				e.printStackTrace();
			}finally {
				if (reader != null) {
					try {
						reader.close();
					} catch (ZFileException zfe) {
						zfe.printStackTrace();  // but continue
					}
				}
				if (writer != null) {
					try {
						writer.flush();
						writer.close();
					} catch (Exception e) {
						e.printStackTrace();
					}
				}
			}
			tempFile.eachByte(1024){byte[] buf,int byteRead->
				md.update(buf,0,byteRead);
			}
			fileStatus.putAt(value,(String)md.digest().encodeHex());

		}
		//replace token

		ant.replace(
				dir:workDir,
				summary: 'true',
				defaultexcludes: 'no',
				replacefilterfile: propFile.canonicalPath,
				encoding: ZUtil.getDefaultPlatformEncoding())
		//apply changes
		fileList.each {fileName->
			def tempFile = new File(workDir,fileName);
			tempFile.eachByte(1024){byte[] buf,int byteRead->
				md.update(buf,0,byteRead);
			}
			def String fileMd5 = md.digest().encodeHex();
			if(!fileMd5.equalsIgnoreCase(fileStatus.get(fileName))){
				apTool.out.println("Apply changes to $fileName");
				RecordWriter target = RecordWriter.newWriter(fileName,ZFileConstants.FLAG_DISP_SHR);

				def output = tempFile.newReader(ZUtil.getDefaultPlatformEncoding());
				byte[] recBuf = null;
				int lineCounts=1;
				try{
					while((line = output.readLine())!= null){
						def record =line.replaceAll(/\s+$/, '').padRight(target.getLrecl()).toString();
						recBuf =record.getBytes(ZUtil.getDefaultPlatformEncoding());
						if(recBuf.length >target.getLrecl()){
							if(failOnTruncate){
								println "Error: line $lineCounts of ${fileName} excceeds ${target.getLrecl()} bytes."
								System.exit(1)
							}
							
							println "Warning: line $lineCounts of ${fileName} excceeds ${target.getLrecl()} bytes and will be truncated."
							println " $lineCounts of ${fileName}: $line"
							target.write(recBuf,0,target.getLrecl());
						}
						else{
							target.write(recBuf, 0, recBuf.length);
						}
						lineCounts++;
					}
					tempFile.delete();
				}catch(e){
					e.printStackTrace();
				}finally{
					if(target) target.close();
					if(output) output.close();
				}
			}
		}

	}
	else {
		println 'No properties or explicit tokens to replace.'
	}
}
catch (Exception e) {
	e.printStackTrace()
	println "Error replacing tokens!"
	System.exit(1)
}
finally {
	if (propFile) {
		propFile.delete();
	}
}

System.exit(0)


LinkedHashSet<String> filter(String include, Boolean allowWildCardIninclude,String exclude,AirPluginTool apTool ){
	def LinkedHashSet<String> result = new LinkedHashSet<String>()
	if(include){
		include.split('\\s|,').each {line ->
			def items=line?.split('\\(|\\)');
			def includeDSPattern = items.length>0?items[0]:""
			def inputMBRPattern= items.length>1?items[1]:""
			if((!allowWildCardIninclude) && (includeDSPattern?.contains("*")||inputMBRPattern?.contains("*"))){
				apTool.out.println("Set Allow Wildcard to true to use wildcard(*) in Include Data Sets.")
				apTool.setOutputProperty("exitCode","8")
				apTool.setOutputProperty("Status","Failed")
				apTool.setOutputProperties();
				System.exit(8);
			}
			if(includeDSPattern?.trim()){
				DataSetHelper.searchNonVsamDataset(includeDSPattern).each {dataset, volume->
					result.addAll(DataSetHelper.getMemebers(dataset,volume, inputMBRPattern))
				}
			}
		}
	}
	if(exclude){

		exclude.split('\\s|,').each {line ->
			def items=line.split('\\(|\\)');
			def excludeDSPattern = items.length>0?items[0]:null
			def excludeMBRPattern = items.length>1?items[1]:"*"
			def pattern = "$excludeDSPattern\\($excludeMBRPattern\\)"
			pattern=pattern.replaceAll('\\.',"\\\\.")
			pattern = pattern.replaceAll("\\\$","\\\$");
			pattern = pattern.replaceAll('\\*',".*")
			def deleteList =[]
			def Iterator iterator=result.iterator()
			while(iterator.hasNext()){
				def fileName = (String)iterator.next();
				if(fileName.matches("^.*$pattern.*\$")){
					deleteList.add(fileName)
				}
			}
			result.removeAll(deleteList)
		}

	}
	return result
}