// Copyright 2020 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 ASH_WEBUI_DIAGNOSTICS_UI_BACKEND_SYSTEM_ROUTINE_CONTROLLER_H_
#define ASH_WEBUI_DIAGNOSTICS_UI_BACKEND_SYSTEM_ROUTINE_CONTROLLER_H_

#include <memory>

#include "ash/webui/diagnostics_ui/mojom/system_routine_controller.mojom.h"
#include "base/memory/weak_ptr.h"
#include "base/time/time.h"
#include "chromeos/services/cros_healthd/public/mojom/cros_healthd.mojom.h"
#include "chromeos/services/cros_healthd/public/mojom/cros_healthd_diagnostics.mojom-forward.h"
#include "mojo/public/cpp/bindings/pending_receiver.h"
#include "mojo/public/cpp/bindings/receiver.h"
#include "mojo/public/cpp/bindings/remote.h"
#include "services/data_decoder/public/cpp/data_decoder.h"
#include "services/device/public/mojom/wake_lock.mojom.h"
#include "services/device/public/mojom/wake_lock_provider.mojom.h"
#include "third_party/abseil-cpp/absl/types/optional.h"

namespace base {
class OneShotTimer;
}  // namespace base

namespace ash {
namespace diagnostics {

class RoutineLog;

constexpr int32_t kInvalidRoutineId = 0;

using RunRoutineCallback = base::OnceCallback<void(
    chromeos::cros_healthd::mojom::RunRoutineResponsePtr)>;

class SystemRoutineController : public mojom::SystemRoutineController {
 public:
  SystemRoutineController();
  explicit SystemRoutineController(RoutineLog* routine_log_ptr);
  ~SystemRoutineController() override;

  SystemRoutineController(const SystemRoutineController&) = delete;
  SystemRoutineController& operator=(const SystemRoutineController&) = delete;

  // mojom::SystemRoutineController:
  void GetSupportedRoutines(GetSupportedRoutinesCallback callback) override;
  void RunRoutine(mojom::RoutineType type,
                  mojo::PendingRemote<mojom::RoutineRunner> runner) override;

  void BindInterface(
      mojo::PendingReceiver<mojom::SystemRoutineController> pending_receiver);
  // Handler for when remote attached to |receiver_| disconnects.
  void OnBoundInterfaceDisconnect();
  bool ReceiverIsBound();

  void SetWakeLockProviderForTesting(
      mojo::Remote<device::mojom::WakeLockProvider> provider) {
    wake_lock_provider_ = std::move(provider);
  }

 private:
  void OnAvailableRoutinesFetched(
      GetSupportedRoutinesCallback callback,
      const std::vector<chromeos::cros_healthd::mojom::DiagnosticRoutineEnum>&
          supported_routines);

  void ExecuteRoutine(mojom::RoutineType routine_type);

  void OnRoutineStarted(
      mojom::RoutineType routine_type,
      chromeos::cros_healthd::mojom::RunRoutineResponsePtr response_ptr);

  void OnPowerRoutineStarted(
      mojom::RoutineType routine_type,
      chromeos::cros_healthd::mojom::RunRoutineResponsePtr response_ptr);

  void ContinuePowerRoutine(mojom::RoutineType routine_type);

  void OnPowerRoutineContinued(
      mojom::RoutineType routine_type,
      chromeos::cros_healthd::mojom::RoutineUpdatePtr update_ptr);

  void CheckRoutineStatus(mojom::RoutineType routine_type);

  void OnRoutineStatusUpdated(
      mojom::RoutineType routine_type,
      chromeos::cros_healthd::mojom::RoutineUpdatePtr update_ptr);

  void HandlePowerRoutineStatusUpdate(
      mojom ::RoutineType routine_type,
      chromeos::cros_healthd::mojom::RoutineUpdatePtr update_ptr);

  bool IsRoutineRunning() const;

  void ScheduleCheckRoutineStatus(uint32_t duration_in_seconds,
                                  mojom::RoutineType routine_type);

  void ParsePowerRoutineResult(mojom::RoutineType routine_type,
                               mojom::StandardRoutineResult result,
                               mojo::ScopedHandle output_handle);

  void OnPowerRoutineResultFetched(mojom::RoutineType routine_type,
                                   const std::string& file_contents);

  void OnPowerRoutineJsonParsed(mojom::RoutineType routine_type,
                                data_decoder::DataDecoder::ValueOrError result);

  void OnStandardRoutineResult(mojom::RoutineType routine_type,
                               mojom::StandardRoutineResult result);

  void OnPowerRoutineResult(mojom::RoutineType routine_type,
                            mojom::StandardRoutineResult result,
                            double percent_change,
                            uint32_t seconds_elapsed);

  void SendRoutineResult(mojom::RoutineResultInfoPtr result_info);

  void BindCrosHealthdDiagnosticsServiceIfNeccessary();

  void OnDiagnosticsServiceDisconnected();

  void OnInflightRoutineRunnerDisconnected();

  void OnRoutineCancelAttempted(
      chromeos::cros_healthd::mojom::RoutineUpdatePtr update_ptr);

  bool IsLoggingEnabled() const;

  void AcquireWakeLock();

  void ReleaseWakeLock();

  RoutineLog* routine_log_ptr_ = nullptr;  // Not Owned.

  // Keeps track of the id created by CrosHealthd for the currently running
  // routine.
  int32_t inflight_routine_id_ = kInvalidRoutineId;

  // The currently inflight routine (if any). This is used to correctly
  // attribute cancellations.
  absl::optional<mojom::RoutineType> inflight_routine_type_;

  // Records the number of routines that a user attempts to run during one
  // session in the app. Emitted when the app is closed.
  uint16_t routine_count_ = 0;

  // Timestamp of when the memory routine was started. Undefined if the memory
  // routine is not running.
  base::Time memory_routine_start_timestamp_;

  mojo::Remote<mojom::RoutineRunner> inflight_routine_runner_;
  std::unique_ptr<base::OneShotTimer> inflight_routine_timer_;

  mojo::Remote<chromeos::cros_healthd::mojom::CrosHealthdDiagnosticsService>
      diagnostics_service_;

  mojo::Receiver<mojom::SystemRoutineController> receiver_{this};

  // `wake_lock_` is used to prevent the device from sleeping during the
  // memory test.
  mojo::Remote<device::mojom::WakeLock> wake_lock_;

  mojo::Remote<device::mojom::WakeLockProvider> wake_lock_provider_;

  // Keeps track of supported routines. Allows us to bypass querying CrosHealthd
  // on subsequent "GetSupportedRoutines" calls since the list never changes.
  std::vector<mojom::RoutineType> supported_routines_;

  base::WeakPtrFactory<SystemRoutineController> weak_factory_{this};
};

}  // namespace diagnostics
}  // namespace ash

#endif  // ASH_WEBUI_DIAGNOSTICS_UI_BACKEND_SYSTEM_ROUTINE_CONTROLLER_H_
