/*
 * 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 java.nio.file.Files;
import com.ibm.teamz.build.ant.jni.ISPFStatistics;
import com.ibm.urbancode.zos.common.DataSetHelper;
import com.ibm.urbancode.zos.dataset.util.DsInfo;
import com.urbancode.air.AirPluginTool;



def apTool = new AirPluginTool(args[0], args[1])
def props=apTool.getStepProperties();
def dirOffset = props['dir']?:'.';
def currentDirectory = new File('.',dirOffset);
def workDirPath = Files.createTempDirectory(currentDirectory.toPath(), "replaceToken");
def workDir = workDirPath.toString();
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", workDirPath.toFile());
    propFile.deleteOnExit();
def LinkedHashSet<String> fileList = new LinkedHashSet<String>();

try {
    // initlize java native library for ISPF Stats processing. 
    def PLUGIN_HOME = System.getenv("PLUGIN_HOME");
    System.setProperty("java.library.path", "${PLUGIN_HOME}/lib/native:" + System.getProperty("java.library.path"));

    // 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] : "";
            propName = propName.trim();
            propValue = propValue.trim();
            //replace \, with just , and then \\ with \
            if (propName && propName.length() > 0) {
                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, 0, bytesRead, 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");
                ISPFStatistics ispfstats = getISPFStats(fileName);
                RecordWriter target = RecordWriter.newWriter(fileName,ZFileConstants.FLAG_DISP_SHR);

                def output = tempFile.newReader(ZUtil.getDefaultPlatformEncoding());
                byte[] recBuf = null;
                int lineCounts=1;
                def dsInfo = new DsInfo(fileName);
                def truncateOnCurrentFile = false;
                try{
                    while((line = output.readLine())!= null){
                        def record =line.replaceAll(/\s+$/, '').padRight(dsInfo.getAvailableRecordLength()).toString();
                        recBuf =record.getBytes(ZUtil.getDefaultPlatformEncoding());
                        if(recBuf.length > dsInfo.getAvailableRecordLength()){
                            if(failOnTruncate){
                                truncateOnCurrentFile = true;
                            }

                            println "Warning: line $lineCounts of ${fileName} excceeds ${dsInfo.getAvailableRecordLength()} bytes and will be truncated."
                            println " $lineCounts of ${fileName}: $line"
                            target.write(recBuf, 0, dsInfo.getAvailableRecordLength());
                        }
                        else{
                            target.write(recBuf, 0, recBuf.length);
                        }
                        lineCounts++;
                    }
                    output.close();
                    tempFile.delete();
                    if(target) target.close();

                    if(ispfstats != null){
                        //update ISPFStats 
                        lineCounts --;
                        updateISPFStats(ispfstats, lineCounts, 0)
                    }

                    if (failOnTruncate && truncateOnCurrentFile) {
                        println "Error: failed for truncated.";
                        System.exit(1);
                    }
                }catch(e){
                    e.printStackTrace();
                    System.exit(1);
                }
            }
        }

    }
    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();
        def tmpDirectory = workDirPath.toFile();
        if (tmpDirectory.exists() && tmpDirectory.isDirectory()) {
            tmpDirectory.deleteDir();
        }
    }
}

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 = DataSetHelper.makeRegexPattren("$excludeDSPattern($excludeMBRPattern)")

            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
}


ISPFStatistics getISPFStats(String name){
    if(!name.startsWith("//'") || !name.endsWith(")'")){
        return null;
    }

    name = name.substring(3,name.length()-1);
    dataset = name.substring(0, name.indexOf("("));
    member = name.substring(name.indexOf("(")+1, name.length()-1);

    ISPFStatistics stats = new ISPFStatistics(dataset, member);
    return stats;
}

boolean updateISPFStats(ISPFStatistics stats, int currentLines, int modifiedLines){
    if(stats == null){
        return false;
    }
    try {
        Calendar cal = Calendar.getInstance();
        stats.setModificationDate(cal.getTime());
        stats.setModifiedLines(modifiedLines);
        stats.setCurrentLines(currentLines)
        stats.setUserId(ZUtil.getCurrentUser());
        stats.save();
        return true;
    } catch (Exception e) {
        e.printStackTrace();
        return false;
    }
}