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

#ifndef COMPONENTS_VIZ_SERVICE_DISPLAY_DISPLAY_DAMAGE_TRACKER_H_
#define COMPONENTS_VIZ_SERVICE_DISPLAY_DISPLAY_DAMAGE_TRACKER_H_

#include <memory>
#include <vector>

#include "base/containers/flat_map.h"
#include "base/memory/raw_ptr.h"
#include "base/time/time.h"
#include "components/viz/common/frame_sinks/begin_frame_args.h"
#include "components/viz/common/surfaces/surface_id.h"
#include "components/viz/service/surfaces/surface_observer.h"
#include "components/viz/service/viz_service_export.h"
#include "ui/latency/latency_info.h"

namespace viz {

class SurfaceAggregator;
class SurfaceManager;

// DisplayDamageTracker is used to track Surfaces damage that belong to current
// Display. It tracks pending damage when clients received BeginFrames but
// didn't replied yet and it tracks whether damage to those surfaces contribute
// to Display damage. Used by DisplayScheduler to determine frame deadlines.
class VIZ_SERVICE_EXPORT DisplayDamageTracker : public SurfaceObserver {
 public:
  class VIZ_SERVICE_EXPORT Delegate {
   public:
    virtual ~Delegate() = default;
    virtual void OnDisplayDamaged(SurfaceId surface_id,
                                  BeginFrameId frame_id) = 0;
    virtual void OnRootFrameMissing(bool missing) = 0;
    virtual void OnPendingSurfacesChanged() = 0;
  };

  DisplayDamageTracker(SurfaceManager* surface_manager,
                       SurfaceAggregator* aggregator);
  ~DisplayDamageTracker() override;

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

  // Sets the source_id associated with this displays begin frame source.
  // DisplayDamageTracker ignores expected damage from frame sinks that received
  // a begin frame from a different begin frame source after this is set.
  void SetDisplayBeginFrameSourceId(uint64_t begin_frame_source_id);

  void SetDelegate(Delegate* delegate);

  // Notification that there was a resize and we should expect root surface
  // damage.
  void DisplayResized();

  // Notification that the root surface changed.
  void SetNewRootSurface(const SurfaceId& root_surface_id);

  // Mark root surface as damaged.
  void SetRootSurfaceDamaged();

  // Send Surface Acks to damaged surfaces after draw.
  void RunDrawCallbacks();

  // This returns whether there are pending surfaces. SurfaceIs pending if the
  // corresponding CompositorFrameSink has received BeginFrame but hasn't
  // replied with Ack yet.
  bool HasPendingSurfaces(const BeginFrameArgs& begin_frame_args);

  // Returns true if any of the damage received was due to an ongoing scroll or
  // touch interaction.
  bool HasDamageDueToInteraction();

  // Returns the earliest input generation time among all damaged surfaces that
  // contributed to the current frame, if they are handling interaction.
  std::optional<base::TimeTicks>
  GetEarliestInputGenerationTimeOfDamagedSurfaces() const;

  // Called after a frame finishes (may or may not result in a draw).
  void DidFinishFrame();

  // Returns true if damage to this Surface could affect the display.
  bool CheckForDisplayDamage(const SurfaceId& surface_id);

  bool root_frame_missing() const { return root_frame_missing_; }
  bool IsRootSurfaceValid() const;

  bool expecting_root_surface_damage_because_of_resize() const {
    return expecting_root_surface_damage_because_of_resize_;
  }

  void reset_expecting_root_surface_damage_because_of_resize() {
    expecting_root_surface_damage_because_of_resize_ = false;
  }

  // SurfaceObserver implementation.
  void OnSurfaceMarkedForDestruction(const SurfaceId& surface_id) override;
  bool OnSurfaceDamaged(
      const SurfaceId& surface_id,
      const BeginFrameAck& ack,
      HandleInteraction handle_interaction,
      const std::vector<ui::LatencyInfo>& latency_info) override;
  void OnSurfaceDamageExpected(const SurfaceId& surface_id,
                               const BeginFrameArgs& args) override;

 protected:
  struct SurfaceBeginFrameState {
    BeginFrameArgs last_args;
    BeginFrameAck last_ack;
  };

  virtual bool SurfaceHasUnackedFrame(const SurfaceId& surface_id) const;
  virtual void UpdateRootFrameMissing();
  void SetRootFrameMissing(bool missing);

  // Checks if the begin frame `source_id` is for this display. This will return
  // true if:
  // 1. `source_id` matches the display source id.
  // 2. Display source id was never set.
  // 3. `source_id` is a manual source id since that could be relevant for any
  //    display.
  bool CheckBeginFrameSourceId(uint64_t source_id);

  // Indicates that there was damage to one of the surfaces.
  void ProcessSurfaceDamage(
      const SurfaceId& surface_id,
      const BeginFrameAck& ack,
      bool display_damaged,
      HandleInteraction handle_interaction,
      const std::vector<ui::LatencyInfo>& latency_info = {});

  // Used to send corresponding notifications to observers.
  void NotifyDisplayDamaged(SurfaceId surface_id, BeginFrameId frame_id);
  void NotifyRootFrameMissing(bool missing);
  void NotifyPendingSurfacesChanged();

  raw_ptr<Delegate> delegate_ = nullptr;
  const raw_ptr<SurfaceManager> surface_manager_;
  const raw_ptr<SurfaceAggregator> aggregator_;

  std::optional<uint64_t> begin_frame_source_id_;
  bool root_frame_missing_ = true;

  bool expecting_root_surface_damage_because_of_resize_ = false;

  bool has_surface_damage_due_to_interaction_ = false;
  std::optional<base::TimeTicks> earliest_input_timestamp_;

  base::flat_map<SurfaceId, SurfaceBeginFrameState> surface_states_;
  std::vector<SurfaceId> surfaces_to_ack_on_next_draw_;

  SurfaceId root_surface_id_;
};

}  // namespace viz

#endif  // COMPONENTS_VIZ_SERVICE_DISPLAY_DISPLAY_DAMAGE_TRACKER_H_
