import java.io.File;

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

import com.urbancode.air.AirPluginTool;


/*
  For a snippet to be applied individually (for example, adding a single JDBC Provider to WAS),
  three things are needed from the configuration data:

  1. The resource itself and all of its children.

  2. The entry that represents the type of resource the snippet is. For example, if our resource
     is a JDBC Provider, our snippet must contain this entry (note the path):

     {
         "name": "JDBC Providers",
          "path": "/CloudBurstCell_1/Nodes/CloudBurstNode_3_1/Servers/Member2/JDBC Providers",
          "teamMappings": [],
          "description": "Discovered JDBC Providers",
          "inheritTeam": "true"
     }

   3. The parent resource of the snippet, with the parent resource being either the Cell, Node, Server,
      or Cluster the snippet resource is under. For example, if our snippet represents a JDBC Provider
      at the Server scope, our snippet should have an entry similar to:

      {
          "name": "Member2",
          "path": "/CloudBurstCell_1/Nodes/CloudBurstNode_3_1/Servers/Member2",
          "teamMappings": [],
          "inheritTeam": "true",
          "description": "Discovered WebSphereServer",
          "roleName": "WebSphereServer",
          "roleProperties": {
              "websphere.server.adjustport": "true",
              "websphere.server.servertype": "APPLICATION_SERVER",
              "websphere.server.developmentmode": "false",
              "websphere.server.provisioncomponents": "false",
              "websphere.server.parallelstartenabled": "true",
              "websphere.server.clustername": "someCluster",
              "websphere.server": "Member2"
          }
      }
*/

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

def configFile = props['configFile'].trim();
def outputFile = props['outputFile'].trim();
def resourceName = props['resourceName'].trim();
def cellName = props['cellName'];
def nodeName = props['nodeName'];
def serverName = props['serverName'];
def clusterName = props['clusterName'];

file =  new File(configFile)
String fileContents = file.getText('UTF-8')
def list = new JsonSlurper().parseText(fileContents)

def newArrayObj = []
def pathToExtract = ""
def scope = ""
def resourceCounter = 0
def pathList = "\n"
def isCellScope = false
def isNodeScope = false


// check to see if the scope is Cell
if (cellName != ""){
  if (nodeName != "" || serverName != "" || clusterName !=""){
    throw new RuntimeException("Error: A cell name of " + cellName + " was specified, indicating the resource to find is at the Cell scope. " +
      "However, a Node, Server, or Cluster was also specified. If the resource to find is at the Cell scope, the Node, Server, and Cluster " +
      "fields should be blank. The Scope: Node Name field was set to " + nodeName + ". The Scope: Server Name field " +
      "was set to " + serverName + ". The Scope: Cluster Name field was set to " + clusterName + ".")
  } else {
    isCellScope = true
    scope = "Cell"
  }
}

if (!isCellScope) {
  // check to see if the scope is Node (or Server, which needs a Node specified)
  if (nodeName != ""){
    // Scope: Cluster Name field needs to be blank (so does the Cell field, but we already checked that)
    if (clusterName !=""){
      throw new RuntimeException("Error: A node name of " + nodeName + " was specified, indicating the resource to find is at the Node or Server scope. " +
        "However, a Cluster was also specified. If the resource to find is at the Node scope, the Cell and Cluster " +
        "fields should be blank. The Scope: Cluster Name field was set to " + clusterName + ".")
    } else {
      scope = "/Nodes/" + nodeName.trim() + "/"
      // if there is no server name, the scope is Node (if there was a server name, scope would be Server)
      if (serverName == ""){
        isNodeScope = true
      }
    }
  }
  // check to see if the scope is Server
  if (serverName != ""){
    // Node name needs to be set
    if (nodeName == ""){
      throw new RuntimeException("Error: A server name was specified but a node name was not specified. If a server name was specifed, the desired server's node name needs to be specified.")
    }
    // Scope: Cluster Name field needs to be blank (so does the Cell field, but we already checked that)
    if (clusterName !=""){
      throw new RuntimeException("Error: A server name of " + serverName + " was specified, indicating the resource to find is at the Server scope. " +
        "However, a Cluster was also specified. If the resource to find is at the Server scope, the Cell and Cluster " +
        "fields should be blank. The Scope: Cluster Name field was set to " + clusterName + ".")
    } else {
      scope = scope + "Servers/" + serverName.trim() + "/"
    }
  }
  // check to see if the scope is Cluster
  if (clusterName != ""){
    scope = "/ServerClusters/" + clusterName.trim() + "/"
  }
}

// print the scope info
if (isCellScope){
  println "Resource to find is at the Cell scope"
}
else if (scope != ""){
  println "Scope to look under is " + scope
} else {
  println "No scope specified. Searching entire config file for a resource named " + resourceName
}

println "Searching for a resource named " + resourceName + " in file " + configFile + "..."

// find the resource we are looking for and get its path
list.each {
  if (it.name == resourceName) {
    // if there is a scope specified, make sure the path is part of that scope
    if (scope != ""){
      // if it's the Cell scope, make sure the path does not contain Nodes or ServerClusters
      if (isCellScope){
        if (!it.path.contains("/Nodes/") && !it.path.contains("/ServerClusters/")) {
          println "Found a resource named " + resourceName + " at the Cell scope. The path is " + it.path
          pathToExtract = it.path
          pathList = pathList + it.path + "\n"
          resourceCounter++
        }
      } else if (isNodeScope){
        // if it's the Node scope, make sure the path does not contain Servers
        if (it.path.contains(scope) && !it.path.contains("/Servers/")) {
          println "Found a resource named " + resourceName + " at the Node scope. The path is " + it.path
          pathToExtract = it.path
          pathList = pathList + it.path + "\n"
          resourceCounter++
      }
      } else {
        // a scope was specified, and it is either Server or Cluster scope
        if (it.path.contains(scope)){
          println "Found a resource named " + resourceName + " at scope " + scope + ". The path is " + it.path
          pathToExtract = it.path
          pathList = pathList + it.path + "\n"
          resourceCounter++
        }
      }
    } else {
      // no scope specified, so search through everything
      println "Found a resource named " + resourceName + " on path " + it.path
      pathToExtract = it.path
      pathList = pathList + it.path + "\n"
      resourceCounter++
    }
  }
}

// if we found more than one resource, something went wrong
if (resourceCounter > 1) {
  throw new RuntimeException("Found more than one resource named " + resourceName + ". Perhaps a scope should be " +
  "specified? Ensure your resource has a unique name. Paths of the found reources are " + pathList)
}

// get all resources under and including the path
if (pathToExtract == ""){
  throw new RuntimeException("Error: Could not find a resource named " + resourceName + " in file " + configFile)
}
else {
  list.each {
    if (it.path == pathToExtract || it.path.indexOf(pathToExtract + "/", 0) == 0) {
      newArrayObj.push(it);
    }
  }
}

// determine and get the path of the resource that represents the snippet's resource type
// For example, if pathToExtract is: /MattExemplarCell01/Nodes/MattExemplarNode1/JDBC Providers/myJDBC,
// we want to get:                   /MattExemplarCell01/Nodes/MattExemplarNode1/JDBC Providers
lastSlash = pathToExtract.lastIndexOf("/")
parentPath = pathToExtract.substring(0, lastSlash)

list.find {
  if (it.path == parentPath) {
    println "Found the snippet type resource at path " + parentPath
    newArrayObj.push(it);
    return true
  }
  else {
    return false
  }
}


// determine and get the parent resource (Cell, Node, Server, Cluster)
// can't rely on a scope being set because a scope is optional
parentScope = "Cell"
parentResourcePath = ""
if (pathToExtract.contains("/Servers/")){
  parentScope = "/Servers/"
} else if (pathToExtract.contains("/Nodes/")){
  parentScope = "/Nodes/"
} else if (pathToExtract.contains("/ServerClusters/")){
  parentScope = "/ServerClusters/"
}

if (parentScope != "Cell"){
  // if pathToExtract is: /MattExemplarCell01/Nodes/MattExemplarNode1/JDBC Providers/myJDBC,
  // we want to get:      /MattExemplarCell01/Nodes/MattExemplarNode1
  parentScopeIndex = pathToExtract.indexOf(parentScope)
  parentScopeLength = parentScope.length()
  nextSlash = pathToExtract.indexOf("/", parentScopeIndex + parentScopeLength)
  parentResourcePath = pathToExtract.substring(0, nextSlash)
} else {
  // for Cell, get the first piece of of the path
  nextSlash = pathToExtract.indexOf("/", 1)
  parentResourcePath = pathToExtract.substring(0, nextSlash)
}

// get the parent resource
if (parentResourcePath == ""){
  throw new RuntimeException("Error: Could not find a parent resource of path " + parentResourcePath)
}
else {
  list.find {
    if (it.path == parentResourcePath) {
      println "Found the parent resource at path " + parentResourcePath
      newArrayObj.push(it);
      return true
    }
    else {
      return false
    }
  }
}

// create output file dir if needed
outputFile = new File(outputFile)
outputFileDir = new File(outputFile.getParent())
if (!outputFileDir.exists()) {
  outputFileDir.mkdirs()
}
// delete the output file if it already exists
if (outputFile.exists()) {
  outputFile.delete()
}

// write the results to the outputFile
println "Creating new configuration file: " + outputFile
contents = new JsonBuilder(newArrayObj).toPrettyString()
// remove brackets, if needed
firstChar = contents.charAt(0)
if (firstChar == "[") {
  startIndex = 1
  endIndex = contents.length()-1
  contents = contents.substring(startIndex, endIndex)
}
new File(outputFile.getParent()+File.separator+outputFile.getName()).write(contents, 'UTF-8')
