// Copyright (c) 2011 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#include "chrome/browser/autofill/autofill_metrics.h"

#include "base/logging.h"
#include "base/metrics/histogram.h"
#include "chrome/browser/autofill/autofill_type.h"

namespace {

enum FieldTypeGroupForMetrics {
  AMBIGUOUS = 0,
  NAME,
  COMPANY,
  ADDRESS_LINE_1,
  ADDRESS_LINE_2,
  ADDRESS_CITY,
  ADDRESS_STATE,
  ADDRESS_ZIP,
  ADDRESS_COUNTRY,
  PHONE,
  FAX,
  EMAIL,
  CREDIT_CARD_NAME,
  CREDIT_CARD_NUMBER,
  CREDIT_CARD_DATE,
  NUM_FIELD_TYPE_GROUPS_FOR_METRICS
};

// Translates |field_type| to the corresponding logical grouping for metrics,
// and then interpolates this with the given |metric|, which should be in the
// range [0, |num_possible_metrics|).  Returns the interpolated metric.
// Clients must ensure that |field_type| is one of the types Chrome supports
// natively, e.g. |field_type| must not be a billng address.
int GetFieldTypeGroupMetric(const AutofillFieldType field_type,
                            const int metric,
                            const int num_possible_metrics) {
  DCHECK(metric < num_possible_metrics);

  FieldTypeGroupForMetrics group;
  switch (AutofillType(field_type).group()) {
    case AutofillType::NO_GROUP:
      group = AMBIGUOUS;
      break;

    case AutofillType::NAME:
      group = NAME;
      break;

    case AutofillType::COMPANY:
      group = COMPANY;
      break;

    case AutofillType::ADDRESS_HOME:
      switch (field_type) {
        case ADDRESS_HOME_LINE1:
          group = ADDRESS_LINE_1;
          break;
        case ADDRESS_HOME_LINE2:
          group = ADDRESS_LINE_2;
          break;
        case ADDRESS_HOME_CITY:
          group = ADDRESS_CITY;
          break;
        case ADDRESS_HOME_STATE:
          group = ADDRESS_STATE;
          break;
        case ADDRESS_HOME_ZIP:
          group = ADDRESS_ZIP;
          break;
        case ADDRESS_HOME_COUNTRY:
          group = ADDRESS_COUNTRY;
          break;
        default:
          NOTREACHED();
          group = AMBIGUOUS;
      }
      break;

    case AutofillType::EMAIL:
      group = EMAIL;
      break;

    case AutofillType::PHONE_HOME:
      group = PHONE;
      break;

    case AutofillType::PHONE_FAX:
      group = FAX;
      break;

    case AutofillType::CREDIT_CARD:
      switch (field_type) {
        case ::CREDIT_CARD_NAME:
          group = CREDIT_CARD_NAME;
          break;
        case ::CREDIT_CARD_NUMBER:
          group = CREDIT_CARD_NUMBER;
          break;
        default:
          group = CREDIT_CARD_DATE;
      }
      break;

    default:
      NOTREACHED();
      group = AMBIGUOUS;
  }

  // Interpolate the |metric| with the |group|, so that all metrics for a given
  // |group| are adjacent.  The resulting metrics will be arranged as:
  // AMBIGUOUS_UNKNOWN
  // AMBIGUOUS_MATCH
  // AMBIGUOUS_MISMATCH
  // NAME_UNKNOWN
  // NAME_MATCH
  // NAME_MISMATCH
  // ...
  return (group * num_possible_metrics) + metric;
}

// A version of the UMA_HISTOGRAM_ENUMERATION macro that allows the |name|
// to vary over the program's runtime.
void LogUMAHistogramEnumeration(const std::string& name,
                                int sample,
                                int boundary_value) {
  // We can't use the UMA_HISTOGRAM_ENUMERATION macro here because the histogram
  // name can vary over the duration of the program.
  // Note that this leaks memory; that is expected behavior.
  base::Histogram* counter =
      base::LinearHistogram::FactoryGet(
          name,
          1,
          boundary_value,
          boundary_value + 1,
          base::Histogram::kUmaTargetedHistogramFlag);
  counter->Add(sample);
}

// Logs a type quality metric.  The primary histogram name is constructed based
// on |base_name| and |experiment_id|.  The field-specific histogram name also
// factors in the |field_type|.  Logs a sample of |metric|, which should be in
// the range [0, |num_possible_metrics|).
void LogTypeQualityMetric(const std::string& base_name,
                          const int metric,
                          const int num_possible_metrics,
                          const AutofillFieldType field_type,
                          const std::string& experiment_id) {
  DCHECK(metric < num_possible_metrics);

  std::string histogram_name = base_name;
  if (!experiment_id.empty())
    histogram_name += "_" + experiment_id;
  LogUMAHistogramEnumeration(histogram_name, metric, num_possible_metrics);

  std::string sub_histogram_name = base_name + ".ByFieldType";
  if (!experiment_id.empty())
    sub_histogram_name += "_" + experiment_id;
  const int field_type_group_metric =
      GetFieldTypeGroupMetric(field_type, metric, num_possible_metrics);
  const int num_field_type_group_metrics =
      num_possible_metrics * NUM_FIELD_TYPE_GROUPS_FOR_METRICS;
  LogUMAHistogramEnumeration(sub_histogram_name,
                             field_type_group_metric,
                             num_field_type_group_metrics);
}

}  // namespace

AutofillMetrics::AutofillMetrics() {
}

AutofillMetrics::~AutofillMetrics() {
}

void AutofillMetrics::Log(CreditCardInfoBarMetric metric) const {
  DCHECK(metric < NUM_CREDIT_CARD_INFO_BAR_METRICS);

  UMA_HISTOGRAM_ENUMERATION("Autofill.CreditCardInfoBar", metric,
                            NUM_CREDIT_CARD_INFO_BAR_METRICS);
}

void AutofillMetrics::Log(HeuristicTypeQualityMetric metric,
                          AutofillFieldType field_type,
                          const std::string& experiment_id) const {
  LogTypeQualityMetric("Autofill.Quality.HeuristicType",
                       metric, NUM_HEURISTIC_TYPE_QUALITY_METRICS,
                       field_type, experiment_id);
}

void AutofillMetrics::Log(PredictedTypeQualityMetric metric,
                          AutofillFieldType field_type,
                          const std::string& experiment_id) const {
  LogTypeQualityMetric("Autofill.Quality.PredictedType",
                       metric, NUM_PREDICTED_TYPE_QUALITY_METRICS,
                       field_type, experiment_id);
}

void AutofillMetrics::Log(QualityMetric metric,
                          const std::string& experiment_id) const {
  DCHECK(metric < NUM_QUALITY_METRICS);

  std::string histogram_name = "Autofill.Quality";
  if (!experiment_id.empty())
    histogram_name += "_" + experiment_id;

  LogUMAHistogramEnumeration(histogram_name, metric, NUM_QUALITY_METRICS);
}

void AutofillMetrics::Log(ServerQueryMetric metric) const {
  DCHECK(metric < NUM_SERVER_QUERY_METRICS);

  UMA_HISTOGRAM_ENUMERATION("Autofill.ServerQueryResponse", metric,
                            NUM_SERVER_QUERY_METRICS);
}

void AutofillMetrics::Log(ServerTypeQualityMetric metric,
                          AutofillFieldType field_type,
                          const std::string& experiment_id) const {
  LogTypeQualityMetric("Autofill.Quality.ServerType",
                       metric, NUM_SERVER_TYPE_QUALITY_METRICS,
                       field_type, experiment_id);
}

void AutofillMetrics::LogIsAutofillEnabledAtStartup(bool enabled) const {
  UMA_HISTOGRAM_BOOLEAN("Autofill.IsEnabled.Startup", enabled);
}

void AutofillMetrics::LogIsAutofillEnabledAtPageLoad(bool enabled) const {
  UMA_HISTOGRAM_BOOLEAN("Autofill.IsEnabled.PageLoad", enabled);
}

void AutofillMetrics::LogStoredProfileCount(size_t num_profiles) const {
  UMA_HISTOGRAM_COUNTS("Autofill.StoredProfileCount", num_profiles);
}

void AutofillMetrics::LogAddressSuggestionsCount(size_t num_suggestions) const {
  UMA_HISTOGRAM_COUNTS("Autofill.AddressSuggestionsCount", num_suggestions);
}