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 serverName = props['serverName'].trim();
def nodeName = props['sourceNodeName'].trim();
def discoveryRunOnServer = props['discoveryRunOnServer'].trim();
def configFile = props['configFile'].trim();
def cellName = props['cellName'].trim();
def nodeHostName = props['nodeHostName'].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 (discoveryRunOnServer == "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()
  }

  // verify source node directory exists.  if it doesn't, the node probably wasn't extracted
  sourceNodeDirString = extractedDirString + "Node-" + nodeName
  sourceNodeDir = new File(sourceNodeDirString)
  if (!sourceNodeDir.exists()) {
    throw new Exception("Trying to templatize a server from node " + nodeName  +
                        ", however " + nodeName + " does not exist in " + sourceNodeDirString +
                        ". Did you extract " + nodeName + "?")
  }

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

  // get the host name
  sourceNodeJson = new File(extractedDirString + "Node-" + nodeName + File.separator + "node" + File.separator + "Node.json")
  nodeHostName = getRolePropertyValueFromFile(sourceNodeJson, "websphere.node.hostname")

  // the directory that contains the Server-serverName dir (example ..\extracted\Node-NodeName\node
  sourceNodeNodeDirString = extractedDirString + "Node-" + nodeName + File.separator + "node"
  sourceNodeNodeDir = new File(sourceNodeNodeDirString)

  directories = getAllDirsInDir(sourceNodeNodeDir)
  directories.each {
    if (it.getName().startsWith("Server-")){
      if (it.getName() == "Server-" + serverName || serverName.equalsIgnoreCase("ALL")) {
        println "Starting to templatize " + it
        String sourceDirString = it

        // get the server name (need to do this if user selected to templatize ALL servers)
        sourceServerDirString = sourceDirString + File.separator + "Server.json"
        sourceServerJson = new File(sourceServerDirString)
        sourceServerName = getRolePropertyValueFromFile(sourceServerJson, "websphere.server")

        // directory to place the Server.json in
        targetDirString = templateDirString + "Server-" + sourceServerName + "-from_node-"+ nodeName + File.separator

        // copy server info from extracted folder to templates folder
        targetDir = new File(targetDirString)
        if (targetDir.exists()) {
          targetDir.deleteDir()
        }
        targetDir.mkdirs()
        println "Copying " + sourceDirString + " to " + targetDirString
        new AntBuilder().copy( todir: targetDirString ) {
          fileset( dir: sourceDirString )
        }

        // templatize the server
        templatizeServer(targetDirString, cellName, nodeName, nodeHostName, sourceServerName, discoveryRunOnServer, userDefinedTokenization, startTokenDelimiter, endTokenDelimiter)
      }
    }
  }
} else {
  println "Discovery was run on a server only..."
  // discovery was run on the server only, so we need a config file, cell name, and node host name
  if (configFile == "") {
    throw new RuntimeException("Error: No config file was specifed. A config file is required when discovery was run on a server only."); 
  }
  if (cellName == "") {
    throw new RuntimeException("Error: No cell name was specifed. A cell name is required when discovery was run on a server only.");
  }
  if (nodeHostName == "") {
    throw new RuntimeException("Error: No node host name was specifed. A node host name is required when discovery was run on a server only.");
  }
  // the output directory
  outputDir = new File(outputDirString)
  if (!outputDir.exists()) {
    outputDir.mkdirs()
  }
  // copy the Server.json and directories to the output directory
  serverFileString = outputDirString + "Server.json"
  serverFile = new File(serverFileString)
  if (serverFile.exists()) {
    println "Deleting old copy of " + serverFileString
    serverFile.delete()
  }
  println "Copying the file from " + configFile + " to " + serverFileString
  new File(serverFileString) << new File(configFile).text
  templatizeServer(outputDirString, cellName, nodeName, nodeHostName, serverName, discoveryRunOnServer, userDefinedTokenization, startTokenDelimiter, endTokenDelimiter)
}



// 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 templatize a server
void templatizeServer(String targetDirString, String cellName, String nodeName, String nodeHostName, String serverName, String discoveryRunOnServer, String userDefinedTokenization, String startTokenDelimiter, String endTokenDelimiter) {

  // tokenize cell name, node name, and host name for Server.json
  serverFileString = targetDirString + "Server.json"
  serverFile = new File(serverFileString)
  println "Tokenizing values for " + serverFileString + "..."

  serverFile = new File(serverFileString)

  if (discoveryRunOnServer == "true") {
    fixPaths(serverFile, serverName, startTokenDelimiter, endTokenDelimiter)
    serverFile = new File(serverFileString)
  }
  String serverFileContents = serverFile.getText('UTF-8')
  serverFileContents = serverFileContents.replace("/Servers/" + serverName + "/", "/Servers/" + startTokenDelimiter + "websphere.server" + endTokenDelimiter + "/")
  serverFileContents = serverFileContents.replace("(server):" + serverName, "(server):" + startTokenDelimiter + "websphere.server" + endTokenDelimiter)
  serverFileContents = serverFileContents.replace("/Nodes/" + nodeName + "/Servers/", "/Nodes/" + startTokenDelimiter + "websphere.node" + endTokenDelimiter + "/Servers/")
  serverFileContents = serverFileContents.replace("(node):" + nodeName, "(node):" + startTokenDelimiter + "websphere.node" + endTokenDelimiter)
  serverFileContents = serverFileContents.replace("/" + cellName + "/", "/" + startTokenDelimiter + "websphere.cell" + endTokenDelimiter + "/")
  serverFileContents = serverFileContents.replace("(cell):" + cellName, "(cell):" + startTokenDelimiter + "websphere.cell" + endTokenDelimiter)
  serverFile.delete()
  new File(serverFile.getParent()+File.separator+serverFile.getName()).write(serverFileContents, 'UTF-8')

  serverFile = new File(serverFileString)
  tokenizeRolePropertyValues(serverFile, nodeHostName, startTokenDelimiter + "websphere.node.hostname" + endTokenDelimiter)

  serverFile = new File(serverFileString)
  tokenizeClusterName(serverFile, startTokenDelimiter, endTokenDelimiter)

  // just to be safe, tokenize anything we may have missed
  serverFile = new File(serverFileString)
  serverFileContents = serverFile.getText('UTF-8')
  serverFileContents = serverFileContents.replace(serverName, startTokenDelimiter + "websphere.server" + endTokenDelimiter)
  serverFileContents = serverFileContents.replace(nodeName, startTokenDelimiter + "websphere.node" + endTokenDelimiter)
  serverFileContents = serverFileContents.replace(cellName, startTokenDelimiter + "websphere.cell" + endTokenDelimiter)
  serverFile.delete()
  new File(serverFile.getParent()+File.separator+serverFile.getName()).write(serverFileContents, 'UTF-8')

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

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

  // tokenize user defined values
  serverFile = new File(serverFileString)
  doUserDefinedTokenization(serverFile, userDefinedTokenization)

  // remove brackets
  serverFile = new File(serverFileString)
  removeBracketsFromJson(serverFile)

  // fix double quotes
  serverFile = new File(serverFileString)
  fixDoubleQuotes(serverFile)
}

// 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 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 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 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 tokenize cluster name, if there is one
void tokenizeClusterName(File file, startTokenDelimiter, endTokenDelimiter) {
  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.key.equals( 'websphere.server.clustername' )) {
          e.value = startTokenDelimiter + "websphere.cluster" + endTokenDelimiter
          found = true
        }
      }
    }
  }

  if (found) {
    println "Tokenizing cluster name value 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 server only
void fixPaths(File file, String serverName, 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("/" + serverName + "/", "/" + startTokenDelimiter + "websphere.cell" + endTokenDelimiter + "/Nodes/" + startTokenDelimiter + "websphere.node" + endTokenDelimiter + "/Servers/" + startTokenDelimiter + "websphere.server" + endTokenDelimiter + "/")
      it.path = it.path.replace("/" + serverName, "/" + startTokenDelimiter + "websphere.cell" + endTokenDelimiter + "/Nodes/" + startTokenDelimiter + "websphere.node" + endTokenDelimiter + "/Servers/" + startTokenDelimiter + "websphere.server" + endTokenDelimiter)
      found = true
    }
  }

  if (found) {
    println "Fixing paths since discovery was run on a Server only..."
    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')
}
