// Copyright 2016 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.

#ifndef COMPONENTS_ARC_METRICS_ARC_METRICS_SERVICE_H_
#define COMPONENTS_ARC_METRICS_ARC_METRICS_SERVICE_H_

#include <memory>
#include <string>
#include <vector>

#include "base/macros.h"
#include "base/memory/weak_ptr.h"
#include "base/optional.h"
#include "base/threading/thread_checker.h"
#include "base/time/time.h"
#include "base/timer/timer.h"
#include "chromeos/dbus/power/power_manager_client.h"
#include "components/arc/common/metrics.mojom.h"
#include "components/arc/common/process.mojom.h"
#include "components/arc/session/connection_observer.h"
#include "components/keyed_service/core/keyed_service.h"
#include "components/session_manager/core/session_manager_observer.h"
#include "ui/events/ozone/gamepad/gamepad_observer.h"
#include "ui/wm/public/activation_change_observer.h"

class BrowserContextKeyedServiceFactory;
class PrefService;

namespace aura {
class Window;
}  // namespace aura

namespace base {
class Clock;
class TickClock;
}  // namespace base

namespace content {
class BrowserContext;
}  // namespace content

namespace arc {

class ArcBridgeService;

// Collects information from other ArcServices and send UMA metrics.
class ArcMetricsService : public KeyedService,
                          public wm::ActivationChangeObserver,
                          public session_manager::SessionManagerObserver,
                          public chromeos::PowerManagerClient::Observer,
                          public mojom::MetricsHost,
                          public ui::GamepadObserver {
 public:
  // Delegate for handling window focus observation that is used to track ARC
  // app usage metrics.
  class ArcWindowDelegate {
   public:
    virtual ~ArcWindowDelegate() = default;
    // Returns whether |window| is an ARC window.
    virtual bool IsArcAppWindow(const aura::Window* window) const = 0;
    virtual void RegisterActivationChangeObserver() = 0;
    virtual void UnregisterActivationChangeObserver() = 0;
  };

  // Sets the fake ArcWindowDelegate for testing.
  void SetArcWindowDelegateForTesting(
      std::unique_ptr<ArcWindowDelegate> delegate);

  // Sets Clock for testing.
  void SetClockForTesting(base::Clock* clock);

  // Sets TickClock for testing.
  void SetTickClockForTesting(base::TickClock* tick_clock);

  // Returns singleton instance for the given BrowserContext,
  // or nullptr if the browser |context| is not allowed to use ARC.
  static ArcMetricsService* GetForBrowserContext(
      content::BrowserContext* context);
  static ArcMetricsService* GetForBrowserContextForTesting(
      content::BrowserContext* context);

  // Returns factory instance for this class.
  static BrowserContextKeyedServiceFactory* GetFactory();

  ArcMetricsService(content::BrowserContext* context,
                    ArcBridgeService* bridge_service);
  ~ArcMetricsService() override;

  // Implementations for ConnectionObserver<mojom::ProcessInstance>.
  void OnProcessConnectionReady();
  void OnProcessConnectionClosed();

  // MetricsHost overrides.
  void ReportBootProgress(std::vector<mojom::BootProgressEventPtr> events,
                          mojom::BootType boot_type) override;
  void ReportNativeBridge(mojom::NativeBridgeType native_bridge_type) override;

  // wm::ActivationChangeObserver overrides.
  // Records to UMA when a user has interacted with an ARC app window.
  void OnWindowActivated(wm::ActivationChangeObserver::ActivationReason reason,
                         aura::Window* gained_active,
                         aura::Window* lost_active) override;

  // session_manager::SessionManagerObserver overrides.
  void OnSessionStateChanged() override;

  // chromeos::PowerManagerClient::Observer overrides.
  void ScreenIdleStateChanged(
      const power_manager::ScreenIdleState& proto) override;

  // ui::GamepadObserver overrides.
  void OnGamepadEvent(const ui::GamepadEvent& event) override;

  // ArcAppListPrefs::Observer callbacks which are called through
  // ArcMetricsServiceProxy.
  void OnTaskCreated(int32_t task_id,
                     const std::string& package_name,
                     const std::string& activity,
                     const std::string& intent);
  void OnTaskDestroyed(int32_t task_id);

 private:
  // Adapter to be able to also observe ProcessInstance events.
  class ProcessObserver : public ConnectionObserver<mojom::ProcessInstance> {
   public:
    explicit ProcessObserver(ArcMetricsService* arc_metrics_service);
    ~ProcessObserver() override;

   private:
    // ConnectionObserver<mojom::ProcessInstance> overrides.
    void OnConnectionReady() override;
    void OnConnectionClosed() override;

    ArcMetricsService* arc_metrics_service_;

    DISALLOW_COPY_AND_ASSIGN(ProcessObserver);
  };

  void RequestProcessList();
  void ParseProcessList(std::vector<mojom::RunningAppProcessInfoPtr> processes);

  // DBus callbacks.
  void OnArcStartTimeRetrieved(std::vector<mojom::BootProgressEventPtr> events,
                               mojom::BootType boot_type,
                               base::Optional<base::TimeTicks> arc_start_time);

  // Restores accumulated ARC++ engagement time in previous sessions from
  // profile preferences.
  void RestoreEngagementTimeFromPrefs();

  // Called periodically to save accumulated results to profile preferences.
  void SaveEngagementTimeToPrefs();

  // Called whenever engagement state is changed. Time spent in last state is
  // accumulated to corresponding metrics.
  void UpdateEngagementTime();

  // Records accumulated engagement time metrics to UMA if necessary (i.e. day
  // has changed).
  void RecordEngagementTimeToUmaIfNeeded();

  // Resets accumulated engagement times to zero, and updates both OS version
  // and day ID.
  void ResetEngagementTimePrefs();

  bool ShouldAccumulateEngagementTotalTime() const;
  bool ShouldAccumulateEngagementForegroundTime() const;
  bool ShouldAccumulateEngagementBackgroundTime() const;
  bool ShouldRecordEngagementTimeToUma() const;

  THREAD_CHECKER(thread_checker_);

  ArcBridgeService* const arc_bridge_service_;  // Owned by ArcServiceManager.
  std::unique_ptr<ArcWindowDelegate> arc_window_delegate_;

  ProcessObserver process_observer_;
  base::RepeatingTimer request_process_list_timer_;

  PrefService* const pref_service_;
  const base::Clock* clock_;
  const base::TickClock* tick_clock_;
  base::RepeatingTimer update_engagement_time_timer_;
  base::RepeatingTimer save_engagement_time_to_prefs_timer_;
  base::TimeTicks last_update_ticks_;

  // States for determining which engagement metrics should we accumulate to.
  bool was_session_active_ = false;
  bool was_screen_dimmed_ = false;
  bool was_arc_window_active_ = false;
  std::vector<int32_t> task_ids_;

  // Accumulated results and associated state which are saved to profile
  // preferences at fixed interval.
  int day_id_ = 0;
  base::TimeDelta engagement_time_total_;
  base::TimeDelta engagement_time_foreground_;
  base::TimeDelta engagement_time_background_;

  bool gamepad_interaction_recorded_ = false;

  // Always keep this the last member of this class to make sure it's the
  // first thing to be destructed.
  base::WeakPtrFactory<ArcMetricsService> weak_ptr_factory_;

  DISALLOW_COPY_AND_ASSIGN(ArcMetricsService);
};

}  // namespace arc

#endif  // COMPONENTS_ARC_METRICS_ARC_METRICS_SERVICE_H_
