/** @param p Projection to get metrics for. */
  GridProjectionMetricsImpl(GridProjection p) {
    assert p != null;

    Collection<GridRichNode> nodes = p.nodes();

    int size = nodes.size();

    for (GridRichNode node : nodes) {
      GridNodeMetrics m = node.metrics();

      minActJobs = min(minActJobs, m.getCurrentActiveJobs());
      maxActJobs = max(maxActJobs, m.getCurrentActiveJobs());
      avgActJobs += m.getCurrentActiveJobs();

      minCancelJobs = min(minCancelJobs, m.getCurrentCancelledJobs());
      maxCancelJobs = max(maxCancelJobs, m.getCurrentCancelledJobs());
      avgCancelJobs += m.getCurrentCancelledJobs();

      minRejectJobs = min(minRejectJobs, m.getCurrentRejectedJobs());
      maxRejectJobs = max(maxRejectJobs, m.getCurrentRejectedJobs());
      avgRejectJobs += m.getCurrentRejectedJobs();

      minWaitJobs = min(minWaitJobs, m.getCurrentWaitingJobs());
      maxWaitJobs = max(maxWaitJobs, m.getCurrentWaitingJobs());
      avgWaitJobs += m.getCurrentWaitingJobs();

      minJobExecTime = min(minJobExecTime, m.getCurrentJobExecuteTime());
      maxJobExecTime = max(maxJobExecTime, m.getCurrentJobExecuteTime());
      avgJobExecTime += m.getCurrentJobExecuteTime();

      minJobWaitTime = min(minJobWaitTime, m.getCurrentJobWaitTime());
      maxJobWaitTime = max(maxJobWaitTime, m.getCurrentJobWaitTime());
      avgJobWaitTime += m.getCurrentJobWaitTime();

      minDaemonThreadCnt = min(minDaemonThreadCnt, m.getCurrentDaemonThreadCount());
      maxDaemonThreadCnt = max(maxDaemonThreadCnt, m.getCurrentDaemonThreadCount());
      avgDaemonThreadCnt += m.getCurrentDaemonThreadCount();

      minThreadCnt = min(minThreadCnt, m.getCurrentThreadCount());
      maxThreadCnt = max(maxThreadCnt, m.getCurrentThreadCount());
      avgThreadCnt += m.getCurrentThreadCount();

      minIdleTime = min(minIdleTime, m.getCurrentIdleTime());
      maxIdleTime = max(maxIdleTime, m.getCurrentIdleTime());
      avgIdleTime += m.getCurrentIdleTime();

      minBusyTimePerc = min(minBusyTimePerc, m.getBusyTimePercentage());
      maxBusyTimePerc = max(maxBusyTimePerc, m.getBusyTimePercentage());
      avgBusyTimePerc += m.getBusyTimePercentage();

      minCpuLoad = min(minCpuLoad, m.getCurrentCpuLoad());
      maxCpuLoad = max(maxCpuLoad, m.getCurrentCpuLoad());
      avgCpuLoad += m.getCurrentCpuLoad();

      minHeapMemCmt = min(minHeapMemCmt, m.getHeapMemoryCommitted());
      maxHeapMemCmt = max(maxHeapMemCmt, m.getHeapMemoryCommitted());
      avgHeapMemCmt += m.getHeapMemoryCommitted();

      minHeapMemUsed = min(minHeapMemUsed, m.getHeapMemoryUsed());
      maxHeapMemUsed = max(maxHeapMemUsed, m.getHeapMemoryUsed());
      avgHeapMemUsed += m.getHeapMemoryUsed();

      minHeapMemMax = min(minHeapMemMax, m.getHeapMemoryMaximum());
      maxHeapMemMax = max(maxHeapMemMax, m.getHeapMemoryMaximum());
      avgHeapMemMax += m.getHeapMemoryMaximum();

      minHeapMemInit = min(minHeapMemInit, m.getHeapMemoryInitialized());
      maxHeapMemInit = max(maxHeapMemInit, m.getHeapMemoryInitialized());
      avgHeapMemInit += m.getHeapMemoryInitialized();

      minNonHeapMemCmt = min(minNonHeapMemCmt, m.getNonHeapMemoryCommitted());
      maxNonHeapMemCmt = max(maxNonHeapMemCmt, m.getNonHeapMemoryCommitted());
      avgNonHeapMemCmt += m.getNonHeapMemoryCommitted();

      minNonHeapMemUsed = min(minNonHeapMemUsed, m.getNonHeapMemoryUsed());
      maxNonHeapMemUsed = max(maxNonHeapMemUsed, m.getNonHeapMemoryUsed());
      avgNonHeapMemUsed += m.getNonHeapMemoryUsed();

      minNonHeapMemMax = min(minNonHeapMemMax, m.getNonHeapMemoryMaximum());
      maxNonHeapMemMax = max(maxNonHeapMemMax, m.getNonHeapMemoryMaximum());
      avgNonHeapMemMax += m.getNonHeapMemoryMaximum();

      minNonHeapMemInit = min(minNonHeapMemInit, m.getNonHeapMemoryInitialized());
      maxNonHeapMemInit = max(maxNonHeapMemInit, m.getNonHeapMemoryInitialized());
      avgNonHeapMemInit += m.getNonHeapMemoryInitialized();

      minUpTime = min(minUpTime, m.getUpTime());
      maxUpTime = max(maxUpTime, m.getUpTime());
      avgUpTime += m.getUpTime();

      minCpusPerNode = min(minCpusPerNode, m.getTotalCpus());
      maxCpusPerNode = max(maxCpusPerNode, m.getTotalCpus());
      avgCpusPerNode += m.getTotalCpus();
    }

    avgActJobs /= size;
    avgCancelJobs /= size;
    avgRejectJobs /= size;
    avgWaitJobs /= size;
    avgJobExecTime /= size;
    avgJobWaitTime /= size;
    avgDaemonThreadCnt /= size;
    avgThreadCnt /= size;
    avgIdleTime /= size;
    avgBusyTimePerc /= size;
    avgCpuLoad /= size;
    avgHeapMemCmt /= size;
    avgHeapMemUsed /= size;
    avgHeapMemMax /= size;
    avgHeapMemInit /= size;
    avgNonHeapMemCmt /= size;
    avgNonHeapMemUsed /= size;
    avgNonHeapMemMax /= size;
    avgNonHeapMemInit /= size;
    avgUpTime /= size;
    avgCpusPerNode /= size;

    //
    // Note that since we are accessing projection
    // again it can get out of sync with previous
    // measurements as projection could have been
    // changed.
    //
    // We accept it for simplicity and performance
    // reasons. Metrics are not guaranteed to be
    // "transactional".
    //

    youngestNodeStartTime = p.youngest().metrics().getNodeStartTime();
    oldestNodeStartTime = p.oldest().metrics().getNodeStartTime();

    Collection<GridProjection> neighborhood = p.neighborhood();

    for (GridProjection neighbors : neighborhood) {
      minNodesPerHost = min(minNodesPerHost, neighbors.size());
      maxNodesPerHost = max(maxNodesPerHost, neighbors.size());
      avgNodesPerHost += neighbors.size();
    }

    avgNodesPerHost /= neighborhood.size();

    totalCpus = p.cpus();
    totalHosts = p.hosts();
    totalNodes = p.size();
  }