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

#include "chrome/browser/vr/ui_scene_creator.h"

#include <memory>
#include <numbers>
#include <string>
#include <tuple>
#include <utility>

#include "base/functional/bind.h"
#include "base/functional/callback.h"
#include "base/functional/callback_helpers.h"
#include "base/i18n/case_conversion.h"
#include "base/logging.h"
#include "base/strings/utf_string_conversions.h"
#include "build/build_config.h"
#include "chrome/app/vector_icons/vector_icons.h"
#include "chrome/browser/vr/databinding/binding.h"
#include "chrome/browser/vr/elements/draw_phase.h"
#include "chrome/browser/vr/elements/environment/grid.h"
#include "chrome/browser/vr/elements/full_screen_rect.h"
#include "chrome/browser/vr/elements/indicator_spec.h"
#include "chrome/browser/vr/elements/linear_layout.h"
#include "chrome/browser/vr/elements/rect.h"
#include "chrome/browser/vr/elements/scaled_depth_adjuster.h"
#include "chrome/browser/vr/elements/text.h"
#include "chrome/browser/vr/elements/transient_element.h"
#include "chrome/browser/vr/elements/ui_element.h"
#include "chrome/browser/vr/elements/ui_element_name.h"
#include "chrome/browser/vr/elements/vector_icon.h"
#include "chrome/browser/vr/elements/viewport_aware_root.h"
#include "chrome/browser/vr/model/model.h"
#include "chrome/browser/vr/target_property.h"
#include "chrome/browser/vr/ui.h"
#include "chrome/browser/vr/ui_scene.h"
#include "chrome/browser/vr/ui_scene_constants.h"
#include "chrome/grit/generated_resources.h"
#include "components/strings/grit/components_strings.h"
#include "device/base/features.h"
#include "ui/base/l10n/l10n_util.h"
#include "ui/base/ui_base_features.h"
#include "ui/gfx/animation/keyframe/animation_curve.h"
#include "ui/gfx/animation/keyframe/keyframed_animation_curve.h"
#include "ui/gfx/geometry/transform_util.h"
#include "ui/gfx/paint_vector_icon.h"

#if BUILDFLAG(IS_WIN)
#include "chrome/browser/vr/elements/spinner.h"
#endif

namespace vr {

namespace {

template <typename V, typename C, typename S>
void BindColor(Model* model,
               V* view,
               C color,
               const std::string& color_string,
               S setter,
               const std::string& setter_string) {
  view->AddBinding(std::make_unique<Binding<SkColor>>(
      base::BindRepeating([](Model* m, C c) { return (m->color_scheme()).*c; },
                          base::Unretained(model), color),
#ifndef NDEBUG
      color_string,
#endif
      base::BindRepeating(
          [](V* v, S s, const SkColor& value) { (v->*s)(value); },
          base::Unretained(view), setter)
#ifndef NDEBUG
          ,
      setter_string
#endif
      ));
}

#define VR_BIND_COLOR(m, v, c, s) BindColor(m, v, c, #c, s, #s)

#define VR_BIND_VISIBILITY(v, c) \
  v->AddBinding(                 \
      VR_BIND_FUNC(bool, Model, model_, c, UiElement, v.get(), SetVisible));

template <typename T, typename... Args>
std::unique_ptr<T> Create(UiElementName name, DrawPhase phase, Args&&... args) {
  auto element = std::make_unique<T>(std::forward<Args>(args)...);
  element->SetName(name);
  element->SetDrawPhase(phase);
  return element;
}

std::unique_ptr<TransientElement> CreateTransientParent(UiElementName name,
                                                        int timeout_seconds,
                                                        bool animate_opacity) {
  auto element =
      std::make_unique<SimpleTransientElement>(base::Seconds(timeout_seconds));
  element->SetName(name);
  element->SetVisible(false);
  if (animate_opacity)
    element->SetTransitionedProperties({OPACITY});
  return element;
}

#if !BUILDFLAG(IS_ANDROID)
std::unique_ptr<UiElement> CreateSpacer(float width, float height) {
  auto spacer = Create<UiElement>(kNone, kPhaseNone);
  spacer->SetType(kTypeSpacer);
  spacer->SetSize(width, height);
  return spacer;
}
#endif

void BindIndicatorText(Model* model, Text* text, const IndicatorSpec& spec) {
  text->AddBinding(std::make_unique<Binding<std::pair<bool, bool>>>(
      VR_BIND_LAMBDA(
          [](Model* model, bool CapturingStateModel::*signal) {
            return std::make_pair(model->active_capturing.*signal,
                                  model->background_capturing.*signal);
          },
          base::Unretained(model), spec.signal),
      VR_BIND_LAMBDA(
          [](Text* view, int resource, int background_resource,
             int potential_resource, const std::pair<bool, bool>& value) {
            if (value.first)
              view->SetText(l10n_util::GetStringUTF16(resource));
            else if (value.second)
              view->SetText(l10n_util::GetStringUTF16(background_resource));
            else
              view->SetText(l10n_util::GetStringUTF16(potential_resource));
          },
          base::Unretained(text), spec.resource_string,
          spec.background_resource_string, spec.potential_resource_string)));
}

std::unique_ptr<UiElement> CreateWebVrIndicator(Model* model,
                                                IndicatorSpec spec,
                                                DrawPhase phase) {
  auto container = Create<Rect>(spec.webvr_name, phase);
  VR_BIND_COLOR(model, container.get(),
                &ColorScheme::webvr_permission_background, &Rect::SetColor);
  container->SetCornerRadius(kWebVrPermissionCornerRadius);
  container->set_bounds_contain_children(true);
  container->SetVisible(false);
  container->set_padding(
      kWebVrPermissionLeftPadding, kWebVrPermissionTopPadding,
      kWebVrPermissionRightPadding, kWebVrPermissionBottomPadding);

  auto layout = Create<LinearLayout>(kNone, kPhaseNone, LinearLayout::kRight);
  layout->set_margin(kWebVrPermissionMargin);

  auto icon_element = Create<VectorIcon>(kNone, phase, 128);
  VR_BIND_COLOR(model, icon_element.get(),
                &ColorScheme::webvr_permission_foreground,
                &VectorIcon::SetColor);
  icon_element->set_y_anchoring(TOP);
  icon_element->SetSize(kWebVrPermissionIconSize, kWebVrPermissionIconSize);
  icon_element->SetIcon(*spec.icon);

  std::unique_ptr<UiElement> description_element;
  auto text_element = Create<Text>(kNone, phase, kWebVrPermissionFontHeight);
  text_element->SetColor(SK_ColorWHITE);
  text_element->SetFieldWidth(kWebVrPermissionTextWidth);
  if (spec.signal) {
    BindIndicatorText(model, text_element.get(), spec);
  } else {
    text_element->SetText(l10n_util::GetStringUTF16(spec.resource_string));
  }
  VR_BIND_COLOR(model, text_element.get(),
                &ColorScheme::webvr_permission_foreground, &Text::SetColor);
  description_element = std::move(text_element);

  layout->AddChild(std::move(icon_element));
  layout->AddChild(std::move(description_element));
  container->AddChild(std::move(layout));

  return container;
}

std::unique_ptr<Grid> CreateGrid(Model* model, UiElementName name) {
  auto grid = Create<Grid>(name, kPhaseBackground);
  grid->set_gridline_count(kFloorGridlineCount);
  return grid;
}

void ApplyFloorTransform(Rect* floor) {
  floor->SetSize(1.0f, 1.0f);
  floor->SetScale(kSceneSize, kSceneSize, kSceneSize);
  floor->SetTranslate(0.0, kFloorHeight, 0.0);
  floor->SetRotate(1, 0, 0, -std::numbers::pi_v<float> / 2);
}

void SetVisibleInLayout(UiElement* e, bool v) {
  e->SetVisible(v);
  e->set_requires_layout(v);
}

#if BUILDFLAG(IS_WIN)
void BindIndicatorTranscienceForWin(
    TransientElement* e,
    Model* model,
    UiScene* scene,
    const std::optional<
        std::tuple<bool, CapturingStateModel, CapturingStateModel>>& last_value,
    const std::tuple<bool, CapturingStateModel, CapturingStateModel>& value) {
  const bool in_web_vr_presentation = model->web_vr.IsImmersiveWebXrVisible() &&
                                      model->web_vr.has_received_permissions;

  const CapturingStateModel active_capture = std::get<1>(value);
  const CapturingStateModel potential_capture = std::get<2>(value);

  if (!in_web_vr_presentation) {
    e->SetVisibleImmediately(false);
    return;
  }

  e->SetVisible(true);
  e->RefreshVisible();

  for (const auto& spec : GetIndicatorSpecs()) {
    SetVisibleInLayout(
        scene->GetUiElementByName(spec.webvr_name),
        active_capture.*spec.signal || potential_capture.*spec.signal);
  }

  e->RemoveKeyframeModels(TRANSFORM);

  e->SetTranslate(0, kWebVrPermissionOffsetStart, 0);

  // Build up a keyframe model for the initial transition.
  std::unique_ptr<gfx::KeyframedTransformAnimationCurve> curve(
      gfx::KeyframedTransformAnimationCurve::Create());

  gfx::TransformOperations value_1;
  value_1.AppendTranslate(0, kWebVrPermissionOffsetStart, 0);
  curve->AddKeyframe(gfx::TransformKeyframe::Create(
      base::TimeDelta(), value_1,
      gfx::CubicBezierTimingFunction::CreatePreset(
          gfx::CubicBezierTimingFunction::EaseType::EASE)));

  gfx::TransformOperations value_2;
  value_2.AppendTranslate(0, kWebVrPermissionOffsetOvershoot, 0);
  curve->AddKeyframe(gfx::TransformKeyframe::Create(
      base::Milliseconds(kWebVrPermissionOffsetMs), value_2,
      gfx::CubicBezierTimingFunction::CreatePreset(
          gfx::CubicBezierTimingFunction::EaseType::EASE)));

  gfx::TransformOperations value_3;
  value_3.AppendTranslate(0, kWebVrPermissionOffsetFinal, 0);
  curve->AddKeyframe(gfx::TransformKeyframe::Create(
      base::Milliseconds(kWebVrPermissionAnimationDurationMs), value_3,
      gfx::CubicBezierTimingFunction::CreatePreset(
          gfx::CubicBezierTimingFunction::EaseType::EASE)));

  curve->set_target(e);

  e->AddKeyframeModel(gfx::KeyframeModel::Create(
      std::move(curve), gfx::KeyframeEffect::GetNextKeyframeModelId(),
      TRANSFORM));
}

#else

void BindIndicatorTranscience(TransientElement* e,
                              Model* model,
                              UiScene* scene,
                              const bool& in_web_vr_presentation) {
  if (!in_web_vr_presentation) {
    e->SetVisibleImmediately(false);
    return;
  }

  e->SetVisible(true);
  e->RefreshVisible();

  auto specs = GetIndicatorSpecs();
  for (const auto& spec : specs) {
    SetVisibleInLayout(scene->GetUiElementByName(spec.webvr_name),
                       model->active_capturing.*spec.signal ||
                           model->potential_capturing.*spec.signal ||
                           model->background_capturing.*spec.signal);
  }

  e->RemoveKeyframeModels(TRANSFORM);
  e->SetTranslate(0, kWebVrPermissionOffsetStart, 0);

  // Build up a keyframe model for the initial transition.
  std::unique_ptr<gfx::KeyframedTransformAnimationCurve> curve(
      gfx::KeyframedTransformAnimationCurve::Create());

  gfx::TransformOperations value_1;
  value_1.AppendTranslate(0, kWebVrPermissionOffsetStart, 0);
  curve->AddKeyframe(gfx::TransformKeyframe::Create(
      base::TimeDelta(), value_1,
      gfx::CubicBezierTimingFunction::CreatePreset(
          gfx::CubicBezierTimingFunction::EaseType::EASE)));

  gfx::TransformOperations value_2;
  value_2.AppendTranslate(0, kWebVrPermissionOffsetOvershoot, 0);
  curve->AddKeyframe(gfx::TransformKeyframe::Create(
      base::Milliseconds(kWebVrPermissionOffsetMs), value_2,
      gfx::CubicBezierTimingFunction::CreatePreset(
          gfx::CubicBezierTimingFunction::EaseType::EASE)));

  gfx::TransformOperations value_3;
  value_3.AppendTranslate(0, kWebVrPermissionOffsetFinal, 0);
  curve->AddKeyframe(gfx::TransformKeyframe::Create(
      base::Milliseconds(kWebVrPermissionAnimationDurationMs), value_3,
      gfx::CubicBezierTimingFunction::CreatePreset(
          gfx::CubicBezierTimingFunction::EaseType::EASE)));

  curve->set_target(e);

  e->AddKeyframeModel(gfx::KeyframeModel::Create(
      std::move(curve), gfx::KeyframeEffect::GetNextKeyframeModelId(),
      TRANSFORM));
}

#endif

int GetIndicatorsTimeout() {
  // Some runtimes on Windows have quite lengthy animations that may cause
  // indicators to not be visible at our normal timeout length.
#if BUILDFLAG(IS_WIN)
  return kWindowsInitialIndicatorsTimeoutSeconds;
#else
  return kToastTimeoutSeconds;
#endif
}

}  // namespace

UiSceneCreator::UiSceneCreator(UiScene* scene, Ui* ui, Model* model)
    : scene_(scene), ui_(ui), model_(model) {}

UiSceneCreator::~UiSceneCreator() = default;

void UiSceneCreator::CreateScene() {
  CreateWebVrRoot();
  CreateViewportAwareRoot();
  CreateWebVrSubtree();
}

void UiSceneCreator::CreateWebVrRoot() {
  auto element = std::make_unique<UiElement>();
  element->SetName(kWebVrRoot);
  element->SetVisible(true);
  element->SetTranslate(0, 0, 0);
  scene_->AddUiElement(kRoot, std::move(element));
}

void UiSceneCreator::CreateExternalPromptNotifcationOverlay() {
#if !BUILDFLAG(IS_ANDROID)
  auto phase = kPhaseForeground;
  auto icon = Create<VectorIcon>(kNone, phase, 100);
  icon->SetType(kTypePromptIcon);
  icon->SetSize(kPromptIconSize, kPromptIconSize);
  icon->set_y_anchoring(TOP);
  VR_BIND_COLOR(model_, icon.get(), &ColorScheme::modal_prompt_icon_foreground,
                &VectorIcon::SetColor);
  VectorIcon* vector_icon = icon.get();

  auto text1 = Create<Text>(kNone, phase, kPromptFontSize);
  text1->SetType(kTypePromptText);
  text1->SetFieldWidth(kPromptTextWidth);
  VR_BIND_COLOR(model_, text1.get(), &ColorScheme::modal_prompt_foreground,
                &Text::SetColor);
  Text* line1_text = text1.get();

  auto text2 = Create<Text>(kNone, phase, kPromptFontSize);
  text2->SetType(kTypePromptText);
  text2->SetFieldWidth(kPromptTextWidth);
  text2->SetText(l10n_util::GetStringUTF16(IDS_DESKTOP_PROMPT_DOFF_HEADSET));
  VR_BIND_COLOR(model_, text2.get(), &ColorScheme::modal_prompt_foreground,
                &Text::SetColor);

  // This spacer's padding ensures that the top line of text is aligned with the
  // icon even in the multi-line case.
  auto text_spacer1 = CreateSpacer(0, 0);
  text_spacer1->set_bounds_contain_children(true);
  text_spacer1->set_padding(0, (kPromptIconSize - kPromptFontSize) / 2, 0, 0);
  text_spacer1->AddChild(std::move(text1));

  // The second spacer gives space between the two strings.
  auto text_spacer2 = CreateSpacer(0, 0);
  text_spacer2->set_bounds_contain_children(true);
  text_spacer2->set_padding(0, kPromptFontSize * 2, 0, 0);
  text_spacer2->AddChild(std::move(text2));

  // Two lines of text:
  auto text_layout =
      Create<LinearLayout>(kNone, kPhaseNone, LinearLayout::kDown);
  text_layout->AddChild(std::move(text_spacer1));
  text_layout->AddChild(std::move(text_spacer2));

  // Contents of the message box.
  auto message_layout =
      Create<LinearLayout>(kNone, kPhaseNone, LinearLayout::kRight);
  message_layout->set_margin(kPromptIconTextGap);
  message_layout->AddChild(std::move(icon));
  message_layout->AddChild(std::move(text_layout));

  auto prompt_window = Create<Rect>(kNone, phase);
  prompt_window->SetType(kTypePromptBackground);
  prompt_window->set_bounds_contain_children(true);
  prompt_window->set_padding(kPromptPadding, kPromptPadding);
  prompt_window->SetTranslate(0, 0, kPromptShadowOffsetDMM);
  prompt_window->SetCornerRadius(kPromptCornerRadius);
  prompt_window->AddChild(std::move(message_layout));
  VR_BIND_COLOR(model_, prompt_window.get(),
                &ColorScheme::modal_prompt_background, &Rect::SetColor);

  auto scaler = Create<ScaledDepthAdjuster>(kNone, kPhaseNone, kPromptDistance);
  scaler->SetName(kWebXrExternalPromptNotification);
  scaler->SetType(kTypeScaledDepthAdjuster);
  scaler->AddChild(std::move(prompt_window));
  scaler->set_contributes_to_parent_bounds(false);
  VR_BIND_VISIBILITY(scaler, (model->web_vr.external_prompt_notification !=
                              ExternalPromptNotificationType::kPromptNone));

  scaler->AddBinding(std::make_unique<Binding<ExternalPromptNotificationType>>(
      VR_BIND_LAMBDA(
          [](Model* m) { return m->web_vr.external_prompt_notification; },
          base::Unretained(model_)),
      VR_BIND_LAMBDA(
          [](Text* text_element, VectorIcon* icon_element,
             const ExternalPromptNotificationType& prompt) {
            if (prompt == ExternalPromptNotificationType::kPromptNone)
              return;

            int message_id = 0;
            const gfx::VectorIcon* icon = nullptr;
            switch (prompt) {
              case ExternalPromptNotificationType::kPromptGenericPermission:
                message_id = IDS_VR_DESKTOP_GENERIC_PERMISSION_PROMPT;
                icon = &(features::IsRoundedIconsEnabled()
                             ? kOpenInBrowserIcon
                             : kOpenInBrowserOldIcon);
                break;
              case ExternalPromptNotificationType::kPromptNone:
                NOTREACHED();
            }

            text_element->SetText(l10n_util::GetStringUTF16(message_id));
            icon_element->SetIcon(icon);
          },
          base::Unretained(line1_text), base::Unretained(vector_icon))));

  scene_->AddUiElement(kWebVrViewportAwareRoot, std::move(scaler));
#endif  // !BUILDFLAG(IS_ANDROID)
}

void UiSceneCreator::CreateWebVrSubtree() {
  CreateWebVrOverlayElements();
  CreateWebVrTimeoutScreen();
  CreateExternalPromptNotifcationOverlay();

  // Note, this cannot be a descendant of the viewport aware root, otherwise it
  // will fade out when the viewport aware elements reposition.
  auto bg = std::make_unique<FullScreenRect>();
  bg->SetName(kWebVrBackground);
  bg->SetDrawPhase(kPhaseBackground);
  bg->SetVisible(false);
  bg->SetColor(model_->color_scheme().web_vr_background);
  bg->SetTransitionedProperties({OPACITY});
  VR_BIND_VISIBILITY(bg, (!model->web_vr.IsImmersiveWebXrVisible() ||
                          model->web_vr.external_prompt_notification !=
                              ExternalPromptNotificationType::kPromptNone));
  auto grid = CreateGrid(model_, kNone);
  grid->set_owner_name_for_test(kWebVrFloor);
  VR_BIND_COLOR(model_, grid.get(), &ColorScheme::web_vr_floor_grid,
                &Grid::SetGridColor);
  auto grid_bg = Create<Rect>(kWebVrFloor, kPhaseBackground);
  ApplyFloorTransform(grid_bg.get());
  VR_BIND_COLOR(model_, grid_bg.get(), &ColorScheme::web_vr_floor_center,
                &Rect::SetCenterColor);
  VR_BIND_COLOR(model_, grid_bg.get(), &ColorScheme::web_vr_floor_edge,
                &Rect::SetEdgeColor);
  grid_bg->AddBinding(std::make_unique<Binding<bool>>(
      VR_BIND_LAMBDA(
          [](UiElement* timeout_screen) {
            return timeout_screen->GetTargetOpacity() != 0.f;
          },
          base::Unretained(scene_->GetUiElementByName(kWebVrTimeoutRoot))),
      VR_BIND_LAMBDA(
          [](UiElement* e, const bool& value) { e->SetVisible(value); },
          base::Unretained(grid_bg.get()))));
  grid_bg->AddChild(std::move(grid));
  bg->AddChild(std::move(grid_bg));
  scene_->AddUiElement(kWebVrRoot, std::move(bg));
}

void UiSceneCreator::CreateWebVrTimeoutScreen() {
  auto scaler = std::make_unique<ScaledDepthAdjuster>(kTimeoutScreenDisatance);
  scaler->SetName(kWebVrTimeoutRoot);
  scaler->AddBinding(std::make_unique<Binding<bool>>(
      VR_BIND_LAMBDA(
          [](Model* model) {
            return (model->web_vr.state == kWebVrTimeoutImminent ||
                    model->web_vr.state == kWebVrTimedOut);
          },
          base::Unretained(model_)),
      VR_BIND_LAMBDA(
          [](UiElement* e, const bool& value) { e->SetVisible(value); },
          base::Unretained(scaler.get()))));

  // TODO(https://crbug.com/327467653): Investigate spinner code.
#if BUILDFLAG(IS_WIN)
  auto spinner = std::make_unique<Spinner>(512);
  spinner->SetName(kWebVrTimeoutSpinner);
  spinner->SetDrawPhase(kPhaseForeground);
  spinner->SetTransitionedProperties({OPACITY});
  spinner->SetVisible(false);
  spinner->SetSize(kTimeoutSpinnerSizeDMM, kTimeoutSpinnerSizeDMM);
  spinner->SetTranslate(0, kTimeoutSpinnerVerticalOffsetDMM, 0);
  spinner->SetColor(model_->color_scheme().web_vr_timeout_spinner);
  VR_BIND_VISIBILITY(spinner, model->web_vr.state == kWebVrTimeoutImminent);
#endif

  auto timeout_message = Create<Rect>(kWebVrTimeoutMessage, kPhaseForeground);
  timeout_message->SetVisible(false);
  timeout_message->set_bounds_contain_children(true);
  timeout_message->SetCornerRadius(kTimeoutMessageCornerRadiusDMM);
  timeout_message->SetTransitionedProperties({OPACITY, TRANSFORM});
  timeout_message->set_padding(kTimeoutMessageHorizontalPaddingDMM,
                               kTimeoutMessageVerticalPaddingDMM);
  VR_BIND_VISIBILITY(timeout_message, model->web_vr.state == kWebVrTimedOut);
  timeout_message->SetColor(
      model_->color_scheme().web_vr_timeout_message_background);

  auto timeout_layout = Create<LinearLayout>(kWebVrTimeoutMessageLayout,
                                             kPhaseNone, LinearLayout::kRight);
  timeout_layout->set_margin(kTimeoutMessageLayoutGapDMM);

  auto timeout_icon =
      Create<VectorIcon>(kWebVrTimeoutMessageIcon, kPhaseForeground, 512);
  timeout_icon->SetIcon(features::IsRoundedIconsEnabled() ? kSadTabFilledIcon
                                                          : kSadTabOldIcon);
  timeout_icon->SetSize(kTimeoutMessageIconWidthDMM,
                        kTimeoutMessageIconHeightDMM);

  auto timeout_text = Create<Text>(kWebVrTimeoutMessageText, kPhaseForeground,
                                   kTimeoutMessageTextFontHeightDMM);
  timeout_text->SetText(
      l10n_util::GetStringUTF16(IDS_VR_WEB_VR_TIMEOUT_MESSAGE));
  timeout_text->SetColor(
      model_->color_scheme().web_vr_timeout_message_foreground);
  timeout_text->SetFieldWidth(kTimeoutMessageTextWidthDMM);

  timeout_layout->AddChild(std::move(timeout_icon));
  timeout_layout->AddChild(std::move(timeout_text));
  timeout_message->AddChild(std::move(timeout_layout));

  scaler->AddChild(std::move(timeout_message));
#if BUILDFLAG(IS_WIN)
  scaler->AddChild(std::move(spinner));
#endif
  scene_->AddUiElement(kWebVrViewportAwareRoot, std::move(scaler));
}
void UiSceneCreator::CreateViewportAwareRoot() {
  auto element = std::make_unique<ViewportAwareRoot>();
  element->SetName(kWebVrViewportAwareRoot);

  // On Windows, allow the viewport-aware UI to translate as well as rotate, so
  // it remains centered appropriately if the user moves.  Only enabled for
  // OS_WIN, since it conflicts with browser UI that isn't shown on Windows.
#if BUILDFLAG(IS_WIN)
  element->SetRecenterOnRotate(true);
#endif
  scene_->AddUiElement(kWebVrRoot, std::move(element));
}

void UiSceneCreator::CreateWebVrOverlayElements() {
  // Create transient WebVR elements.
  auto indicators = Create<LinearLayout>(kWebVrIndicatorLayout, kPhaseNone,
                                         LinearLayout::kDown);
  indicators->SetTranslate(0, 0, kWebVrPermissionDepth);
  indicators->set_margin(kWebVrPermissionOuterMargin);

  DrawPhase phase = kPhaseOverlayForeground;

  auto specs = GetIndicatorSpecs();
  for (const auto& spec : specs) {
    indicators->AddChild(CreateWebVrIndicator(model_, spec, phase));
  }

  auto parent = CreateTransientParent(kWebVrIndicatorTransience,
                                      GetIndicatorsTimeout(), true);

#if BUILDFLAG(IS_WIN)
  parent->AddBinding(
      std::make_unique<
          Binding<std::tuple<bool, CapturingStateModel, CapturingStateModel>>>(
          VR_BIND_LAMBDA(
              [](Model* model) {
                return std::tuple<bool, CapturingStateModel,
                                  CapturingStateModel>(

                    model->web_vr.IsImmersiveWebXrVisible() &&
                        model->web_vr.has_received_permissions,
                    model->active_capturing, model->potential_capturing);
              },
              base::Unretained(model_)),
          VR_BIND_LAMBDA(BindIndicatorTranscienceForWin,
                         base::Unretained(parent.get()),
                         base::Unretained(model_), base::Unretained(scene_))));
#else

  parent->AddBinding(std::make_unique<Binding<bool>>(
      VR_BIND_LAMBDA(
          [](Model* model) {
            return model->web_vr.IsImmersiveWebXrVisible() &&
                   model->web_vr.has_received_permissions;
          },
          base::Unretained(model_)),
      VR_BIND_LAMBDA(BindIndicatorTranscience, base::Unretained(parent.get()),
                     base::Unretained(model_), base::Unretained(scene_))));
#endif

  auto scaler = std::make_unique<ScaledDepthAdjuster>(kWebVrToastDistance);
  scaler->AddChild(std::move(indicators));
  parent->AddChild(std::move(scaler));

  scene_->AddUiElement(kWebVrViewportAwareRoot, std::move(parent));
}

}  // namespace vr
