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

#include "ui/touch_selection/touch_handle_drawable_aura.h"

#include "ui/aura/window.h"
#include "ui/aura/window_targeter.h"
#include "ui/aura_extra/image_window_delegate.h"
#include "ui/base/cursor/cursor.h"
#include "ui/base/hit_test.h"
#include "ui/base/resource/resource_bundle.h"
#include "ui/compositor/layer.h"
#include "ui/events/event.h"
#include "ui/gfx/canvas.h"
#include "ui/gfx/geometry/rect_conversions.h"
#include "ui/resources/grit/ui_resources.h"

namespace ui {
namespace {

// The distance by which a handle image is offset from the focal point (i.e.
// text baseline) downwards.
const int kSelectionHandleVerticalVisualOffset = 2;

// The padding around the selection handle image can be used to extend the
// handle window so that touch events near the selection handle image are
// targeted to the selection handle window.
const int kSelectionHandlePadding = 0;

// Epsilon value used to compare float values to zero.
const float kEpsilon = 1e-8f;

// Returns the appropriate handle image based on the handle orientation.
gfx::Image* GetHandleImage(TouchHandleOrientation orientation) {
  int resource_id = 0;
  switch (orientation) {
    case TouchHandleOrientation::LEFT:
      resource_id = IDR_TEXT_SELECTION_HANDLE_LEFT;
      break;
    case TouchHandleOrientation::CENTER:
      resource_id = IDR_TEXT_SELECTION_HANDLE_CENTER;
      break;
    case TouchHandleOrientation::RIGHT:
      resource_id = IDR_TEXT_SELECTION_HANDLE_RIGHT;
      break;
    case TouchHandleOrientation::UNDEFINED:
      NOTREACHED() << "Invalid touch handle bound type.";
      return nullptr;
  };
  return &ResourceBundle::GetSharedInstance().GetImageNamed(resource_id);
}

bool IsNearlyZero(float value) {
  return std::abs(value) < kEpsilon;
}

}  // namespace

TouchHandleDrawableAura::TouchHandleDrawableAura(aura::Window* parent)
    : window_(
          std::make_unique<aura::Window>(new aura_extra::ImageWindowDelegate)),
      window_delegate_(
          static_cast<aura_extra::ImageWindowDelegate*>(window_->delegate())),
      enabled_(false),
      alpha_(0),
      orientation_(TouchHandleOrientation::UNDEFINED) {
  window_delegate_->set_image_offset(gfx::Vector2d(kSelectionHandlePadding,
                                                   kSelectionHandlePadding));
  window_->SetTransparent(true);
  window_->Init(LAYER_TEXTURED);
  window_->set_owned_by_parent(false);
  window_->SetEventTargetingPolicy(aura::EventTargetingPolicy::kNone);
  parent->AddChild(window_.get());
}

TouchHandleDrawableAura::~TouchHandleDrawableAura() {
}

void TouchHandleDrawableAura::UpdateBounds() {
  gfx::RectF new_bounds = relative_bounds_;
  new_bounds.Offset(origin_position_.x(), origin_position_.y());
  window_->SetBounds(gfx::ToEnclosingRect(new_bounds));
}

bool TouchHandleDrawableAura::IsVisible() const {
  return enabled_ && !IsNearlyZero(alpha_);
}

void TouchHandleDrawableAura::SetEnabled(bool enabled) {
  if (enabled == enabled_)
    return;

  enabled_ = enabled;
  if (IsVisible())
    window_->Show();
  else
    window_->Hide();
}

void TouchHandleDrawableAura::SetOrientation(TouchHandleOrientation orientation,
                                             bool mirror_vertical,
                                             bool mirror_horizontal) {
  // TODO(AviD): Implement adaptive handle orientation logic for Aura
  DCHECK(window_delegate_);
  DCHECK(!mirror_vertical);
  DCHECK(!mirror_horizontal);

  if (orientation_ == orientation)
    return;
  orientation_ = orientation;
  gfx::Image* image = GetHandleImage(orientation);
  window_delegate_->SetImage(*image);

  // Calculate the relative bounds.
  gfx::Size image_size = image->Size();
  int window_width = image_size.width() + 2 * kSelectionHandlePadding;
  int window_height = image_size.height() + 2 * kSelectionHandlePadding;
  relative_bounds_ =
      gfx::RectF(-kSelectionHandlePadding,
                 kSelectionHandleVerticalVisualOffset - kSelectionHandlePadding,
                 window_width, window_height);
  gfx::Rect paint_bounds(relative_bounds_.x(), relative_bounds_.y(),
                         relative_bounds_.width(), relative_bounds_.height());
  window_->SchedulePaintInRect(paint_bounds);
  UpdateBounds();
}

void TouchHandleDrawableAura::SetOrigin(const gfx::PointF& position) {
  origin_position_ = position;
  UpdateBounds();
}

void TouchHandleDrawableAura::SetAlpha(float alpha) {
  if (alpha == alpha_)
    return;

  alpha_ = alpha;
  window_->layer()->SetOpacity(alpha_);
  if (IsVisible())
    window_->Show();
  else
    window_->Hide();
}

gfx::RectF TouchHandleDrawableAura::GetVisibleBounds() const {
  gfx::RectF bounds(window_->bounds());
  bounds.Inset(gfx::InsetsF::TLBR(
      kSelectionHandlePadding + kSelectionHandleVerticalVisualOffset,
      kSelectionHandlePadding, kSelectionHandlePadding,
      kSelectionHandlePadding));
  return bounds;
}

float TouchHandleDrawableAura::GetDrawableHorizontalPaddingRatio() const {
  // Aura does not have any transparent padding for its handle drawable.
  return 0.0f;
}

#if BUILDFLAG(IS_OHOS)
void TouchHandleDrawableAura::SetEdge(const gfx::PointF& top,
                                      const gfx::PointF& bottom) {}
#endif

}  // namespace ui