// Copyright 2018 The Chromium Embedded Framework Authors. All rights
// reserved. Use of this source code is governed by a BSD-style license that
// can be found in the LICENSE file.

#include "tests/cefclient/browser/osr_render_handler_win.h"

#include "include/base/cef_bind.h"
#include "include/wrapper/cef_closure_task.h"
#include "include/wrapper/cef_helpers.h"
#include "tests/shared/browser/util_win.h"

namespace client {

OsrRenderHandlerWin::OsrRenderHandlerWin(const OsrRendererSettings& settings,
                                         HWND hwnd)
    : settings_(settings),
      hwnd_(hwnd),
      begin_frame_pending_(false),
      weak_factory_(this) {
  CEF_REQUIRE_UI_THREAD();
  DCHECK(hwnd_);
}

OsrRenderHandlerWin::~OsrRenderHandlerWin() {
  CEF_REQUIRE_UI_THREAD();
}

void OsrRenderHandlerWin::SetBrowser(CefRefPtr<CefBrowser> browser) {
  CEF_REQUIRE_UI_THREAD();
  browser_ = browser;
  if (browser_ && settings_.external_begin_frame_enabled) {
    // Start the BeginFrame timer.
    Invalidate();
  }
}

void OsrRenderHandlerWin::Invalidate() {
  CEF_REQUIRE_UI_THREAD();
  if (begin_frame_pending_) {
    // The timer is already running.
    return;
  }

  // Trigger the BeginFrame timer.
  CHECK_GT(settings_.begin_frame_rate, 0);
  const float delay_us = (1.0 / double(settings_.begin_frame_rate)) * 1000000.0;
  TriggerBeginFrame(0, delay_us);
}

void OsrRenderHandlerWin::TriggerBeginFrame(uint64_t last_time_us,
                                            float delay_us) {
  if (begin_frame_pending_ && !settings_.external_begin_frame_enabled) {
    // Render immediately and then wait for the next call to Invalidate() or
    // On[Accelerated]Paint().
    begin_frame_pending_ = false;
    Render();
    return;
  }

  const auto now = GetTimeNow();
  float offset = now - last_time_us;
  if (offset > delay_us) {
    offset = delay_us;
  }

  if (!begin_frame_pending_) {
    begin_frame_pending_ = true;
  }

  // Trigger again after the necessary delay to maintain the desired frame rate.
  CefPostDelayedTask(TID_UI,
                     base::Bind(&OsrRenderHandlerWin::TriggerBeginFrame,
                                weak_factory_.GetWeakPtr(), now, delay_us),
                     int64(offset / 1000.0));

  if (settings_.external_begin_frame_enabled && browser_) {
    // We're running the BeginFrame timer. Trigger rendering via
    // On[Accelerated]Paint().
    browser_->GetHost()->SendExternalBeginFrame();
  }
}

}  // namespace client
