package hip.ch8;


import org.apache.commons.lang.reflect.MethodUtils;
import org.apache.hadoop.mapred.JobHistory;

import java.lang.reflect.InvocationTargetException;
import java.text.ParseException;
import java.util.Collections;
import java.util.Comparator;
import java.util.List;

public final class DataSkewMetrics {

  public static void main(String... args) throws Exception {
    try {
      dumpTaskTimes(args);
    } catch (Throwable t) {
      t.printStackTrace();
    }
  }

  public static void dumpTaskTimes(String... args)
      throws Exception {
    printTasks(JobHistoryHelper.getJobInfoFromCliArgs(args));
  }

  public static void printTasks(JobHistory.JobInfo job)
      throws ParseException, IllegalAccessException,
      InvocationTargetException, NoSuchMethodException {

    List<TaskMetrics> mapMetrics = JobHistoryHelper.getMapTaskMetrics(
        job);
    List<TaskMetrics> reduceMetrics = JobHistoryHelper.getReduceTaskMetrics(
        job);

    decorateHeader("MAP TASKS");

    dumpTasks(mapMetrics, "execution time",
        new TaskMetrics.ExecTimeComparator(), "getOverallTimeMillis",
        true);
    dumpTasks(mapMetrics, "input records",
        new TaskMetrics.InputRecordsComparator(), "getInputRecords",
        false);
    dumpTasks(mapMetrics, "input bytes",
        new TaskMetrics.InputBytesComparator(), "getInputBytes",
        false);

    decorateHeader("REDUCE TASKS");

    dumpTasks(reduceMetrics, "execution time",
        new TaskMetrics.ExecTimeComparator(), "getOverallTimeMillis", true);
    dumpTasks(reduceMetrics, "input records",
        new TaskMetrics.InputRecordsComparator(), "getInputRecords",
        false);
    dumpTasks(reduceMetrics, "input bytes",
        new TaskMetrics.InputBytesComparator(), "getInputBytes",
        false);
  }

  public static void decorateHeader(String header) {
    System.out.println("****************************************");
    System.out.println(String.format("** %24s%10s **", header, ""));
    System.out.println("****************************************");
  }

  public static void dumpTasks(List<TaskMetrics> metrics,
                               String heading,
                               Comparator<TaskMetrics> comparator,
                               String fieldName,
                               boolean isTime)
      throws IllegalAccessException, InvocationTargetException,
      NoSuchMethodException {

    if (metrics.size() == 0) {
      return;
    }
    System.out.println("== Tasks ordered by " + heading + " ==");

    PaddedTable table = new PaddedTable();
    table
        .addColumnTitle("Type")
        .addColumnTitle("TaskId")
        .addColumnTitle("Status")
        .addColumnTitle("Host")
        .addColumnTitle("ExecutionTime")
        .addColumnTitle("InputBytes")
        .addColumnTitle("OutputBytes")
        .addColumnTitle("InputRecords")
        .addColumnTitle("OputputRecords");

    Collections.sort(metrics, comparator);
    Collections.reverse(metrics);

    Long minVal = null;
    Long maxVal = null;
    long totalVals = 0;

    for (TaskMetrics m : metrics) {
      long v = extractLongFieldValue(m, fieldName);
      minVal = minVal == null ? v : Math.min(minVal, v);
      maxVal = maxVal == null ? v : Math.max(maxVal, v);
      totalVals += v;

      table.newRow();
      table.addColumnValue(m.getType())
          .addColumnValue(m.getTaskId())
          .addColumnValue(m.getStatus())
          .addColumnValue(m.getHost())
          .addColumnValue(JobHistoryHelper.formatTime(
              m.getOverallTimeMillis()))
          .addColumnValue(m.getInputBytes())
          .addColumnValue(m.getOutputBytes())
          .addColumnValue(m.getInputRecords())
          .addColumnValue(m.getOutputRecords());
    }

    System.out.println();
    if (isTime) {
      System.out
          .println(String.format("Min/max/avg (HH:MM:SS) = %s/%s/%s",
              JobHistoryHelper.formatTime(minVal),
              JobHistoryHelper.formatTime(maxVal),
              JobHistoryHelper.formatTime(totalVals / metrics.size())));
    } else {
      System.out.println(String.format("Min/max/avg = %d/%d/%d",
          minVal, maxVal, totalVals / metrics.size()));
    }
    System.out.println();
    System.out.println(table);
  }

  public static long extractLongFieldValue(TaskMetrics m,
                                           String fieldName)
      throws IllegalAccessException, InvocationTargetException,
      NoSuchMethodException {
    return (Long) MethodUtils.invokeMethod(m, fieldName, null);
  }
}