import java.io.File;

import groovy.json.JsonSlurper
import groovy.json.JsonBuilder
import groovy.io.FileType

import com.urbancode.air.AirPluginTool;

def apTool = new AirPluginTool(this.args[0], this.args[1]);
def props = apTool.getStepProperties();

def outputDirString = props['outputDir'].trim();
def clusterName = props['clusterName'].trim();
def useMultiplicity = props['useMultiplicity'].trim();
def discoveryRunOnCluster = props['discoveryRunOnCluster'].trim();
def configFile = props['configFile'].trim();
def cellName = props['cellName'].trim();
def userDefinedTokenization = props['userDefinedTokenization'].trim();
def startTokenDelimiter = props['startTokenDelimiter'].trim();
def endTokenDelimiter = props['endTokenDelimiter'].trim();

// make sure the working directory ends with /
lastCarOfWorkingDir = outputDirString.substring(outputDirString.length() - 1)
if (lastCarOfWorkingDir != "/" && lastCarOfWorkingDir != "\\") {
  outputDirString = outputDirString + File.separator
}

if (discoveryRunOnCluster == "false") {
  // the templates directory
  templateDirString = outputDirString + "templates" + File.separator
  templateDir = new File(templateDirString)
  if (!templateDir.exists()) {
    templateDir.mkdirs()
  }

  // the extracted directory
  extractedDirString = outputDirString + "extracted" + File.separator
  extractedDir = new File(extractedDirString)

  // get the cell name
  cellDirString = extractedDirString + "Cell" + File.separator + "cell" + File.separator + "Cell.json"
  cellJson = new File(cellDirString)
  cellName = getRolePropertyValueFromFile(cellJson, "websphere.cell")

  directories = getAllDirsInDir(extractedDir)
  directories.each {
    if (it.getName().startsWith("Cluster-")){
      if (it.getName() == "Cluster-" + clusterName || clusterName.equalsIgnoreCase("ALL")) {
        println "Starting to templatize " + it
        // copy cluster directory to template folder
        sourceDir = it.getParent() + File.separator + it.getName()
        newClusterDirString = templateDirString + it.getName()
        newClusterDir = new File(newClusterDirString)
        if (newClusterDir.exists()) {
          newClusterDir.deleteDir()
        }
        newClusterDir.mkdirs()
        println "Copying " + sourceDir + " to " + newClusterDirString
        new AntBuilder().copy( todir: newClusterDirString ) {
          fileset( dir: sourceDir )
        }
        // get the cluster name
        clusterDirString = newClusterDirString + File.separator + "Cluster.json"
        clusterJson = new File(clusterDirString)
        clusterNameFromJson = getRolePropertyValueFromFile(clusterJson, "websphere.cluster")
        templatizeCluster(clusterDirString, cellName, useMultiplicity, discoveryRunOnCluster, clusterNameFromJson, false, userDefinedTokenization, startTokenDelimiter, endTokenDelimiter)
        // templatize the dynamic cluster server template, if it exists
        dcServerTemplateString = newClusterDirString + File.separator + "DynamicClusterServerTemplate.json"
        dcServerTemplate = new File(dcServerTemplateString)
        if (dcServerTemplate.exists()) {
          templatizeCluster(dcServerTemplateString, cellName, useMultiplicity, discoveryRunOnCluster, clusterNameFromJson, true, userDefinedTokenization, startTokenDelimiter, endTokenDelimiter)
        }
      }
    }
  }
} else {
  println "Discovery was run on a cluster only..."
  // the output directory
  outputDir = new File(outputDirString)
  if (!outputDir.exists()) {
    outputDir.mkdirs()
  }
  // discovery was run on the cluster only, so we need a config file and cell name
  if (configFile == "") {
    throw new RuntimeException("Error: No config file was specifed. A config file is required when discovery was run on a cluster only.");
  }
  if (cellName == "") {
    throw new RuntimeException("Error: No cell name was specifed. A cell name is required when discovery was run on a cluster only.");
  }
  file = new File(configFile)
  newClusterDirString = outputDirString + "Cluster.json"
  newClusterFile = new File(newClusterDirString)
  if (newClusterFile.exists()) {
    println "Deleting old copy of " + newClusterFile
    newClusterFile.delete()
  }
  // copy cluster config file to outputDir
  println "Copying " + file.getName() + " from " + file.getParent() + " to " + outputDirString + "..."
  newClusterFile = new File(newClusterDirString) << new File(configFile).text
  templatizeCluster(newClusterDirString, cellName, useMultiplicity, discoveryRunOnCluster, clusterName, false, userDefinedTokenization, startTokenDelimiter, endTokenDelimiter)

  // determine if this is a dynamic cluster
  isDynamicCluster = false
  newClusterFile = new File(newClusterDirString)
  dcName = getRolePropertyValueFromFile(newClusterFile, "websphere.dynamiccluster.name")
  if (dcName != "") {
    isDynamicCluster = true
  }

  // extract the server template, if it's a dynamic cluster
  if (isDynamicCluster) {
    println "Extracting the dynamic cluster server template for cluster " + clusterName + "..."
    newClusterFile = new File(newClusterDirString)
    extractresourceswithtypeandchildren(newClusterFile, "WebSphereDynamicClusterServerTemplate", "DynamicClusterServerTemplate.json", true)
    // remove brackets
    dcServerTemplateFile = new File(outputDirString + file.separator + "DynamicClusterServerTemplate.json")
    removeBracketsFromJson(dcServerTemplateFile)
    newClusterFile = new File(newClusterDirString)
    removeBracketsFromJson(newClusterFile)
    // fix double quotes
    dcServerTemplateFile = new File(outputDirString + file.separator + "DynamicClusterServerTemplate.json")
    fixDoubleQuotes(dcServerTemplateFile)
    newClusterFile = new File(newClusterDirString)
    fixDoubleQuotes(newClusterFile)
  }
}





// method to templatize a cluster
void templatizeCluster(String configFileString, String cellName, String useMultiplicity, String discoveryRunOnCluster, String clusterName, boolean isServerTemplate, String userDefinedTokenization, String startTokenDelimiter, String endTokenDelimiter) {

  // tokenize Cluster.json
  println "Tokenizing values for " + clusterName + "'s Cluster.json..."
  clusterJson = new File(configFileString)
  tokenizeRolePropertyValues(clusterJson, clusterName, startTokenDelimiter + "websphere.cluster" + endTokenDelimiter)
  clusterJson = new File(configFileString)
  String clusterFileContents = clusterJson.getText('UTF-8')
  if (discoveryRunOnCluster == "true") {
    def list = new JsonSlurper().parseText(clusterFileContents)
    list.each {
      if(it.keySet().contains('roleName')) {
        if (it.roleName == "WebSphereDynamicCluster"){
          it.path = "/" + startTokenDelimiter + "websphere.cell" + endTokenDelimiter + "/Dynamic Clusters/" + startTokenDelimiter + "websphere.cluster" + endTokenDelimiter
        }
      }
    }
    clusterFileContents = new JsonBuilder(list).toPrettyString()
    // fix paths since discovery was run on a cluster only
    clusterFileContents = fixPaths(clusterFileContents, clusterName, startTokenDelimiter, endTokenDelimiter)
  }
  def list = new JsonSlurper().parseText(clusterFileContents)
  list.each {
    if(it.keySet().contains('roleName')) {
      if (it.roleName == "WebSphereCluster"){
        it.path = "/" + startTokenDelimiter + "websphere.cell" + endTokenDelimiter + "/ServerClusters/" + startTokenDelimiter + "websphere.cluster" + endTokenDelimiter
        it.name = startTokenDelimiter + "websphere.cluster" + endTokenDelimiter
      }
    }
  }
  clusterFileContents = new JsonBuilder(list).toPrettyString()
  clusterFileContents = clusterFileContents.replace("/" + cellName + "/ServerClusters/", "/" + startTokenDelimiter + "websphere.cell" + endTokenDelimiter + "/ServerClusters/")
  clusterFileContents = clusterFileContents.replace("(cell):" + cellName, "(cell):" + startTokenDelimiter + "websphere.cell" + endTokenDelimiter + "")
  clusterFileContents = clusterFileContents.replace("/ServerClusters/" + clusterName + "/", "/ServerClusters/" + startTokenDelimiter + "websphere.cluster" + endTokenDelimiter + "/")
  clusterFileContents = clusterFileContents.replace("(cluster):" + clusterName, "(cluster):" + startTokenDelimiter + "websphere.cluster" + endTokenDelimiter)
  // just to be safe, tokenize anything we may have missed
  clusterFileContents = clusterFileContents.replace(cellName, startTokenDelimiter + "websphere.cell" + endTokenDelimiter)
  clusterFileContents = clusterFileContents.replace(clusterName, startTokenDelimiter + "websphere.cluster" + endTokenDelimiter)
  clusterJson.delete()
  new File(clusterJson.getParent()+File.separator+clusterJson.getName()).write(clusterFileContents, 'UTF-8')

  isDynamicCluster = false
  if (!isServerTemplate) {
    // add parent property to Cluster.json
    println "Adding parent property to " + clusterName + "'s Cluster.json..."
    clusterJson = new File(configFileString)
    addPropertyToResource(clusterJson, "/" + startTokenDelimiter + "websphere.cell" + endTokenDelimiter + "/ServerClusters/" + startTokenDelimiter + "websphere.cluster" + endTokenDelimiter, "parent", "{ \"name\": \"ServerClusters\", \"path\": \"/" + startTokenDelimiter + "websphere.cell" + endTokenDelimiter + "/ServerClusters\", \"parent\": { \"roleName\": \"WebSphereCell\", \"name\": \"" + startTokenDelimiter + "websphere.cell" + endTokenDelimiter + "\", \"path\": \"/" + startTokenDelimiter + "websphere.cell" + endTokenDelimiter + "\", \"roleProperties\": { \"websphere.cell\":\"" + startTokenDelimiter + "websphere.cell" + endTokenDelimiter + "\" } } }")
    // determine if this is a dynamic cluster
    dcName = getRolePropertyValueFromFile(clusterJson, "websphere.dynamiccluster.name")
    if (dcName != "") {
      println "Cluster " + clusterName + " is a dynamic cluster..."
      isDynamicCluster = true
    }
  }

  // remove the cluster member config data if using multiplicity feature or if it's a dynamic cluster
  if (useMultiplicity == "true" || isDynamicCluster) {
    clusterJson = new File(configFileString)
    println "Removing cluster member config data..."
    extractresourceswithtypeandchildren(clusterJson, "WebSphereClusterMember", "", false)
  }

  // tokenize user defined values
  clusterJson = new File(configFileString)
  doUserDefinedTokenization(clusterJson, userDefinedTokenization)

  // remove brackets
  clusterJson = new File(configFileString)
  removeBracketsFromJson(clusterJson)

  // fix double quotes
  clusterJson = new File(configFileString)
  fixDoubleQuotes(clusterJson)

}

// method to fix "" saving as "\"\""
void fixDoubleQuotes(File file) {
  String fileContents = file.getText('UTF-8')
  fileContents = fileContents.replace("\"\\\"\\\"\"", "\"\"")
  file.delete()
  new File(file.getParent()+File.separator+file.getName()).write(fileContents, 'UTF-8')
}

// method to remove brackets from JSON
void removeBracketsFromJson(File file) {
  String fileContents = file.getText('UTF-8')
  fileContents = fileContents.trim()
  firstChar = fileContents.charAt(0)
  if (firstChar == "[") {
	startIndex = 1
	endIndex = fileContents.length()-1
	fileContents = fileContents.substring(startIndex, endIndex)
    file.delete()
    new File(file.getParent()+File.separator+file.getName()).write(fileContents, 'UTF-8')
  }
}

// method to add a property to a resource
void addPropertyToResource(File file, String path, String propertyName, String value) {
  String fileContents = file.getText('UTF-8')
  def list = new JsonSlurper().parseText(fileContents)

  // find the desired path
  def foundResource = []
  list.each {
    if (it.path == path){
      foundResource.push(it)
    }
  }

  // make sure we found one and only one resource, then add the property to it
  if (foundResource.size() > 1) {
    throw new RuntimeException("Error: found more than one path of " + path + " found in file " + file);
  } else if (foundResource.size() == 0) {
    throw new RuntimeException("Error: no path of " + path + " found in file " + file);
  } else {
    parsedValue = new JsonSlurper().parseText(value)
    foundResource[0][propertyName] = parsedValue
  }

  fileContents = new JsonBuilder(list).toPrettyString()
  file.delete()
  new File(file.getParent()+File.separator+file.getName()).write(fileContents, 'UTF-8')
}

// method to get a role property value
String getRolePropertyValueFromFile(File file, String rolePropertyKey) {
  String fileContents = file.getText('UTF-8')
  fileContents = fileContents.trim()
  firstChar = fileContents.charAt(0)
  if (firstChar != "[") {
    fileContents = "[" + fileContents + "]"
  }

  def list = new JsonSlurper().parseText(fileContents)
  value = ""
  list.each {
    if(it.keySet().contains( 'roleProperties' )) {
	  for (e in it.roleProperties) {
		if (e.key == rolePropertyKey) {
		  value = e.value
		}
	  }
	}
  }
  return value
}

// method to get all directories in a directory
List getAllDirsInDir(File dir) {
  def list = []
  dir.eachFileRecurse (FileType.DIRECTORIES) { directory ->
    // don't include the template dir
	directoryPath = directory.getParent() + File.separator + directory.getName()
	standardTemplateDirString = templateDirString.replaceAll("\\\\", "/")
	standardDirectoryPath = directoryPath.replaceAll("\\\\", "/")
	if (!standardDirectoryPath.startsWith(standardTemplateDirString)) {
	  list << directory
	}
  }
  return list
}

// method to extract a resource and its children and place it into a separate file
void extractresourceswithtypeandchildren(File file, String typeToExtract, String fileName, boolean createExtractedResourceFile = true) {
  String fileContents = file.getText('UTF-8')
  fileContents = fileContents.trim()
  firstChar = fileContents.charAt(0)
  if (firstChar != "[") {
    fileContents = "[" + fileContents + "]"
  }

  def list = new JsonSlurper().parseText(fileContents)

  // get the path of the resources we want to extract
  def pathsToExtract = []
  list.each {
	  if (it.roleName == typeToExtract){
	    pathsToExtract.push(it.path)
	  }
  }

  remainingResources = list

  // go through the resources and extract the ones which contain a path
  // of the resources we wish to extract.
  def indexesToRemove = []
  fileCounter = 0;
  pathsToExtract.each {
	def extractedResources = []
	indexCounter = 0
	path = it
	list.each {
	  if (it.path == path || it.path.indexOf(path + "/", 0) == 0) {
		extractedResources.push(it);
		// save the index of the resources we will wish to remove from the original file
		indexesToRemove.push(indexCounter)
	  }
	  indexCounter = indexCounter + 1
	}

	if (extractedResources.size() > 0 && createExtractedResourceFile == true) {
	  extractedFileContents = new JsonBuilder(extractedResources).toPrettyString()
	  new File(file.getParent()+File.separator+fileName).write(extractedFileContents, 'UTF-8')
	  fileCounter = fileCounter + 1
	}
  }

  // remove resources
  i = 0;
  for (index in indexesToRemove) {
	indexToRemove = index - i
	remainingResources.remove(indexToRemove)
	i = i + 1;
  }

  if (remainingResources.size() > 0) {
	remainingFileContents = new JsonBuilder(remainingResources).toPrettyString()
	file.delete()
	new File(file.getParent()+File.separator+file.getName()).write(remainingFileContents, 'UTF-8')
  }
}

// method to handle user defined tokenization
void doUserDefinedTokenization(File file, String userDefinedTokenization) {
  String fileContents = file.getText('UTF-8')
  lines = userDefinedTokenization.split('\n')
  for (line in lines) {
    if (line.indexOf("->") >= 0) {
      oldStart = 0
      oldEnd = line.indexOf("->")
      oldValue = line.substring(oldStart, oldEnd)
      newStart = line.indexOf("->") + 2
      newEnd = line.length();
      newValue = line.substring(newStart, newEnd)
      println "Replacing " + oldValue + " with " + newValue
      fileContents = fileContents.replace(oldValue, newValue)
    }
  }
  file.delete()
  new File(file.getParent()+File.separator+file.getName()).write(fileContents, 'UTF-8')
}

// method to tokenize role properties only
void tokenizeRolePropertyValues(File file, String propertyToTokenize, String token) {
  String fileContents = file.getText('UTF-8')
  def list = new JsonSlurper().parseText(fileContents)
  found = false

  // get all of the role properties and check their values
  list.each {
    if(it.keySet().contains( 'roleProperties' )) {
      for (e in it.roleProperties) {
        if (e.value in String) {
          if (e.value.contains(propertyToTokenize)) {
            e.value = e.value.replace(propertyToTokenize, token)
            found = true
          }
        }
      }
    }
  }

  if (found) {
    println "Tokenizing value " + propertyToTokenize + " to " + token + " for file " + file.getName()
    fileContents = new JsonBuilder(list).toPrettyString()
    file.delete()
    new File(file.getParent()+File.separator+file.getName()).write(fileContents, 'UTF-8')
  }
}

// method to fix the paths if discovery was run on a cluster only
String fixPaths(String fileContents, String clusterName, String startTokenDelimiter, String endTokenDelimiter) {
  def list = new JsonSlurper().parseText(fileContents)
  found = false
  list.each {
    if(it.keySet().contains( 'path' )) {
      it.path = it.path.replaceFirst("/" + clusterName + "/Dynamic Cluster Server Templates/" + clusterName, "/" + startTokenDelimiter + "websphere.cell" + endTokenDelimiter + "/Dynamic Clusters/" + startTokenDelimiter + "websphere.cluster" + endTokenDelimiter + "/Dynamic Cluster Server Templates/" + startTokenDelimiter + "websphere.cluster" + endTokenDelimiter)
      it.path = it.path.replaceFirst("/" + clusterName + "/Dynamic Cluster Server Templates", "/" + startTokenDelimiter + "websphere.cell" + endTokenDelimiter + "/Dynamic Clusters/" + startTokenDelimiter + "websphere.cluster" + endTokenDelimiter + "/Dynamic Cluster Server Templates")
      it.path = it.path.replaceFirst("/" + clusterName + "/", "/" + startTokenDelimiter + "websphere.cell" + endTokenDelimiter + "/ServerClusters/" + startTokenDelimiter + "websphere.cluster" + endTokenDelimiter + "/")
      it.path = it.path.replaceFirst("/" + clusterName + '"', "/" + startTokenDelimiter + "websphere.cell" + endTokenDelimiter + "/ServerClusters/" + startTokenDelimiter + "websphere.cluster" + endTokenDelimiter)
      found = true
    }
  }
  if (found) {
    println "Fixing paths since discovery was run on a Cluster only..."
    fileContents = new JsonBuilder(list).toPrettyString()
  }
  return fileContents;
}
