// Copyright 2014 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 "chromecast/browser/metrics/cast_metrics_service_client.h"

#include "base/command_line.h"
#include "base/guid.h"
#include "base/i18n/rtl.h"
#include "base/prefs/pref_service.h"
#include "chromecast/browser/metrics/cast_stability_metrics_provider.h"
#include "chromecast/browser/metrics/platform_metrics_providers.h"
#include "chromecast/common/chromecast_switches.h"
#include "chromecast/common/pref_names.h"
#include "components/metrics/client_info.h"
#include "components/metrics/gpu/gpu_metrics_provider.h"
#include "components/metrics/metrics_pref_names.h"
#include "components/metrics/metrics_provider.h"
#include "components/metrics/metrics_service.h"
#include "components/metrics/metrics_state_manager.h"
#include "components/metrics/net/net_metrics_log_uploader.h"
#include "components/metrics/net/network_metrics_provider.h"
#include "components/metrics/profiler/profiler_metrics_provider.h"
#include "content/public/common/content_switches.h"

#if defined(OS_LINUX)
#include "chromecast/browser/metrics/external_metrics.h"
#endif  // defined(OS_LINUX)

namespace chromecast {
namespace metrics {

// static
scoped_ptr<CastMetricsServiceClient> CastMetricsServiceClient::Create(
    base::TaskRunner* io_task_runner,
    PrefService* pref_service,
    net::URLRequestContextGetter* request_context) {
  return make_scoped_ptr(new CastMetricsServiceClient(io_task_runner,
                                                      pref_service,
                                                      request_context));
}

void CastMetricsServiceClient::SetMetricsClientId(
    const std::string& client_id) {
  client_id_ = client_id;
  LOG(INFO) << "Metrics client ID set: " << client_id;
  PlatformSetClientID(cast_service_, client_id);
}

void CastMetricsServiceClient::StoreClientInfo(
    const ::metrics::ClientInfo& client_info) {
  const std::string& client_id = client_info.client_id;
  DCHECK(client_id.empty() || base::IsValidGUID(client_id));
  // backup client_id or reset to empty.
  SetMetricsClientId(client_id);
}

scoped_ptr< ::metrics::ClientInfo> CastMetricsServiceClient::LoadClientInfo() {
  scoped_ptr< ::metrics::ClientInfo> client_info(new ::metrics::ClientInfo);

  // kMetricsIsNewClientID would be missing if either the device was just
  // FDR'ed, or it is on pre-v1.2 build.
  if (!pref_service_->GetBoolean(prefs::kMetricsIsNewClientID)) {
    // If the old client id exists, the device must be on pre-v1.2 build,
    // instead of just being FDR'ed.
    if (!pref_service_->GetString(::metrics::prefs::kMetricsOldClientID)
        .empty()) {
      // Force old client id to be regenerated. See b/9487011.
      client_info->client_id = base::GenerateGUID();
      pref_service_->SetBoolean(prefs::kMetricsIsNewClientID, true);
      return client_info.Pass();
    }
    // else the device was just FDR'ed, pass through.
  }

  const std::string client_id(GetPlatformClientID(cast_service_));
  if (!client_id.empty() && base::IsValidGUID(client_id)) {
    client_info->client_id = client_id;
    return client_info.Pass();
  } else {
    if (client_id.empty()) {
      LOG(WARNING) << "Empty client id from platform,"
                   << " assuming this is the first boot up of a new device.";
    } else {
      LOG(ERROR) << "Invalid client id " << client_id << " from platform.";
    }
    return scoped_ptr< ::metrics::ClientInfo>();
  }
}



bool CastMetricsServiceClient::IsOffTheRecordSessionActive() {
  // Chromecast behaves as "off the record" w/r/t recording browsing state,
  // but this value is about not disabling metrics because of it.
  return false;
}

int32_t CastMetricsServiceClient::GetProduct() {
  // Chromecast currently uses the same product identifier as Chrome.
  return ::metrics::ChromeUserMetricsExtension::CHROME;
}

std::string CastMetricsServiceClient::GetApplicationLocale() {
  return base::i18n::GetConfiguredLocale();
}

bool CastMetricsServiceClient::GetBrand(std::string* brand_code) {
  return false;
}

::metrics::SystemProfileProto::Channel CastMetricsServiceClient::GetChannel() {
  return GetPlatformReleaseChannel(cast_service_);
}

std::string CastMetricsServiceClient::GetVersionString() {
  return GetPlatformVersionString(cast_service_);
}

void CastMetricsServiceClient::OnLogUploadComplete() {
  PlatformOnLogUploadComplete(cast_service_);
}

void CastMetricsServiceClient::StartGatheringMetrics(
    const base::Closure& done_callback) {
  done_callback.Run();
}

void CastMetricsServiceClient::CollectFinalMetrics(
    const base::Closure& done_callback) {
  done_callback.Run();
}

scoped_ptr< ::metrics::MetricsLogUploader>
CastMetricsServiceClient::CreateUploader(
    const std::string& server_url,
    const std::string& mime_type,
    const base::Callback<void(int)>& on_upload_complete) {
  std::string uma_server_url(server_url);
  base::CommandLine* command_line = base::CommandLine::ForCurrentProcess();
  if (command_line->HasSwitch(switches::kOverrideMetricsUploadUrl)) {
    uma_server_url.assign(
        command_line->GetSwitchValueASCII(switches::kOverrideMetricsUploadUrl));
  }
  DCHECK(!uma_server_url.empty());
  return scoped_ptr< ::metrics::MetricsLogUploader>(
      new ::metrics::NetMetricsLogUploader(
          request_context_,
          uma_server_url,
          mime_type,
          on_upload_complete));
}

void CastMetricsServiceClient::EnableMetricsService(bool enabled) {
  if (!metrics_service_loop_->BelongsToCurrentThread()) {
    metrics_service_loop_->PostTask(
        FROM_HERE,
        base::Bind(&CastMetricsServiceClient::EnableMetricsService,
                   base::Unretained(this),
                   enabled));
    return;
  }

  if (enabled) {
    metrics_service_->Start();
  } else {
    metrics_service_->Stop();
  }
}

CastMetricsServiceClient::CastMetricsServiceClient(
    base::TaskRunner* io_task_runner,
    PrefService* pref_service,
    net::URLRequestContextGetter* request_context)
    : io_task_runner_(io_task_runner),
      pref_service_(pref_service),
      cast_service_(NULL),
      external_metrics_(NULL),
      metrics_service_loop_(base::MessageLoopProxy::current()),
      request_context_(request_context) {
}

CastMetricsServiceClient::~CastMetricsServiceClient() {
  DCHECK(!external_metrics_);
}

void CastMetricsServiceClient::Initialize(CastService* cast_service) {
  DCHECK(cast_service);
  DCHECK(!cast_service_);
  cast_service_ = cast_service;

  metrics_state_manager_ = ::metrics::MetricsStateManager::Create(
      pref_service_,
      base::Bind(&CastMetricsServiceClient::IsReportingEnabled,
                 base::Unretained(this)),
      base::Bind(&CastMetricsServiceClient::StoreClientInfo,
                 base::Unretained(this)),
      base::Bind(&CastMetricsServiceClient::LoadClientInfo,
                 base::Unretained(this)));
  metrics_service_.reset(new ::metrics::MetricsService(
      metrics_state_manager_.get(),
      this,
      pref_service_));

  // Always create a client id as it may also be used by crash reporting,
  // (indirectly) included in feedback, and can be queried during setup.
  // For UMA and crash reporting, associated opt-in settings will control
  // sending reports as directed by the user.
  // For Setup (which also communicates the user's opt-in preferences),
  // report the client-id and expect that setup will handle the current opt-in
  // value.
  metrics_state_manager_->ForceClientIdCreation();

  CastStabilityMetricsProvider* stability_provider =
      new CastStabilityMetricsProvider(metrics_service_.get());
  metrics_service_->RegisterMetricsProvider(
      scoped_ptr< ::metrics::MetricsProvider>(stability_provider));

  base::CommandLine* command_line = base::CommandLine::ForCurrentProcess();
  if (!command_line->HasSwitch(switches::kDisableGpu)) {
    metrics_service_->RegisterMetricsProvider(
        scoped_ptr< ::metrics::MetricsProvider>(
            new ::metrics::GPUMetricsProvider));
  }
  metrics_service_->RegisterMetricsProvider(
      scoped_ptr< ::metrics::MetricsProvider>(
          new ::metrics::NetworkMetricsProvider(io_task_runner_)));
  metrics_service_->RegisterMetricsProvider(
      scoped_ptr< ::metrics::MetricsProvider>(
          new ::metrics::ProfilerMetricsProvider));
  RegisterPlatformMetricsProviders(metrics_service_.get(), cast_service_);

  metrics_service_->InitializeMetricsRecordingState();
#if !defined(OS_ANDROID)
  // Reset clean_shutdown bit after InitializeMetricsRecordingState().
  metrics_service_->LogNeedForCleanShutdown();
#endif  // !defined(OS_ANDROID)

  if (IsReportingEnabled())
    metrics_service_->Start();

  // Start external metrics collection, which feeds data from external
  // processes into the main external metrics.
#if defined(OS_LINUX)
  external_metrics_ = new ExternalMetrics(stability_provider);
  external_metrics_->Start();
#endif  // defined(OS_LINUX)
}

void CastMetricsServiceClient::Finalize() {
#if !defined(OS_ANDROID)
  // Set clean_shutdown bit.
  metrics_service_->RecordCompletedSessionEnd();
#endif  // !defined(OS_ANDROID)

  // Stop metrics service cleanly before destructing CastMetricsServiceClient.
#if defined(OS_LINUX)
  external_metrics_->StopAndDestroy();
  external_metrics_ = NULL;
#endif  // defined(OS_LINUX)
  metrics_service_->Stop();
}

bool CastMetricsServiceClient::IsReportingEnabled() {
  return PlatformIsReportingEnabled(cast_service_);
}

}  // namespace metrics
}  // namespace chromecast
