import codecs;
import os;
import sys;
from java.util import Properties;
from java.io import File, FileInputStream;
from org.codehaus.jettison.json import JSONObject, JSONArray, JSONException;

class PluginInputHelper:

  def __init__(self, inputPropsLocation):
    self.inputPropsFile = File(inputPropsLocation);
    self.parsedResourceConfiguration = 0;
    self.inputProperties = Properties();
    inStream = FileInputStream(self.inputPropsFile);
    self.inputProperties.load(inStream);
    inStream.close();
    self.fullResourceConfiguration = None;
    
  def getLogDirectory(self):
    return self.inputPropsFile.getParentFile();

  def getFullResourceConfiguration(self, forceRootPath = ""):
    if self.parsedResourceConfiguration == 0:
      self._parseResourceConfiguration(forceRootPath);
    return self.fullResourceConfiguration;

  def getInputProperties(self):
    return self.inputProperties;

  def _parseResourceConfiguration(self, forceRootPath = ""):
    self.parsedResourceConfiguration = 1;
    if self.fullResourceConfiguration is None:
      resourceJSONObject = None;
      configFile = self.inputProperties.getProperty("configurationFile");
      if configFile is not None and len(configFile) > 0:
        if os.path.isfile(configFile):
          resourceJSONObject = self._parseConfigurationFile(configFile, forceRootPath);
      if resourceJSONObject is None:
        fullResConf = self.inputProperties.getProperty("fullResourceConfiguration");
        if fullResConf is not None and len(fullResConf) > 0:
          resourceJSONObject = JSONObject(fullResConf);
      self.fullResourceConfiguration = resourceJSONObject;
    else:
      pass;

  def _parseConfigurationFile(self, configFile, forceRootPath = ""):
    print "Processing configuration file %s" % configFile;
    resourceJSONObject = None;
    inputFile = codecs.open(configFile, "r", "utf-8");

    inputData = inputFile.readlines();

    # Our provided components create a new json file (ibm-ucd-resources.json) as part of the apply process.
    # This file contains the merged contents of a component version's artifacts
    # If there are no version artifacts, the new json file has a value of ['[\n', '\n', ']']
    # Let's throw an exception with a clear message in this case
    if str(inputData) == "['[\\n', '\\n', ']']":
      raise ValueError("No configuration data was found. Verify your component version artifacts are present.")
    inputData = ''.join(inputData);
    try:
      resourceJSONObject = JSONObject(inputData);
    except JSONException, je:
      resourceJSONArray = JSONArray(inputData);
      resourceJSONObject = self._convertResourceJSONArrayToJSONObject(resourceJSONArray, forceRootPath);
    return resourceJSONObject;

  def _convertResourceJSONArrayToJSONObject(self, resourceJSONArray, forceRootPath = ""):
    objMap = self._getBaseMapFromJSONArray(resourceJSONArray);
    return self._convertObjectDictToJSONObject(objMap, forceRootPath);

  def _getBaseMapFromJSONArray(self, resourceJSONArray):
    objMap = {};
    for i in range(resourceJSONArray.length()):
      myObj = resourceJSONArray.getJSONObject(i);
      thispath = myObj.getString('path');
      objMap[thispath] = myObj;
      children = myObj.optJSONArray("children");
      if children is None:
          myObj.put("children", JSONArray());
    return objMap;

  def _convertObjectDictToJSONObject(self, objMap, forceRootPath = ""):
    root = None;
    objMapKeys = objMap.keys();

    # this method works by grabbing an object from the JSON, getting it's path,
    # then looping through all objects and comparing paths to find the root parent object.
    # in the case where we have multiple objects that may be a root parent object,
    # we may want to force which root parent we want to find.
    # for example, for dynamic clusters, the config data contains two root objects with these paths:
    #   /cellName/Dynamic Clusters/clusterName
    #   /cellName/ServerClusters/clusterName
    # if we wanted to ensure we find the dynamic clusters in this case,
    # we'd set forceRootPath to "/Dynamic Clusters/"

    # if forceRootPath is set, ensure we start looping with an object of that path
    if forceRootPath != "":
      for path in objMapKeys:
        if path.find(forceRootPath) != -1:
          root = objMap[path]
          rootPath = root.getString("path");
          break;

    for path in objMapKeys:
      curObj = objMap[path];

      if root is None:
        root = curObj;
      else:
        rootPath = root.getString("path");
        if rootPath.startswith(path + "/"):
          root = curObj;

      ind = path.rfind("/");
      if ind != -1:
        parentPath = path[0:ind];
        if parentPath in objMapKeys:
          parent = objMap[parentPath];
          children = parent.optJSONArray("children");
          if children is None:
            children = JSONArray();
            parent.put("children", children);
          children.put(curObj);

    self._normalizePaths(root, objMap);
    return root;

  def _normalizePaths(self, root, objMap):
    rootPath = root.getString("path");
    stripPath = rootPath[0:rootPath.rfind("/")];
    if len(stripPath) > 0:
      self._stripPaths(stripPath, objMap);

  def _stripPaths(self, stripPath, objMap):
    for x in objMap.values():
      curPath = x.getString("path");
      if curPath.startswith(stripPath):
        newPath = curPath[len(stripPath):];
        x.put("path", newPath);
