/**
   * Creates and returns a JSON Node for a JPA Node Entity Used for GET Requests for NodeResource
   *
   * @param nodeEntity
   * @return
   */
  public Node getNodeJSONFromNodeEntity(org.nikhil.webapp.nodeadmin.entity.Node nodeEntity) {
    Node nodeJson = new Node();
    nodeJson.setName(nodeEntity.getName());
    nodeJson.setDescription(nodeEntity.getDescription());
    nodeJson.setIpAddress(nodeEntity.getIpAddress());
    nodeJson.setMaster(nodeEntity.isMaster());
    nodeJson.setUserName(nodeEntity.getUserName());
    nodeJson.setPassword(nodeEntity.getPassword());
    nodeJson.setShutdownTimer(nodeEntity.getShutdownTimer());
    nodeJson.setRestartTimer(nodeEntity.getRestartTimer());
    nodeJson.setStatus(nodeEntity.getStatus());

    for (NodeAndService nodeAndService : nodeEntity.getNodeServices()) {
      Service service = new Service();
      org.nikhil.webapp.nodeadmin.entity.Service serviceToCopyFrom =
          nodeAndService.getCompositePK().getService();
      service.setName(serviceToCopyFrom.getName());
      service.setDescription(serviceToCopyFrom.getDescription());
      service.setRunAlwaysInBackground(serviceToCopyFrom.isRunAlwaysInBackground());
      service.setStartCommand(serviceToCopyFrom.getStartCommand());
      service.setStopCommand(serviceToCopyFrom.getStopCommand());
      service.setStatusWildCard(serviceToCopyFrom.getStatusWildCard());

      // composite info flattened
      service.setAdditionalRunParamsStart(nodeAndService.getAdditionalRunParamsStart());
      service.setAdditionalRunParamsStop(nodeAndService.getAdditionalRunParamsStop());
      service.setStatusWildCardOverride(nodeAndService.getStatusWildCardOverride());
      service.setStatusCommandOverride(nodeAndService.getStatusCommandOverride());
      service.setRundir(nodeAndService.getRundir());

      // TODOne put this one on the composite/join table
      service.setRunning(nodeAndService.isRunning());

      nodeJson.getServices().add(service);
    }

    for (NodeAndCommand nodeAndCommand : nodeEntity.getNodeCommands()) {
      Command command = new Command();
      org.nikhil.webapp.nodeadmin.entity.Command commandToCopyFrom =
          nodeAndCommand.getCompositePK().getCommand();
      command.setName(commandToCopyFrom.getName());
      command.setDescription(commandToCopyFrom.getDescription());
      command.setCommand(commandToCopyFrom.getCommand());

      // composite info flattened
      command.setAdditionalRunParams(nodeAndCommand.getAdditionalRunParams());
      command.setAlwaysRunInBackground(nodeAndCommand.isAlwaysRunInBackground());
      command.setRundir(nodeAndCommand.getRundir());

      nodeJson.getCommands().add(command);
    }

    return nodeJson;
  }
  /**
   * Creates or fetches a JPA Node Entity for JSON Node Used for PUT and POST Requests for
   * NodeResource The services and commands are updated from that of the JSON Node
   *
   * @param nodeEntity
   * @return
   */
  public org.nikhil.webapp.nodeadmin.entity.Node getJPANodeFromJSONNode(Node jsonNode) {

    String nodeName = jsonNode.getName();
    org.nikhil.webapp.nodeadmin.entity.Node jpaNode = nodeOperation.getNode(nodeName);
    boolean addingANode = false;
    boolean prevServicesPresent = false;
    boolean prevCmmandsPresent = false;

    if (jpaNode == null) {
      // Create Mode
      jpaNode = new org.nikhil.webapp.nodeadmin.entity.Node();
      jpaNode.setName(nodeName);
      addingANode = true;
    }
    // create / update Mode
    jpaNode.setDescription(jsonNode.getDescription());
    jpaNode.setIpAddress(jsonNode.getIpAddress());
    jpaNode.setMaster(jsonNode.isMaster());
    jpaNode.setUserName(jsonNode.getUserName());
    jpaNode.setPassword(jsonNode.getPassword());
    jpaNode.setShutdownTimer(jsonNode.getShutdownTimer());
    jpaNode.setRestartTimer(jsonNode.getRestartTimer());
    jpaNode.setStatus(jsonNode.getStatus());

    /*		Add or Update the existing NodeService relationships
    	optimizing single fetch for node services
    */
    List<NodeAndService> nodeAssiginedServices = jpaNode.getNodeServices();

    if (jsonNode.getServices().size() > 0) {
      prevServicesPresent = true;
      for (Service jsonService : jsonNode.getServices()) {
        org.nikhil.webapp.nodeadmin.entity.Service jpaService = null;
        org.nikhil.webapp.nodeadmin.entity.NodeAndService jpaNodeAndService = null;
        boolean serviceFoundAlreadyLinked = false;

        if (nodeAssiginedServices.size() > 0) {
          for (org.nikhil.webapp.nodeadmin.entity.NodeAndService fetchedNodeAndService :
              nodeAssiginedServices) {
            if (fetchedNodeAndService
                .getCompositePK()
                .getService()
                .getName()
                .equals(jsonService.getName())) {
              jpaNodeAndService = fetchedNodeAndService;
              serviceFoundAlreadyLinked = true;
              break;
            }
          }
        }
        // Add service relationship for current json service
        if (!serviceFoundAlreadyLinked || nodeAssiginedServices.size() == 0) {
          jpaService = serviceOperation.getService(jsonService.getName());
          jpaNodeAndService = new NodeAndService();
          jpaNodeAndService.setCompositePK(new NodeAndServiceId(jpaNode, jpaService));
          // adding relationship
          jpaNode.getNodeServices().add(jpaNodeAndService);
        }

        // so here we have jpanodeandservice identified either new and added or retrieved from node
        // itself - update it now
        jpaNodeAndService.setAdditionalRunParamsStart(jsonService.getAdditionalRunParamsStart());
        jpaNodeAndService.setAdditionalRunParamsStop(jsonService.getAdditionalRunParamsStop());
        jpaNodeAndService.setStatusWildCardOverride(jsonService.getStatusWildCardOverride());
        jpaNodeAndService.setStatusCommandOverride(jsonService.getStatusCommandOverride());
        jpaNodeAndService.setRundir(jsonService.getRundir());
        jpaNodeAndService.setRunning(jsonService.isRunning());
      }

      /* delete the relationships that are not part of new assignment
       * if adding a node all service relationships are new so no need to delete anything from jpaNode
       */
      if (prevServicesPresent && !addingANode) {
        int index = 0;
        ArrayList<Integer> deleteIndices = new ArrayList<Integer>();
        for (org.nikhil.webapp.nodeadmin.entity.NodeAndService fetchedNodeAndService :
            nodeAssiginedServices) {

          boolean serviceMatched = false;
          for (Service jsonService : jsonNode.getServices()) {
            if (fetchedNodeAndService
                .getCompositePK()
                .getService()
                .getName()
                .equals(jsonService.getName())) {
              serviceMatched = true;
              break;
            }
          }

          if (!serviceMatched) {
            deleteIndices.add(index);
          }
          index++;
        }

        // delete unmatched services
        for (Integer indexToDelete : deleteIndices) {
          nodeAssiginedServices.remove(indexToDelete.intValue());
        }
      }
    }

    List<NodeAndCommand> nodeAssiginedCommands = jpaNode.getNodeCommands();

    if (jsonNode.getCommands().size() > 0) {
      prevCmmandsPresent = true;
      for (Command jsonCommand : jsonNode.getCommands()) {
        org.nikhil.webapp.nodeadmin.entity.Command jpaCommand = null;
        org.nikhil.webapp.nodeadmin.entity.NodeAndCommand jpaNodeAndCommand = null;
        boolean commandFoundAlreadyLinked = false;

        if (nodeAssiginedCommands.size() > 0) {
          for (org.nikhil.webapp.nodeadmin.entity.NodeAndCommand fetchedNodeAndCommand :
              nodeAssiginedCommands) {
            if (fetchedNodeAndCommand
                .getCompositePK()
                .getCommand()
                .getName()
                .equals(jsonCommand.getName())) {
              jpaNodeAndCommand = fetchedNodeAndCommand;
              commandFoundAlreadyLinked = true;
              break;
            }
          }
        }
        // Add command relationship for current json command
        if (!commandFoundAlreadyLinked || nodeAssiginedCommands.size() == 0) {
          jpaCommand = commandOperation.getCommand(jsonCommand.getName());
          jpaNodeAndCommand = new NodeAndCommand();
          jpaNodeAndCommand.setCompositePK(new NodeAndCommandId(jpaNode, jpaCommand));
          // adding relationship
          jpaNode.getNodeCommands().add(jpaNodeAndCommand);
        }

        // so here we have jpanodeandcommand identified either new and added or retrieved from node
        // itself - update it now
        jpaNodeAndCommand.setAdditionalRunParams(jsonCommand.getAdditionalRunParams());
        jpaNodeAndCommand.setAlwaysRunInBackground(jsonCommand.isAlwaysRunInBackground());
        jpaNodeAndCommand.setRundir(jsonCommand.getRundir());
      }

      /* delete the relationships that are not part of new assignment
       * if adding a node all command relationships are new so no need to delete anything from jpaNode
       */
      if (prevCmmandsPresent && !addingANode) {
        int index = 0;
        ArrayList<Integer> deleteIndices = new ArrayList<Integer>();
        for (org.nikhil.webapp.nodeadmin.entity.NodeAndCommand fetchedNodeAndCommand :
            nodeAssiginedCommands) {

          boolean commandMatched = false;
          for (Command jsonCommand : jsonNode.getCommands()) {
            if (fetchedNodeAndCommand
                .getCompositePK()
                .getCommand()
                .getName()
                .equals(jsonCommand.getName())) {
              commandMatched = true;
              break;
            }
          }

          if (!commandMatched) {
            deleteIndices.add(index);
          }
          index++;
        }

        // delete unmatched commands
        for (Integer indexToDelete : deleteIndices) {
          nodeAssiginedCommands.remove(indexToDelete.intValue());
        }
      }
    }

    return jpaNode;
  }