import java.io.File;
import java.util.List;
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 workingDirString = props['outputDir'].trim();
def userDefinedTokenization = props['userDefinedTokenization'].trim();
def discoveredOnlyCellConfig = props['discoveredOnlyCellConfig'].trim();
def nodeName = props['nodeName'].trim();
def hostName = props['nodeHostName'].trim();
def startTokenDelimiter = props['startTokenDelimiter'].trim();
def endTokenDelimiter = props['endTokenDelimiter'].trim();

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

// the extracted directory
extractedDirString = workingDirString + "extracted" + File.separator

// the templates directory
templateDirString = workingDirString + "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)
String cellName = getRolePropertyValueFromFile(cellJson, "websphere.cell")

// copy the cell directory
oldCellDirString = extractedDirString + "Cell"
newCellDirString = templateDirString + "Cell"
newCellDir = new File(newCellDirString)
if (newCellDir.exists()) {
  newCellDir.deleteDir()
}
newCellDir.mkdirs()
new AntBuilder().copy( todir: newCellDirString ) {
  fileset( dir: oldCellDirString )
}

if (discoveredOnlyCellConfig == "false") {
  // handle deployment manager, if there is one
  dmgrJsonString = templateDirString + "Cell" + File.separator +"dmgr" + File.separator + "dmgr.json"
  dmgrJson = new File(dmgrJsonString)
  if (dmgrJson.exists()) {
    println "Tokenizing values for dmgr.json..."
    // get the dmgr node name
    String dmgrNodeName = getPropertyValueByRoleName(dmgrJson, "WebSphereServer", "path")
    startOfPath = "/" + cellName + "/Nodes/"
    startIndex = startOfPath.size()
    serversIndex = dmgrNodeName.lastIndexOf("/Servers/")
    dmgrNodeName = dmgrNodeName.substring(startIndex, serversIndex)
    println "dmgrNodeName to tokenize: " + dmgrNodeName

    // get the dmgr host name
    nodeJson = new File(extractedDirString + "Node-" + dmgrNodeName + File.separator + "node" + File.separator + "Node.json")
    String dmgrHostName = getRolePropertyValueFromFile(nodeJson, "websphere.node.hostname")
    println "dmgrHostName to tokenize: " + dmgrHostName

    // tokenize values
    String dmgrFileContents = dmgrJson.getText('UTF-8')
    dmgrFileContents = dmgrFileContents.replace("/Nodes/" + dmgrNodeName + "/Servers/", "/Nodes/" + startTokenDelimiter + "websphere.node" + endTokenDelimiter + "/Servers/")
    dmgrFileContents = dmgrFileContents.replace("(node):" + dmgrNodeName, "(node):" + startTokenDelimiter + "websphere.node" + endTokenDelimiter)
    dmgrFileContents = dmgrFileContents.replace("/" + cellName + "/", "/" + startTokenDelimiter + "websphere.cell" + endTokenDelimiter + "/")
    dmgrFileContents = dmgrFileContents.replace("(cell):" + cellName, "(cell):" + startTokenDelimiter + "websphere.cell" + endTokenDelimiter)
    dmgrJson.delete()
    new File(dmgrJson.getParent()+File.separator+dmgrJson.getName()).write(dmgrFileContents, 'UTF-8')
    dmgrJson = new File(dmgrJsonString)
    tokenizeRolePropertyValues(dmgrJson, dmgrHostName, startTokenDelimiter + "websphere.node.hostname" + endTokenDelimiter)

    // just to be safe, tokenize anything we may have missed
    dmgrJson = new File(dmgrJsonString)
    dmgrFileContents = dmgrJson.getText('UTF-8')
    dmgrFileContents = dmgrFileContents.replace(dmgrNodeName, startTokenDelimiter + "websphere.node" + endTokenDelimiter)
    dmgrFileContents = dmgrFileContents.replace(cellName, startTokenDelimiter + "websphere.cell" + endTokenDelimiter)
    dmgrJson.delete()
    new File(dmgrJson.getParent()+File.separator+dmgrJson.getName()).write(dmgrFileContents, 'UTF-8')

    // get dmgr name
    dmgrJson = new File(dmgrJsonString)
    String dmgrName = getRolePropertyValueFromFile(dmgrJson, "websphere.server")

    // add parent property to dmgr.json
    println "Adding parent property to dmgr.json..."
    dmgrJson = new File(dmgrJsonString)
    addPropertyToResource(dmgrJson, "/" + startTokenDelimiter + "websphere.cell" + endTokenDelimiter + "/Nodes/" + startTokenDelimiter + "websphere.node" + endTokenDelimiter + "/Servers/" + dmgrName, "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
    dmgrJson = new File(dmgrJsonString)
    doUserDefinedTokenization(dmgrJson, userDefinedTokenization)

    // remove brackets
    dmgrJson = new File(dmgrJsonString)
    removeBracketsFromJson(dmgrJson)

    // fix double quotes
    dmgrJson = new File(dmgrJsonString)
    fixDoubleQuotes(dmgrJson)

    // set the nodeName and hostName for use later
    nodeName = dmgrNodeName
    hostName = dmgrHostName
  } else {
    // no dmgr.json file exists, so delete the dmgr directory
    dmgrDirString = newCellDirString + File.separator + "dmgr"
    dmgrDir = new File(dmgrDirString)
    dmgrDir.deleteDir()

    // if there is no dmgr, this must be WAS Base.
    // if this is WAS Base, there must be only one application server.
    // that application server is on a node.
    // we need to get that node's name and host name (see WI 146549).
    println "No deployment manager detected, so this must be WAS Base..."
    println "Finding node the application server belongs to..."
    isNodeWeWant = false
    // get all directories and find ones that start with Node-
    extractedDir = new File(extractedDirString)
    dirs = getAllFoldersInDir(extractedDir)
    dirs.each {
      if (it.getName().startsWith("Node-") && isNodeWeWant == false){
        nodeFolder = new File(it.getParent()+File.separator+it.getName())
        // get all files for this node and find Server.json
        nodeFiles = getAllFilesInDir(nodeFolder)
        nodeFiles.each {
          if (it.getName() == "Server.json"){
            serverDirString = it.getParent()+File.separator+it.getName()
            serverJson = new File(serverDirString)
            String serverType = getRolePropertyValueFromFile(serverJson, "websphere.server.servertype")
            // see if this server is the application server. If it is, we are on the node we want
            if (serverType == "APPLICATION_SERVER"){
              println "Found the node the application server belongs to..."
              isNodeWeWant = true
            }
          }
        }
        if (isNodeWeWant == true){
          // get this node's Node.json file
          nodeDirString = it.getParent()+File.separator+it.getName()+ File.separator + "node" + File.separator + "Node.json"
          nodeJson = new File(nodeDirString)
          // get the node name
          nodeName = getRolePropertyValueFromFile(nodeJson, "websphere.node")
          println "The application server's node name is " + nodeName
          // get the node's host name
          hostName = getRolePropertyValueFromFile(nodeJson, "websphere.node.hostname")
        }
      }
    }
  }
}

// tokenize Cell.json
println "Tokenizing values for Cell.json..."
cellJsonString = newCellDirString + File.separator + "cell" + File.separator + "Cell.json"

cellJson = new File(cellJsonString)
String cellFileContents = cellJson.getText('UTF-8')
cellFileContents = cellFileContents.replace("/" + cellName + "/", "/" + startTokenDelimiter + "websphere.cell" + endTokenDelimiter + "/")
cellFileContents = cellFileContents.replace("(cell):" + cellName, "(cell):" + startTokenDelimiter + "websphere.cell" + endTokenDelimiter)
cellJson.delete()
new File(cellJson.getParent()+File.separator+cellJson.getName()).write(cellFileContents, 'UTF-8')

// substitute any references to nodename and hostname
// find which value is longer and replace that value first
// (to handle cases where hostname is a subset of node name and vice versa)
cellJson = new File(cellJsonString)
nodeNameLength = nodeName.length()
hostNameLength = hostName.length()

if (nodeNameLength >= hostNameLength) {
  if (hostName?.trim()) {
    tokenizeRolePropertyValues(cellJson, nodeName, startTokenDelimiter + "websphere.node" + endTokenDelimiter)
  } else {
    println "No node name specified. Not templatizing a node name."
  }
  if (nodeName?.trim()) {
    tokenizeRolePropertyValues(cellJson, hostName, startTokenDelimiter + "websphere.node.hostname" + endTokenDelimiter)
  } else {
    println "No node host name specified. Not templatizing a node host name."
  }
} else {
    tokenizeRolePropertyValues(cellJson, hostName, startTokenDelimiter + "websphere.node.hostname" + endTokenDelimiter)
  if (nodeName?.trim()) {
    tokenizeRolePropertyValues(cellJson, nodeName, startTokenDelimiter + "websphere.node" + endTokenDelimiter)
  } else {
    println "No node name specified. Not templatizing a node name."
  }
}

// just to be safe, tokenize anything we may have missed
cellJson = new File(cellJsonString)
cellFileContents = cellJson.getText('UTF-8')
cellFileContents = cellFileContents.replace(cellName, startTokenDelimiter + "websphere.cell" + endTokenDelimiter)
cellJson.delete()
new File(cellJson.getParent()+File.separator+cellJson.getName()).write(cellFileContents, 'UTF-8')

// virtual host "default_host" has property websphere.virtualhost.aliases which
//  usually contains hardcoded hostnames. so, we will tokenize that value.
println "If applicable, tokenizing virtual host aliases for virtual host named 'default_host'..."
cellJson = new File(cellJsonString)
replaceRolePropertyValueFromFile(cellJson, "websphere.virtualhost.name", "default_host", "websphere.virtualhost.aliases", startTokenDelimiter + "DEFAULTHOSTVIRTUALHOSTALIASES" + endTokenDelimiter)

// tokenize user defined values
cellJson = new File(cellJsonString)
doUserDefinedTokenization(cellJson, userDefinedTokenization)

// remove brackets
cellJson = new File(cellJsonString)
removeBracketsFromJson(cellJson)

// fix double quotes
cellJson = new File(cellJsonString)
fixDoubleQuotes(cellJson)



// method to get a property value from an object based on object's role name
String getPropertyValueByRoleName(File file, String roleName, String property) {
  String fileContents = file.getText('UTF-8')
  def list = new JsonSlurper().parseText(fileContents)
  value = ""
  list.each {
    if (it.roleName == roleName){
      value = it.path
    }
  }
  return value
}

// 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 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 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 get all files in a directory
List getAllFilesInDir(File dir) {
  def list = []
  dir.eachFileRecurse (FileType.FILES) { file ->
    list << file
  }
  return list
}


// method to get all folders in a directory
List getAllFoldersInDir(File parentDir) {
  def list = []
  parentDir.eachDir() { dir ->
    list << dir
  }
  return list
}

// method to replace a role property value
//   rolePropertyNameKey = a role property to be used as an identifier so we know we have the correct entry (example: "websphere.virtualhost.name")
//   name = value of the rolePropertyNameKey we are looking for (example: "default_host")
//   rolePropertyKeyToUpdate = the role property to be updated
//   newValue = updated value of rolePropertyKeyToUpdate
// In other words: if JSON entry has role property named rolePropertyNameKey, and rolePropertyNameKey = name, and if JSON entry
//  also has a role property named rolePropertyKeyToUpdate, then update rolePropertyKeyToUpdate to newValue
String replaceRolePropertyValueFromFile(File file, String rolePropertyNameKey, String name, String rolePropertyKeyToUpdate, String newValue) {
  String fileContents = file.getText('UTF-8')
  def list = new JsonSlurper().parseText(fileContents)
  list.each {
    if(it.keySet().contains( 'roleProperties' )) {
	  if (it.roleProperties.keySet().contains (rolePropertyNameKey)) {
		if (it.roleProperties[rolePropertyNameKey] == name) {
		  if (it.roleProperties.keySet().contains (rolePropertyKeyToUpdate))
		    it.roleProperties[rolePropertyKeyToUpdate] = newValue
		}
      }
    }
  }
  contents = new JsonBuilder(list).toPrettyString()
  file.delete()
  new File(file.getParent()+File.separator+file.getName()).write(contents, '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')
}
