/*
 * Copyright (C) 2018 The Android Open Source Project
 *
 * Licensed 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.
 */
#ifndef ANDROID_EVENT_METRIC_H_
#define ANDROID_EVENT_METRIC_H_

#include <media/MediaMetricsItem.h>
#include <utils/Timers.h>

namespace android {

// This is a simple holder for the statistics recorded in EventMetric.
struct EventStatistics {
  // The count of times the event occurred.
  int64_t count;

  // The minimum and maximum values recorded in the Record method.
  double min;
  double max;

  // The average (mean) of all values recorded.
  double mean;
  // The sum of squared devation. Variance can be calculated from
  // this value.
  //    var = sum_squared_deviation / count;
  double sum_squared_deviation;
};

// The EventMetric class is used to accumulate stats about an event over time.
// A common use case is to track clock timings for a method call or operation.
// An EventMetric can break down stats by a dimension specified by the
// application. E.g. an application may want to track counts broken out by
// error code or the size of some parameter.
//
// Example:
//
//   struct C {
//     status_t DoWork() {
//       unsigned long start_time = now();
//       status_t result;
//
//       // DO WORK and determine result;
//
//       work_event_.Record(now() - start_time, result);
//
//       return result;
//     }
//     EventMetric<status_t> work_event_;
//   };
//
//   C c;
//   c.DoWork();
//
//   std::map<int, int64_t> values;
//   metric.ExportValues(
//       [&] (int attribute_value, int64_t value) {
//            values[attribute_value] = value;
//       });
//   // Do something with the exported stat.
//
template<typename AttributeType>
class EventMetric {
 public:
  // Instantiate the counter with the given metric name and
  // attribute names. |attribute_names| must not be null.
  EventMetric(
      const std::string& metric_name,
      const std::string& attribute_name)
          : metric_name_(metric_name),
            attribute_name_(attribute_name) {}

  // Increment the count of times the operation occurred with this
  // combination of attributes.
  void Record(double value, AttributeType attribute) {
    if (values_.find(attribute) != values_.end()) {
      EventStatistics* stats = values_[attribute].get();
      // Using method of provisional means.
      double deviation = value - stats->mean;
      stats->mean = stats->mean + (deviation / stats->count);
      stats->sum_squared_deviation =
          stats->sum_squared_deviation + (deviation * (value - stats->mean));
      stats->count++;

      stats->min = stats->min < value ? stats->min : value;
      stats->max = stats->max > value ? stats->max : value;
    } else {
      std::unique_ptr<EventStatistics> stats =
          std::make_unique<EventStatistics>();
      stats->count = 1;
      stats->min = value;
      stats->max = value;
      stats->mean = value;
      stats->sum_squared_deviation = 0;
      values_[attribute] = std::move(stats);
    }
  };

  // Export the metrics to the provided |function|. Each value for Attribute
  // has a separate set of stats. As such, |function| will be called once per
  // value of Attribute.
  void ExportValues(
      std::function<void (const AttributeType&,
                          const EventStatistics&)> function) const {
    for (auto it = values_.begin(); it != values_.end(); it++) {
      function(it->first, *(it->second));
    }
  }

  const std::string& metric_name() const { return metric_name_; };

 private:
  const std::string metric_name_;
  const std::string attribute_name_;
  std::map<AttributeType, std::unique_ptr<struct EventStatistics>> values_;
};

// The EventTimer is a supporting class for EventMetric instances that are used
// to time methods. The EventTimer starts a timer when first in scope, and
// records the timing when exiting scope.
//
// Example:
//
// EventMetric<int> my_metric;
//
// {
//   EventTimer<int> my_timer(&my_metric);
//   // Set the attribute to associate with this timing.
//   my_timer.SetAttribtue(42);
//
//   // Do some work that you want to time.
//
// }  // The EventTimer destructor will record the the timing in my_metric;
//
template<typename AttributeType>
class EventTimer {
 public:
  explicit EventTimer(EventMetric<AttributeType>* metric)
      :start_time_(systemTime()), metric_(metric) {
  }

  virtual ~EventTimer() {
    if (metric_) {
      metric_->Record(ns2us(systemTime() - start_time_), attribute_);
    }
  }

  // Set the attribute to associate with this timing. E.g. this can be used to
  // record the return code from the work that was timed.
  void SetAttribute(const AttributeType& attribute) {
    attribute_ = attribute;
  }

 protected:
  // Visible for testing only.
  nsecs_t start_time_;

 private:
  EventMetric<AttributeType>* metric_;
  AttributeType attribute_;
};

}  // namespace android

#endif  // ANDROID_EVENT_METRIC_H_
