#ifndef MEDIAPIPE_FRAMEWORK_PROFILER_REPORTER_REPORTER_H_
#define MEDIAPIPE_FRAMEWORK_PROFILER_REPORTER_REPORTER_H_

#include <memory>
#include <ostream>
#include <set>

#include "map"
#include "mediapipe/framework/calculator.pb.h"
#include "mediapipe/framework/calculator_profile.pb.h"
#include "mediapipe/framework/port/status.h"
#include "mediapipe/framework/profiler/reporter/statistic.h"

namespace mediapipe {
namespace reporter {

struct GraphData {
  int64_t min_time = std::numeric_limits<int64_t>::max();

  int64_t max_time = std::numeric_limits<int64_t>::min();

  int64_t total_time = 0;
};

// Holds all of the measured data for a calculator.
struct CalculatorData {
  // Name of the calculator.
  std::string name;

  // The number of times the calculator was detected entering a PROCESS state.
  int counter;

  // The number of times the calculator finished its process state.
  int completed;

  // The number of dropped packets.
  double dropped;

  // Specifies how quickly (1/s) this calculator can output events given its
  // input latency and processing time.
  double fps;

  // Reports how often this calculator was called across the lifetime of the
  // trace (1/s).
  double frequency;

  // The max number of threads used.
  double thread_count;

  // Reports how fast this calculator can run in isolation. (1/s).
  double processing_rate;

  // Percentage of the total time spent in this calculator.
  double time_percent;

  // Records the total time this calculator spent in PROCESS (microseconds).
  Statistic time_stat;

  // Records the input latency (microseconds). This is the longest time
  // it takes for the input packets of a calculator to reach this calculator
  // from their origin.
  Statistic input_latency_stat;

  // The threads on which this calculator ran.
  std::set<int> threads;
};

// A snapshot of statistics generated by Reporter.
class Report {
 public:
  virtual ~Report() = default;

  // Prints the data contained within the report to a given
  // stream (e.g., std::cout).
  virtual void Print(std::ostream& output) = 0;

  // Provides the list of headers included in the report. The column
  // "calculator" will always come first, followed by the selected
  // columns in alphabetical order.
  virtual const std::vector<std::string>& headers() = 0;

  // Provides a line for each calculator which in turn contains the name
  // of the calculator followed by stringified values of each column's
  // statistics.
  virtual const std::vector<std::vector<std::string>>& lines() = 0;

  // Returns summary data for the graph. Invalidated if Report() is called again
  // on Reporter.
  virtual const GraphData& graph_data() = 0;

  // Returns summary data for each calculator in the graph. Invalidated if
  // Report() is called again on Reporter.
  virtual const std::map<std::string, CalculatorData>& calculator_data() = 0;
};

// Provides a way to accumulate statistics from one or more
// GraphProfile protobufs in order to generate a report summarizing
// the statistics within them.
class Reporter {
 public:
  Reporter();

  // Adds the contents of a given profile.
  void Accumulate(const mediapipe::GraphProfile& profile);

  // Accepts names of of columns or wildcard patterns (* or ?) to
  // select which statistics columns will be included in a generated
  // report.
  absl::Status set_columns(const std::vector<std::string>& columns);

  // Generates a report based on the current accumulated statistics.
  std::unique_ptr<reporter::Report> Report();

  // Set to true to remove decorative whitespace from the output.
  void set_compact(bool value) { compact_flag_ = value; }

 private:
  bool compact_flag_ = false;

  std::vector<std::string> columns_;

  // Maps calculator.name -> profile information for that calculator.
  std::map<std::string, CalculatorData> calculator_data_;
  GraphData graph_data_;
};

}  // namespace reporter
}  // namespace mediapipe

#endif  // MEDIAPIPE_FRAMEWORK_PROFILER_REPORTER_REPORTER_H_
