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

#include "components/viz/service/frame_sinks/external_begin_frame_source_mojo_mac.h"

#include <utility>
#include <vector>

#include "base/metrics/histogram_macros.h"
#include "components/viz/common/frame_sinks/begin_frame_args.h"
#include "ui/display/mac/vsync_provider_mac.h"

namespace viz {

ExternalBeginFrameSourceMojoMac::ExternalBeginFrameSourceMojoMac(
    mojo::PendingReceiver<mojom::ExternalBeginFrameController>
        controller_receiver,
    mojo::PendingRemote<mojom::ExternalBeginFrameControllerClient>
        controller_remote_client,
    base::RepeatingCallback<void(int64_t)> update_vsync_displays_cb)
    : receiver_(this),
      remote_client_(std::move(controller_remote_client)),
      update_vsync_displays_cb_(std::move(update_vsync_displays_cb)) {
  CHECK(remote_client_);

  receiver_.Bind(std::move(controller_receiver));
}

ExternalBeginFrameSourceMojoMac::~ExternalBeginFrameSourceMojoMac() {
  remote_client_->SetNeedsBeginFrame(false);
}

// mojom::ExternalBeginFrameController implementation.
void ExternalBeginFrameSourceMojoMac::IssueExternalVSync(
    const CADisplayLinkParams& params) {
  base::TimeDelta ipc_duration =
      base::TimeTicks::Now() - params.ipc_begin_timestamp;
  if (base::ShouldRecordSubsampledMetric(0.001)) {
    UMA_HISTOGRAM_CUSTOM_MICROSECONDS_TIMES(
        "Viz.BeginFrameSource.IPC.LatencyUs2", ipc_duration,
        base::Microseconds(10), base::Minutes(1), 50);
  }

  ui::VSyncParamsMac ui_params(true, params.timestamp, params.interval, true,
                               params.target_timestamp, params.interval);
  ui::VSyncProviderMac::GetInstance()->OnVSync(ui_params, params.display_id);
}

void ExternalBeginFrameSourceMojoMac::SetSupportedDisplayLinkId(
    int64_t display_id,
    bool is_browser_vsync_supported) {
  if (ui::VSyncProviderMac::GetInstance()->IsDisplayLinkInBrowserValid(
          display_id) == is_browser_vsync_supported) {
    return;
  }

  // Connect to VSyncProviderMac.
  // Set the NeedsBeginFrameWithId callback right before calling
  // update_vsync_displays_cb_ to ensure we only record the ExternalDisplayLink
  // histograms after it's connected. Move this back to Ctor if those histograms
  // are no longer needed.
  if (!cb_to_vsync_provider_set_) {
    cb_to_vsync_provider_set_ = true;
    ui::NeedsBeginFrameCB callback = base::BindRepeating(
        &ExternalBeginFrameSourceMojoMac::NeedsBeginFrameWithId,
        weak_factory_.GetWeakPtr());
    ui::VSyncProviderMac::GetInstance()->SetCallbackForRemoteNeedsBeginFrame(
        std::move(callback));
  }

  // Update VSyncProvider on whether the CADisplayLink created in the Browser
  // process is still valid or not. This allows ExternalBeginFrameSourceMac to
  // recreate DisplayLinkMac accordingly.
  ui::VSyncProviderMac::GetInstance()->SetSupportedDisplayLinkId(
      display_id, is_browser_vsync_supported);

  // When ExternalBeginFrameSourceMac is using DisplayLink in Browser, destroy
  // and recreate a DisplayLinkMac in every ExternalBeginFrameSourceMac if
  // needed.
  update_vsync_displays_cb_.Run(display_id);
}

void ExternalBeginFrameSourceMojoMac::IssueExternalBeginFrame(
    const BeginFrameArgs& args,
    IssueExternalBeginFrameCallback callback) {
  // IssueExternalBeginFrame on Mac is for headless only.
  NOTREACHED();
}

void ExternalBeginFrameSourceMojoMac::NeedsBeginFrameWithId(
    int64_t display_id,
    bool needs_begin_frames) {
  remote_client_->NeedsBeginFrameWithId(display_id, needs_begin_frames);
}

}  // namespace viz
