class ClusterRestApi {

  @GET
  @Path("/" + RestConstants.NODES)
  @SubmarineApi
  public Response getClusterNodes(){
    ArrayList<HashMap<String, Object>> nodes = new ArrayList<>();

    Map<String, HashMap<String, Object>> clusterMeta = null;
    Map<String, HashMap<String, Object>> intpMeta = null;
    clusterMeta = clusterServer.getClusterMeta(ClusterMetaType.SERVER_META, "");
    intpMeta = clusterServer.getClusterMeta(ClusterMetaType.INTP_PROCESS_META, "");

    // Number of interpreter processes
    for (Map.Entry<String, HashMap<String, Object>> serverMetaEntity : clusterMeta.entrySet()) {
      if (!serverMetaEntity.getValue().containsKey(ClusterMeta.NODE_NAME)) {
        continue;
      }
      String serverNodeName = (String) serverMetaEntity.getValue().get(ClusterMeta.NODE_NAME);

      ArrayList<String> arrIntpProcess = new ArrayList<>();
      int intpProcCount = 0;
      for (Map.Entry<String, HashMap<String, Object>> intpMetaEntity : intpMeta.entrySet()) {
        if (!intpMetaEntity.getValue().containsKey(ClusterMeta.NODE_NAME)
            && !intpMetaEntity.getValue().containsKey(ClusterMeta.INTP_PROCESS_NAME)) {
          continue;
        }
        String intpNodeName = (String) intpMetaEntity.getValue().get(ClusterMeta.NODE_NAME);

        if (serverNodeName.equals(intpNodeName)) {
          intpProcCount++;
          String intpName = (String) intpMetaEntity.getValue().get(ClusterMeta.INTP_PROCESS_NAME);
          arrIntpProcess.add(intpName);
        }
      }
      serverMetaEntity.getValue().put(ClusterMeta.INTP_PROCESS_COUNT, intpProcCount);
      serverMetaEntity.getValue().put(ClusterMeta.INTP_PROCESS_LIST, arrIntpProcess);
    }

    for (Map.Entry<String, HashMap<String, Object>> entry : clusterMeta.entrySet()) {
      String nodeName = entry.getKey();
      Map<String, Object> properties = entry.getValue();

      Map<String, Object> sortProperties = new HashMap<>();

      if (properties.containsKey(ClusterMeta.CPU_USED)
          && properties.containsKey(ClusterMeta.CPU_CAPACITY)) {
        float cpuUsed = (long) properties.get(ClusterMeta.CPU_USED) / (float) 100.0;
        float cpuCapacity = (long) properties.get(ClusterMeta.CPU_CAPACITY) / (float) 100.0;
        float cpuRate = cpuUsed / cpuCapacity * 100;

        String cpuInfo = String.format("%.2f / %.2f = %.2f", cpuUsed, cpuCapacity, cpuRate);
        sortProperties.put(ClusterMeta.CPU_USED + " / " + ClusterMeta.CPU_CAPACITY, cpuInfo + "%");
      }

      if (properties.containsKey(ClusterMeta.MEMORY_USED)
          && properties.containsKey(ClusterMeta.MEMORY_CAPACITY)) {
        float memoryUsed = (long) properties.get(ClusterMeta.MEMORY_USED) / (float) (1024 * 1024 * 1024);
        float memoryCapacity
            = (long) properties.get(ClusterMeta.MEMORY_CAPACITY) / (float) (1024 * 1024 * 1024);
        float memoryRate = memoryUsed / memoryCapacity * 100;

        String memoryInfo = String.format("%.2fGB / %.2fGB = %.2f",
            memoryUsed, memoryCapacity, memoryRate);
        sortProperties.put(ClusterMeta.MEMORY_USED + " / " + ClusterMeta.MEMORY_CAPACITY, memoryInfo + "%");
      }

      if (properties.containsKey(ClusterMeta.SERVER_START_TIME)) {
        // format LocalDateTime
        Object serverStartTime = properties.get(ClusterMeta.SERVER_START_TIME);
        if (serverStartTime instanceof LocalDateTime) {
          LocalDateTime localDateTime = (LocalDateTime) serverStartTime;
          String dateTime = formatLocalDateTime(localDateTime);
          sortProperties.put(ClusterMeta.SERVER_START_TIME, dateTime);
        } else {
          sortProperties.put(ClusterMeta.SERVER_START_TIME, "Wrong time type!");
        }
      }
      if (properties.containsKey(ClusterMeta.STATUS)) {
        sortProperties.put(ClusterMeta.STATUS, properties.get(ClusterMeta.STATUS));
      }
      if (properties.containsKey(ClusterMeta.LATEST_HEARTBEAT)) {
        // format LocalDateTime
        Object latestHeartbeat = properties.get(ClusterMeta.LATEST_HEARTBEAT);
        if (latestHeartbeat instanceof LocalDateTime) {
          LocalDateTime localDateTime = (LocalDateTime) latestHeartbeat;
          String dateTime = formatLocalDateTime(localDateTime);
          sortProperties.put(ClusterMeta.LATEST_HEARTBEAT, dateTime);
        } else {
          sortProperties.put(ClusterMeta.LATEST_HEARTBEAT, "Wrong time type!");
        }
      }
      if (properties.containsKey(ClusterMeta.INTP_PROCESS_LIST)) {
        sortProperties.put(ClusterMeta.INTP_PROCESS_LIST, properties.get(ClusterMeta.INTP_PROCESS_LIST));
      }

      HashMap<String, Object> node = new HashMap<>();
      node.put(ClusterMeta.NODE_NAME, nodeName);
      node.put(ClusterMeta.PROPERTIES, sortProperties);

      nodes.add(node);
    }

    return new JsonResponse.Builder<ArrayList<HashMap<String, Object>>>(Response.Status.OK)
        .success(true).result(nodes).build();
  }

}