// Copyright 2014 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#ifndef REMOTING_HOST_FAKE_DESKTOP_ENVIRONMENT_H_
#define REMOTING_HOST_FAKE_DESKTOP_ENVIRONMENT_H_

#include <cstdint>
#include <memory>
#include <optional>
#include <string>
#include <utility>
#include <vector>

#include "base/functional/callback_forward.h"
#include "base/memory/raw_ptr.h"
#include "base/memory/scoped_refptr.h"
#include "base/memory/weak_ptr.h"
#include "base/task/single_thread_task_runner.h"
#include "remoting/host/action_executor.h"
#include "remoting/host/base/desktop_environment_options.h"
#include "remoting/host/base/screen_controls.h"
#include "remoting/host/base/screen_resolution.h"
#include "remoting/host/desktop_environment.h"
#include "remoting/host/fake_active_display_monitor.h"
#include "remoting/host/input_injector.h"
#include "remoting/proto/control.pb.h"
#include "remoting/protocol/clipboard_stub.h"
#include "remoting/protocol/desktop_capturer.h"
#include "remoting/protocol/fake_desktop_capturer.h"
#include "remoting/protocol/input_stub.h"
#include "third_party/webrtc/modules/desktop_capture/desktop_capture_types.h"

namespace remoting {
class IpcFifoBufferReader;

class FakeInputInjector : public InputInjector {
  class FifoBufferWriter;

 public:
  FakeInputInjector();

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

  ~FakeInputInjector() override;

  void Start(
      std::unique_ptr<protocol::ClipboardStub> client_clipboard) override;
  void InjectKeyEvent(const protocol::KeyEvent& event) override;
  void InjectTextEvent(const protocol::TextEvent& event) override;
  void InjectMouseEvent(const protocol::MouseEvent& event) override;
  void InjectTouchEvent(const protocol::TouchEvent& event) override;
  void InjectClipboardEvent(const protocol::ClipboardEvent& event) override;

  void set_key_events(std::vector<protocol::KeyEvent>* key_events) {
    key_events_ = key_events;
  }
  void set_text_events(std::vector<protocol::TextEvent>* text_events) {
    text_events_ = text_events;
  }
  void set_mouse_events(std::vector<protocol::MouseEvent>* mouse_events) {
    mouse_events_ = mouse_events;
  }
  void set_touch_events(std::vector<protocol::TouchEvent>* touch_events) {
    touch_events_ = touch_events;
  }
  void set_clipboard_events(
      std::vector<protocol::ClipboardEvent>* clipboard_events) {
    clipboard_events_ = clipboard_events;
  }

 private:
  friend class FakeDesktopEnvironment;

  raw_ptr<std::vector<protocol::KeyEvent>> key_events_ = nullptr;
  raw_ptr<std::vector<protocol::TextEvent>> text_events_ = nullptr;
  raw_ptr<std::vector<protocol::MouseEvent>> mouse_events_ = nullptr;
  raw_ptr<std::vector<protocol::TouchEvent>> touch_events_ = nullptr;
  raw_ptr<std::vector<protocol::ClipboardEvent>> clipboard_events_ = nullptr;

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

class FakeScreenControls : public ScreenControls {
 public:
  FakeScreenControls();
  ~FakeScreenControls() override;

  // ScreenControls implementation.
  void SetScreenResolution(const ScreenResolution& resolution,
                           std::optional<webrtc::ScreenId> screen_id) override;
  void SetVideoLayout(const protocol::VideoLayout& video_layout) override;

  const ScreenResolution& last_resolution() const { return last_resolution_; }
  bool set_resolution_called() const { return set_resolution_called_; }
  const protocol::VideoLayout& last_video_layout() const {
    return last_video_layout_;
  }
  bool set_video_layout_called() const { return set_video_layout_called_; }

  base::WeakPtr<FakeScreenControls> GetWeakPtr() {
    return weak_factory_.GetWeakPtr();
  }

  void reset() {
    last_resolution_ = ScreenResolution();
    set_resolution_called_ = false;
    last_video_layout_ = protocol::VideoLayout();
    set_video_layout_called_ = false;
  }

 private:
  ScreenResolution last_resolution_;
  bool set_resolution_called_ = false;
  protocol::VideoLayout last_video_layout_;
  bool set_video_layout_called_ = false;

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

class FakeDesktopEnvironment : public DesktopEnvironment {
 public:
  explicit FakeDesktopEnvironment(
      scoped_refptr<base::SingleThreadTaskRunner> capture_thread,
      const DesktopEnvironmentOptions& options);

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

  ~FakeDesktopEnvironment() override;

  void set_frame_generator(
      protocol::FakeDesktopCapturer::FrameGenerator frame_generator) {
    frame_generator_ = std::move(frame_generator);
  }

  const DesktopEnvironmentOptions& options() const;

  // DesktopEnvironment implementation.
  std::unique_ptr<ActionExecutor> CreateActionExecutor() override;
  std::unique_ptr<AudioCapturer> CreateAudioCapturer() override;
  std::unique_ptr<InputInjector> CreateInputInjector() override;
  std::unique_ptr<ScreenControls> CreateScreenControls() override;
  std::unique_ptr<DesktopCapturer> CreateVideoCapturer(
      webrtc::ScreenId id) override;
  DesktopDisplayInfoMonitor* GetDisplayInfoMonitor() override;
  std::unique_ptr<protocol::MouseCursorMonitor> CreateMouseCursorMonitor()
      override;
  std::unique_ptr<KeyboardLayoutMonitor> CreateKeyboardLayoutMonitor(
      base::RepeatingCallback<void(const protocol::KeyboardLayout&)> callback)
      override;
  std::unique_ptr<ActiveDisplayMonitor> CreateActiveDisplayMonitor(
      ActiveDisplayMonitor::Callback callback) override;
  std::unique_ptr<FileOperations> CreateFileOperations() override;
  std::unique_ptr<UrlForwarderConfigurator> CreateUrlForwarderConfigurator()
      override;
  std::string GetCapabilities() const override;
  void SetCapabilities(const std::string& capabilities) override;
  std::unique_ptr<RemoteWebAuthnStateChangeNotifier>
  CreateRemoteWebAuthnStateChangeNotifier() override;
  std::unique_ptr<AudioInjector> CreateAudioInjector(
      std::unique_ptr<IpcFifoBufferReader> reader) override;

  base::WeakPtr<FakeInputInjector> last_input_injector() {
    return last_input_injector_;
  }

  base::WeakPtr<FakeActiveDisplayMonitor> last_active_display_monitor() {
    return last_active_display_monitor_;
  }

  base::WeakPtr<FakeScreenControls> last_screen_controls() {
    return last_screen_controls_;
  }

 private:
  friend class FakeDesktopEnvironmentFactory;

  scoped_refptr<base::SingleThreadTaskRunner> capture_thread_;
  protocol::FakeDesktopCapturer::FrameGenerator frame_generator_;

  base::WeakPtr<FakeInputInjector> last_input_injector_;
  base::WeakPtr<FakeActiveDisplayMonitor> last_active_display_monitor_;
  base::WeakPtr<FakeScreenControls> last_screen_controls_;

  const DesktopEnvironmentOptions options_;

  std::string capabilities_;

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

class FakeDesktopEnvironmentFactory : public DesktopEnvironmentFactory {
 public:
  explicit FakeDesktopEnvironmentFactory(
      scoped_refptr<base::SingleThreadTaskRunner> capture_thread);

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

  ~FakeDesktopEnvironmentFactory() override;

  // Sets frame generator to be used for protocol::FakeDesktopCapturer created
  // by FakeDesktopEnvironment.
  void set_frame_generator(
      protocol::FakeDesktopCapturer::FrameGenerator frame_generator) {
    frame_generator_ = std::move(frame_generator);
  }

  // Sets the capabilities that the FakeDesktopEnvironment will claim to
  // support. Useful for testing functionality that is triggered after
  // negotiating a capability with a client.
  void set_capabilities(const std::string& capabilities) {
    capabilities_ = capabilities;
  }

  // DesktopEnvironmentFactory implementation.
  void Create(base::WeakPtr<ClientSessionControl> client_session_control,
              base::WeakPtr<ClientSessionEvents> client_session_events,
              const DesktopEnvironmentOptions& options,
              CreateCallback callback) override;
  bool SupportsAudioCapture() const override;

  base::WeakPtr<FakeDesktopEnvironment> last_desktop_environment() {
    return last_desktop_environment_;
  }

 private:
  scoped_refptr<base::SingleThreadTaskRunner> capture_thread_;
  protocol::FakeDesktopCapturer::FrameGenerator frame_generator_;
  std::string capabilities_;

  base::WeakPtr<FakeDesktopEnvironment> last_desktop_environment_;
};

}  // namespace remoting

#endif  // REMOTING_HOST_FAKE_DESKTOP_ENVIRONMENT_H_
