/*
 * Licensed to the Apache Software Foundation (ASF) under one
 * or more contributor license agreements.  See the NOTICE file
 * distributed with this work for additional information
 * regarding copyright ownership.  The ASF licenses this file
 * to you under the Apache License, Version 2.0 (the
 * "License"); you may not use this file except in compliance
 * with the License.  You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package org.apache.hadoop.hive.ql.exec.tez.monitoring;

import org.apache.hadoop.fs.FileSystem;
import org.apache.hadoop.hive.ql.exec.Utilities;
import org.apache.hadoop.hive.ql.session.SessionState;
import org.apache.tez.common.counters.FileSystemCounter;
import org.apache.tez.common.counters.TezCounters;
import org.apache.tez.dag.api.TezException;
import org.apache.tez.dag.api.client.DAGClient;
import org.apache.tez.dag.api.client.Progress;
import org.apache.tez.dag.api.client.StatusGetOpts;

import java.io.IOException;
import java.util.*;

import static org.apache.hadoop.hive.ql.exec.tez.monitoring.Constants.SEPARATOR;
import static org.apache.hadoop.hive.ql.exec.tez.monitoring.TezJobMonitor.getCounterValueByGroupName;

public class FSCountersSummary implements PrintSummary {

  private static final String FORMATTING_PATTERN = "%10s %15s %13s %18s %18s %13s";
  private static final String HEADER = String.format(FORMATTING_PATTERN,
      "VERTICES", "BYTES_READ", "READ_OPS", "LARGE_READ_OPS", "BYTES_WRITTEN", "WRITE_OPS");

  private Map<String, Progress> progressMap;
  private DAGClient dagClient;

  FSCountersSummary(Map<String, Progress> progressMap, DAGClient dagClient) {
    this.progressMap = progressMap;
    this.dagClient = dagClient;
  }

  @Override
  public void print(SessionState.LogHelper console) {
    console.printInfo("FileSystem Counters Summary");

    SortedSet<String> keys = new TreeSet<>(progressMap.keySet());
    Set<StatusGetOpts> statusOptions = new HashSet<>(1);
    statusOptions.add(StatusGetOpts.GET_COUNTERS);
    // Assuming FileSystem.getAllStatistics() returns all schemes that are accessed on task side
    // as well. If not, we need a way to get all the schemes that are accessed by the tez task/llap.
    for (FileSystem.Statistics statistics : FileSystem.getAllStatistics()) {
      final String scheme = statistics.getScheme().toUpperCase();

      console.printInfo("");
      console.printInfo("Scheme: " + scheme);
      console.printInfo(SEPARATOR);
      console.printInfo(HEADER);
      console.printInfo(SEPARATOR);

      for (String vertexName : keys) {
        TezCounters vertexCounters = vertexCounters(statusOptions, vertexName);
        if (vertexCounters != null) {
          console.printInfo(summary(scheme, vertexName, vertexCounters));
        }
      }

      console.printInfo(SEPARATOR);
    }
  }

  private String summary(String scheme, String vertexName, TezCounters vertexCounters) {
    final String counterGroup = FileSystemCounter.class.getName();
    final long bytesRead = getCounterValueByGroupName(vertexCounters,
        counterGroup, scheme + "_" + FileSystemCounter.BYTES_READ.name());
    final long bytesWritten = getCounterValueByGroupName(vertexCounters,
        counterGroup, scheme + "_" + FileSystemCounter.BYTES_WRITTEN.name());
    final long readOps = getCounterValueByGroupName(vertexCounters,
        counterGroup, scheme + "_" + FileSystemCounter.READ_OPS.name());
    final long largeReadOps = getCounterValueByGroupName(vertexCounters,
        counterGroup, scheme + "_" + FileSystemCounter.LARGE_READ_OPS.name());
    final long writeOps = getCounterValueByGroupName(vertexCounters,
        counterGroup, scheme + "_" + FileSystemCounter.WRITE_OPS.name());

    return String.format(FORMATTING_PATTERN,
        vertexName,
        Utilities.humanReadableByteCount(bytesRead),
        readOps,
        largeReadOps,
        Utilities.humanReadableByteCount(bytesWritten),
        writeOps);
  }

  private TezCounters vertexCounters(Set<StatusGetOpts> statusOptions, String vertexName) {
    try {
      return dagClient.getVertexStatus(vertexName, statusOptions).getVertexCounters();
    } catch (IOException | TezException e) {
      // best attempt, shouldn't really kill DAG for this
    }
    return null;
  }
}
