/*
 * Licensed Materials - Property of IBM Corp.
 * IBM UrbanCode Build
 * IBM UrbanCode Deploy
 * IBM UrbanCode Release
 * IBM AnthillPro
 * (c) Copyright IBM Corporation 2016. All Rights Reserved.
 *
 * U.S. Government Users Restricted Rights - Use, duplication or disclosure restricted by
 * GSA ADP Schedule Contract with IBM Corp.
 */

package com.urbancode.air.plugin.iisconfiguration.helper
import org.codehaus.jettison.json.JSONObject
import org.codehaus.jettison.json.JSONArray
import com.urbancode.air.plugin.iisconfiguration.helper.ResourceHelper
import com.urbancode.air.plugin.iisconfiguration.Globals

class TopologyDiscoveryHelper {
    def resourceHelper

    public TopologyDiscoveryHelper(ResourceHelper resourceHelper){
        this.resourceHelper = resourceHelper
    }

    //convert json to a properties map
    private jsonToPropMap (JSONObject jsonObj){
        def result = [:]
        jsonObj.keys().each{
            result[it] = jsonObj.get(it)
        }

        return result
    }

    //convert properties map into json
    private propMapToJson (propMap){
        JSONObject resourceRoleProps = new JSONObject()
        return propMap.each { key, value -> resourceRoleProps.put(key, value) }
    }

    //merge first jsonarray contents into the second one
    public mergeJSONArrays (JSONArray jsonArrayInput, JSONArray result){
        for (int i = 0; i < jsonArrayInput.length(); i++) {
            result.put(jsonArrayInput.get(i))
        }
        return result
    }

    //bundle iis objects into a JSONArray
    public extractToJSONArray (iisObjectMap, thisResourcePath, thisParentPath, roleName, description){
        JSONArray result = new JSONArray()

        iisObjectMap.each{ name,  propMap ->
            def skipIteration = false
            JSONObject resourceRoleProps = propMapToJson(propMap)

            JSONObject newResourceJson = new JSONObject()
            newResourceJson.put("name", name)
            newResourceJson.put("description", description)
            newResourceJson.put("parent", thisParentPath)
            newResourceJson.put("role", roleName)
            newResourceJson.put("path", "${thisParentPath}/${name}")
            newResourceJson.put("roleProperties", resourceRoleProps)

            //if this resource is an Application,  we need to place it underneath
            //the associated Site on the resource tree
            if (roleName == Globals.APP_ROLE_NAME){
                //alter path to be underneath the associated Site
                newResourceJson.put("path", "${thisResourcePath}/Sites/${name}")

                //name should be the last part of the application path which is the
                //name for all applications
                name = (name =~ /.*\/(.*)/)[0][1]
                if (name.trim().isEmpty() || name == null) {
                    //skip due to name conflicts between sites and applications
                    skipIteration = true
                }
                else {
                    //parent should be the path before the trailing name '(.*)/<name>'
                    thisParentPath = (newResourceJson.get("path") =~ /(.*)\//)[0][1]
                    newResourceJson.put("parent", thisParentPath)
                    newResourceJson.put("name", name)
                }
            }

            if (!skipIteration) {
                result.put(newResourceJson)
            }
        }

        return result
    }

    //add iis objects to the resource tree
    public addResourcesToResourceTree (JSONArray resources){
        for (def i = 0; i < resources.length(); i++){
            //load props from json and create a base resource
            def resourceJsonObject = resources.get(i)
            def resourceName = resourceJsonObject.get("name").trim()
            def resourcePath = resourceJsonObject.get("path")
            def resourceParent = resourceJsonObject.get("parent")
            def resourceDescription = resourceJsonObject.get("description")
            def resourceRole = resourceJsonObject.get("role")

            //check to make sure the path to this resource has already been created
            if (resourceHelper.getResource(resourceParent) == null){
                createParentPaths(resourceParent)
            }

            def resource = resourceHelper.getOrCreateSubResource(resourcePath, resourceParent, resourceName, resourceDescription)

            //now add role properties to the resource we just created
            def resourceRoleProps = jsonToPropMap(resourceJsonObject.get("roleProperties"))
            resourceHelper.addRoleToResource(resource, resourceRole, resourceRoleProps)
        }
    }

    //use 2 stacks to create children that are missing from a given path.
    //currentPathToCheckStack - keeps track of each child in the original path in the original order of the path.
    //      since we preserve the order of the path, we pop to move further toward the root of the path.
    //parentNamesToCreateStack - keeps track of each child name that we pop from currentPathToCheckStack in reverse order.
    //      when we pop from this stack we are moving from the root toward the bottom in the path, creating children
    //      as we move down the stack.
    // example:
    //      resourceParent = "/this/is/a/path/to"
    //      currentPathToCheckStack = [this, is, a, path, to]
    //      parentNamesToCreateStack = []
    //      *now we need to check the child "to" so we input the path to it by joining currentPathToCheckStack.*
    //      resourceHelper.getResource("/this/is/a/path/to")
    //      *this returns null, we need to add "to" to the parentNamesToCreateStack now so we can preserve the reverse order of the path.*
    //      currentPathToCheckStack = [this, is, a, path]
    //      parentNamesToCreateStack = [to]
    //      *now repeat until you find a child's path that is not null, then you pop through parentNamesToCreateStack to move down,
    //          creating each child in the original order of the path we started with.*
    private createParentPaths (String resourceParent){
        def currentPathToCheckStack = resourceParent.tokenize("/") as Stack
        def parentNamesToCreateStack = [currentPathToCheckStack.pop()] as Stack

        //keep working up path until you reach a non null
        while (resourceHelper.getResource("/${currentPathToCheckStack.join("/")}") == null){
            parentNamesToCreateStack.push(currentPathToCheckStack.pop())
        }

        //now create the resources back down the tree
        while (!parentNamesToCreateStack.isEmpty()){
            def childName = parentNamesToCreateStack.pop()
            def parentPath = "/${currentPathToCheckStack.join("/")}"
            def fullPath = "${parentPath}/${childName}"
            resourceHelper.getOrCreateSubResource(fullPath, parentPath, childName, "")

            currentPathToCheckStack.push(childName)
        }
    }
}