/*
* 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.urbancode.air.AirPluginTool
import com.urbancode.air.FileSet
import javax.xml.parsers.DocumentBuilder
import javax.xml.parsers.DocumentBuilderFactory

final def workDir = new File('.').canonicalFile
final def airTool = new AirPluginTool(args[0], args[1])
final def props = airTool.getStepProperties()

enum FailMode {
    
        //**************************************************************************
        // CLASS
        //**************************************************************************
        WARN_ONLY,
        FAIL_FAST,
        FAIL_ON_ANY_FAILURE    
        //**************************************************************************
        // INSTANCE
        //**************************************************************************
}

final def srcDirOffset = props['srcDirOffset']
final def destDirOffset = props['destDirOffset']
final def failMode = FailMode.valueOf(props['failMode'])
final def includes = props['includes']
final def excludes = props['excludes']
final def replaceText = props['replaceText']
final def setAttr = props['setAttr']
final def removeList = props['remove']
final def insertList = props['insert']
final def failWithoutMatch = props['failWithoutMatch']
final def ignoreDTD = Boolean.valueOf(props['ignoreDTD'])
final def prettyPrint = Boolean.valueOf(props['prettyPrint'])

def ant = new AntBuilder()
FileSet fs

def separator = '\n|[\n]|\r\n|[\r\n]'
def unparsedIncludes = includes.replaceAll(separator, ",")
def formattedIncludes = unparsedIncludes.split(',')*.trim()
ant.taskdef(name:'xmltask', classname:'com.oopsconsultancy.xmltask.ant.XmlTask')

if (!srcDirOffset) {
    srcDirOffset = '.'
}

if (!destDirOffset) {
    destDirOffset = '.'
}

println "Working Directory: ${workDir}"
println "Source Directory Offset: ${srcDirOffset}"
println "Destination Directory Offset: ${destDirOffset}"
println "File Includes: ${includes}"
println "File Excludes: ${excludes}"
println "-----------------------------"

def wellFormedXmlIncludes = []
def malFormedXmlIncludes = []

//Keeps track of malformed xmls
def errorCount = 0
for(def xml in  formattedIncludes){
   
    def fileName =  xml.toString().trim()
    DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance()
    DocumentBuilder db = dbf.newDocumentBuilder()
    try {
        db.parse(new File(srcDirOffset + workDir.separator + fileName))
        wellFormedXmlIncludes.add(fileName)
    }catch( Exception e ) {
        malFormedXmlIncludes.add(fileName)
        errorCount++;
        if (failMode == FailMode.FAIL_FAST) {
            println "Fail Fast: xml malformed ${fileName}"
            e.printStackTrace()
            throw new IllegalStateException("Xml malformed.")
        }
        println e.getMessage()
      }
}

println "Wellformed xmls : ${wellFormedXmlIncludes}"
println "Malformed xmls : ${malFormedXmlIncludes}"

if (ignoreDTD) {
    fs = new FileSet(new File(workDir, srcDirOffset))
    fs.include(wellFormedXmlIncludes)
    fs.exclude(excludes)

    println("[Warning] Temporarily commenting out all DOCTYPE declarations so they may be ignored.")

    // comment out all DOCTYPE declarations
    for(def file : fs.files()) {
        fileText = file.text
        file.withWriter{ w -> w << fileText.replaceAll("(<!DOCTYPE.*>)", "<!--\$1-->")}
    }
}

try {
    ant.xmltask(expandEntityReferences:'false',
            report:'false',
            failWithoutMatch:failWithoutMatch,
            outputter: prettyPrint?'simple':'default',
            toDir: new File(workDir, destDirOffset)) {
        fileset(dir:srcDirOffset) {
            wellFormedXmlIncludes.each {
                if (it && it.trim().length() > 0) {
                    include(name:it.trim())
                }
            }

            if(excludes) {
                excludes.split('\n').each {
                    if (it && it.trim().length() > 0) {
                        exclude(name:it.trim())
                    }
                }
            }
        }

        if (replaceText) {
            replaceText.split('\n').each {
                if (it && it.trim().length() > 0) {
                    def index = it.indexOf('->')
                    if ( index > 0) {
                        println "Replace ${it[0..index - 1].trim()} with ${it[index + 2..-1]}"
                        replace(path:it[0..index - 1].trim(), withText:it[index + 2..-1], expandProperties:'false')
                    }
                    else {
                        println "Invalid replace text rule found: $it"
                        System.exit 1
                    }
                }
            }
        }

        if (insertList) {
            insertList.split('\n').each {
                if (it && it.trim().length() > 0) {
                    def index = it.indexOf('->')
                    if ( index > 0) {
                        println "Inserting\n${it[index + 2..-1]}\nunder ${it[0..index - 1].trim()}"
                        insert(
                                path:it[0..index - 1].trim(),
                                position:'under',
                                expandProperties:'false',
                                xml:it[index + 2..-1]) {
                        }
                    }
                    else {
                        println "Invalid insert rule found: $it"
                        System.exit 1
                    }
                }
            }
        }

        if (setAttr) {
            setAttr.split('\n').each {
                if (it && it.trim().length() > 0) {
                    def separatorIndex = it.indexOf('->')
                    def attributeIndex = it.indexOf('/@')
                    if ( separatorIndex > 0 && attributeIndex > 0) {
                        def element = it.substring(0, attributeIndex)
                        def attribute = it.substring(attributeIndex + 2, separatorIndex)
                        println "Set attribute $attribute on element $element to ${it[separatorIndex + 2..-1]}"
                        attr(path:element.trim(), attr: attribute.trim(), value:it[separatorIndex + 2..-1])
                    }
                    else {
                        println "Invalid replace text rule found: $it"
                        System.exit 1
                    }
                }
            }
        }

        if (removeList) {
            removeList.split('\n').each {
                if (it && it.trim().length() > 0) {
                    println "Removing ${it.trim()}"
                    remove(path:"${it.trim()}")
                }
            }
        }
    }
}
catch (Exception e) {
    e.printStackTrace()
    System.exit 1
}
finally {
    if (ignoreDTD) {
        println("[Action] Removing temporary doctype comments.")

        for (def file : fs.files()) {
            def fileText = file.text
            file.withWriter{ w -> w << fileText.replaceAll("<!--(<!DOCTYPE.*>)-->", "\n\$1\n")}
        }
    }
}

//------------------------------------------------------------------------------
// Check post conditions
//------------------------------------------------------------------------------

if (failMode == FailMode.FAIL_ON_ANY_FAILURE && errorCount > 0) {
    throw new IllegalStateException("Fail On Any Failure: Got ${errorCount} failures!")
}

System.exit(0)

