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 nodeName = props['nodeName'].trim();
def discoveryRunOnNode = props['discoveryRunOnNode'].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 (discoveryRunOnNode == "false") {
  // the extracted directory
  extractedDirString = outputDirString + "extracted" + File.separator

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

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

  // make directories and copy files over
  oldNodeDirString = extractedDirString + "Node-" + nodeName
  newNodeDirString = templateDirString + "Node-" + nodeName
  newNodeDir = new File(newNodeDirString)
  if (newNodeDir.exists()) {
    newNodeDir.deleteDir()
  }
  newNodeDir.mkdirs()
  new AntBuilder().copy( file: oldNodeDirString + File.separator + "node" + File.separator + "Node.json",
    tofile: newNodeDirString + File.separator + "node" + File.separator + "Node.json")
  // nodeagent may not exist (for example, if using WAS Base)
  nodeagentJson = new File(oldNodeDirString + "/nodeagent/nodeagent.json")
  if (nodeagentJson.exists()) {
    new AntBuilder().copy( file: oldNodeDirString + File.separator + "nodeagent" + File.separator + "nodeagent.json",
      tofile: newNodeDirString + File.separator + "nodeagent" + File.separator + "nodeagent.json")
  }

  // get the Node.json file
  configFile = newNodeDirString + File.separator + "node" + File.separator + "Node.json"
  nodeJson = new File(configFile)

}
else {
  println "Discovery was run on a node only..."
  // the output directory
  outputDir = new File(outputDirString)
  if (!outputDir.exists()) {
    outputDir.mkdirs()
  }
  // discovery was run on the node 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 node only.");
  }
  if (cellName == "") {
    throw new RuntimeException("Error: No cell name was specifed. A cell name is required when discovery was run on a node only.");
  }
  file = new File(configFile)
  newNodeFileString = outputDirString + "Node.json"
  nodeJson = new File(newNodeFileString)
  if (nodeJson.exists()) {
    println "Deleting old copy of " + nodeJson
    nodeJson.delete()
  }
  // copy the config file to outputDir
  println "Copying " + file.getName() + " from " + file.getParent() + " to " + outputDirString + "..."
  new AntBuilder().copy( file: configFile,
    tofile: newNodeFileString)

  configFile = newNodeFileString
}

// get the node hostname
String hostName = getRolePropertyValueFromFile(nodeJson, "websphere.node.hostname")

// tokenize cell name, node name, and host name for Node.json
println "Tokenizing values for Node.json..."


if (discoveryRunOnNode == "true") {
  fixPaths(nodeJson, nodeName, startTokenDelimiter, endTokenDelimiter)
}
else {
  fileContents = nodeJson.getText('UTF-8')
  fileContents = fileContents.replace("/Nodes/" + nodeName, "/Nodes/" + startTokenDelimiter + "websphere.node" + endTokenDelimiter)
  nodeJson.delete()
  new File(nodeJson.getParent()+File.separator+nodeJson.getName()).write(fileContents, 'UTF-8')
}
nodeJson = new File(configFile)
fileContents = nodeJson.getText('UTF-8')

fileContents = fileContents.replace("(node):" + nodeName, "(node):" + startTokenDelimiter + "websphere.node" + endTokenDelimiter)
fileContents = fileContents.replace("/" + cellName + "/", "/" + startTokenDelimiter + "websphere.cell" + endTokenDelimiter + "/")
fileContents = fileContents.replace("(cell):" + cellName, "(cell):" + startTokenDelimiter + "websphere.cell" + endTokenDelimiter)
nodeJson.delete()
new File(nodeJson.getParent()+File.separator+nodeJson.getName()).write(fileContents, 'UTF-8')
nodeJson = new File(configFile)
tokenizeRolePropertyValues(nodeJson, hostName, startTokenDelimiter + "websphere.node.hostname" + endTokenDelimiter)

// just to be safe, tokenize anything we may have missed
nodeJson = new File(configFile)
fileContents = nodeJson.getText('UTF-8')
fileContents = fileContents.replace(nodeName, startTokenDelimiter + "websphere.node" + endTokenDelimiter)
fileContents = fileContents.replace(cellName, startTokenDelimiter + "websphere.cell" + endTokenDelimiter)

nodeJson.delete()
new File(nodeJson.getParent()+File.separator+nodeJson.getName()).write(fileContents, 'UTF-8')

// find and tokenize the profile name, if there is one.
nodeJson = new File(configFile)
tokenizeProfileName(nodeJson, startTokenDelimiter, endTokenDelimiter)

// remove Server Entries
println "Removing Server Entries from Node.json, if needed..."
nodeJson = new File(configFile)
removeResourcesUnderPath(nodeJson, "/" + startTokenDelimiter + "websphere.cell" + endTokenDelimiter + "/Nodes/" + startTokenDelimiter + "websphere.node" + endTokenDelimiter + "/Server Indices/ServerIndex/Server Entries")

// add parent property to Node.json
println "Adding parent property to Node.json..."
nodeJson = new File(configFile)
addPropertyToResource(nodeJson, "/" + startTokenDelimiter + "websphere.cell" + endTokenDelimiter + "/Nodes/" + startTokenDelimiter + "websphere.node" + endTokenDelimiter, "parent", "{ \"name\": \"Nodes\", \"path\": \"/" + startTokenDelimiter + "websphere.cell" + endTokenDelimiter + "/Nodes\", \"parent\": { \"roleName\": \"WebSphereCell\", \"name\": \"" + startTokenDelimiter + "websphere.cell" + endTokenDelimiter + "\", \"path\": \"/" + startTokenDelimiter + "websphere.cell" + endTokenDelimiter + "\", \"roleProperties\":  { \"websphere.cell\": \"" + startTokenDelimiter + "websphere.cell" + endTokenDelimiter + "\" } } }")

// tokenize user defined values
nodeJson = new File(configFile)
doUserDefinedTokenization(nodeJson, userDefinedTokenization)

// remove brackets
nodeJson = new File(configFile)
removeBracketsFromJson(nodeJson)

// fix double quotes
nodeJson = new File(configFile)
fixDoubleQuotes(nodeJson)

if (discoveryRunOnNode == "false") {
  // get the nodeagent.json file
  nodeagentJsonString = newNodeDirString + File.separator + "nodeagent" + File.separator + "nodeagent.json"
  nodeagentJson = new File(nodeagentJsonString)

  if (nodeagentJson.exists()) {
    // tokenize cell name, node name, and host name for nodeagent.json
    println "Tokenizing values for nodeagent.json..."
    nodeagentJson = new File(nodeagentJsonString)
    String nodeAgentFileContents = nodeagentJson.getText('UTF-8')
    nodeAgentFileContents = nodeAgentFileContents.replace("/Nodes/" + nodeName + "/Servers/", "/Nodes/" + startTokenDelimiter + "websphere.node" + endTokenDelimiter + "/Servers/")
    nodeAgentFileContents = nodeAgentFileContents.replace("(node):" + nodeName, "(node):" + startTokenDelimiter + "websphere.node" + endTokenDelimiter)
    nodeAgentFileContents = nodeAgentFileContents.replace("/" + cellName + "/", "/" + startTokenDelimiter + "websphere.cell" + endTokenDelimiter + "/")
    nodeAgentFileContents = nodeAgentFileContents.replace("(cell):" + cellName, "(cell):" + startTokenDelimiter + "websphere.cell" + endTokenDelimiter)
    nodeagentJson.delete()
    new File(nodeagentJson.getParent()+File.separator+nodeagentJson.getName()).write(nodeAgentFileContents, 'UTF-8')
    nodeagentJson = new File(nodeagentJsonString)
    tokenizeRolePropertyValues(nodeagentJson, hostName, startTokenDelimiter + "websphere.node.hostname" + endTokenDelimiter)

    // just to be safe, tokenize anything we may have missed
    nodeagentJson = new File(nodeagentJsonString)
    nodeAgentFileContents = nodeagentJson.getText('UTF-8')
    nodeAgentFileContents = nodeAgentFileContents.replace(nodeName, startTokenDelimiter + "websphere.node" + endTokenDelimiter)
    nodeAgentFileContents = nodeAgentFileContents.replace(cellName, startTokenDelimiter + "websphere.cell" + endTokenDelimiter)

    nodeagentJson.delete()
    new File(nodeagentJson.getParent()+File.separator+nodeagentJson.getName()).write(nodeAgentFileContents, 'UTF-8')

    // find and tokenize the profile name, if there is one.
    nodeagentJson = new File(nodeagentJsonString)
    tokenizeProfileName(nodeagentJson, startTokenDelimiter, endTokenDelimiter)

    // add parent property to nodeagent.json
    println "Adding parent property to nodeagent.json..."
    nodeagentJson = new File(nodeagentJsonString)
    addPropertyToResource(nodeagentJson, "/" + startTokenDelimiter + "websphere.cell" + endTokenDelimiter + "/Nodes/" + startTokenDelimiter + "websphere.node" + endTokenDelimiter + "/Servers/nodeagent", "parent", "{ \"name\": \"Servers\", \"path\": \"/" + startTokenDelimiter + "websphere.cell" + endTokenDelimiter + "/Nodes/" + startTokenDelimiter + "websphere.node" + endTokenDelimiter + "/Servers\", \"parent\": { \"name\": \"" + startTokenDelimiter + "websphere.node" + endTokenDelimiter + "\", \"path\": \"/" + startTokenDelimiter + "websphere.cell" + endTokenDelimiter + "/Nodes/" + startTokenDelimiter + "websphere.node" + endTokenDelimiter + "\", \"roleName\": \"WebSphereNode\", \"roleProperties\": { \"websphere.node\": \"" + startTokenDelimiter + "websphere.node" + endTokenDelimiter + "\" }, \"parent\": { \"name\": \"Nodes\", \"path\": \"/" + startTokenDelimiter + "websphere.cell" + endTokenDelimiter + "/Nodes\", \"parent\": { \"roleName\": \"WebSphereCell\", \"name\": \"" + startTokenDelimiter + "websphere.cell" + endTokenDelimiter + "\", \"path\": \"/" + startTokenDelimiter + "websphere.cell" + endTokenDelimiter + "\", \"roleProperties\": { \"websphere.cell\": \"" + startTokenDelimiter + "websphere.cell" + endTokenDelimiter + "\" } } } } }")

    // tokenize user defined values
    nodeagentJson = new File(nodeagentJsonString)
    doUserDefinedTokenization(nodeagentJson, userDefinedTokenization)

    // remove brackets
    nodeagentJson = new File(nodeagentJsonString)
    removeBracketsFromJson(nodeagentJson)

    // fix double quotes
    nodeagentJson = new File(nodeagentJsonString)
    fixDoubleQuotes(nodeagentJson)
  }
}





// 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 remove resources under a path
void removeResourcesUnderPath(File file, String path) {
  String fileContents = file.getText('UTF-8')
  def list = new JsonSlurper().parseText(fileContents)

  def newArrayObj = []
  list.each {
    if (it.path.lastIndexOf(path, 0) != 0){
    newArrayObj.push(it)
    }
  }

  contents = new JsonBuilder(newArrayObj).toPrettyString()
  file.delete()
  new File(file.getParent()+File.separator+file.getName()).write(contents, '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')
  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 tokenize profile name
void tokenizeProfileName(File file, String startTokenDelimiter, String endTokenDelimiter) {
  String fileContents = file.getText('UTF-8')
  def list = new JsonSlurper().parseText(fileContents)
  foundProfileName = false

  // find the profile name, assuming it's under /profiles/
  list.each {
    if(it.keySet().contains( 'roleProperties' )) {
      for (e in it.roleProperties) {
        if (e.key == "websphere.variablemap.entries") {
          if (e.value in String) {
            if (e.value.contains("/profiles/")) {
              profileNameRegex = e.value =~ /\/profiles\/(.*?)(\n|\/)/;
              profileName = profileNameRegex[0][1];
              // replace the profile name with a token
              println "Tokenizing profile name " + profileName + " in " + file.getName()
              e.value = e.value.replace(profileName, startTokenDelimiter + "websphere.node.profilename" + endTokenDelimiter)
              foundProfileName = true
            }
          }
        }
      }
    }
  }

  if (foundProfileName) {
    fileContents = new JsonBuilder(list).toPrettyString()
    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 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 fix the paths if discovery was run on a node only
void fixPaths(File file, String nodeName, String startTokenDelimiter, String endTokenDelimiter) {
  String fileContents = file.getText('UTF-8')
  def list = new JsonSlurper().parseText(fileContents)
  found = false
  list.each {
    if(it.keySet().contains( 'path' )) {
      it.path = it.path.replace("/" + nodeName + "/", "/" + startTokenDelimiter + "websphere.cell" + endTokenDelimiter + "/Nodes/" + startTokenDelimiter + "websphere.node" + endTokenDelimiter + "/")
      it.path = it.path.replace("/" + nodeName, "/" + startTokenDelimiter + "websphere.cell" + endTokenDelimiter + "/Nodes/" + startTokenDelimiter + "websphere.node" + endTokenDelimiter)
      found = true
    }
  }

  if (found) {
    println "Fixing paths since discovery was run on a Node only..."
    fileContents = new JsonBuilder(list).toPrettyString()
    file.delete()
    new File(file.getParent()+File.separator+file.getName()).write(fileContents, 'UTF-8')
  }
}
