// 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/views/animation/ink_drop_painted_layer_delegates.h"

#include <vector>

#include "cc/paint/paint_canvas.h"
#include "cc/paint/paint_flags.h"
#include "third_party/skia/include/core/SkRRect.h"
#include "ui/compositor/paint_recorder.h"
#include "ui/gfx/canvas.h"
#include "ui/gfx/color_palette.h"
#include "ui/gfx/geometry/insets.h"
#include "ui/gfx/geometry/point.h"
#include "ui/gfx/geometry/point_conversions.h"
#include "ui/gfx/geometry/rect.h"
#include "ui/gfx/geometry/rect_conversions.h"
#include "ui/gfx/geometry/rect_f.h"
#include "ui/gfx/geometry/size_conversions.h"
#include "ui/gfx/geometry/skia_conversions.h"
#include "ui/gfx/skia_paint_util.h"

namespace views {

////////////////////////////////////////////////////////////////////////////////
//
// BasePaintedLayerDelegate
//

BasePaintedLayerDelegate::BasePaintedLayerDelegate(SkColor color)
    : color_(color) {}

BasePaintedLayerDelegate::~BasePaintedLayerDelegate() = default;

gfx::Vector2dF BasePaintedLayerDelegate::GetCenteringOffset() const {
  return gfx::RectF(GetPaintedBounds()).CenterPoint().OffsetFromOrigin();
}

void BasePaintedLayerDelegate::OnDeviceScaleFactorChanged(
    float old_device_scale_factor,
    float new_device_scale_factor) {}

////////////////////////////////////////////////////////////////////////////////
//
// CircleLayerDelegate
//

CircleLayerDelegate::CircleLayerDelegate(SkColor color, int radius)
    : BasePaintedLayerDelegate(color), radius_(radius) {}

CircleLayerDelegate::~CircleLayerDelegate() = default;

gfx::RectF CircleLayerDelegate::GetPaintedBounds() const {
  const int diameter = radius_ * 2;
  return gfx::RectF(0, 0, diameter, diameter);
}

void CircleLayerDelegate::OnPaintLayer(const ui::PaintContext& context) {
  cc::PaintFlags flags;
  flags.setColor(color());
  flags.setAntiAlias(true);
  flags.setStyle(cc::PaintFlags::kFill_Style);

  ui::PaintRecorder recorder(context,
                             gfx::ToEnclosingRect(GetPaintedBounds()).size());
  gfx::Canvas* canvas = recorder.canvas();

  canvas->DrawCircle(GetPaintedBounds().CenterPoint(), radius_, flags);
}

////////////////////////////////////////////////////////////////////////////////
//
// RectangleLayerDelegate
//

RectangleLayerDelegate::RectangleLayerDelegate(SkColor color, gfx::SizeF size)
    : BasePaintedLayerDelegate(color), size_(size) {}

RectangleLayerDelegate::~RectangleLayerDelegate() = default;

gfx::RectF RectangleLayerDelegate::GetPaintedBounds() const {
  return gfx::RectF(size_);
}

void RectangleLayerDelegate::OnPaintLayer(const ui::PaintContext& context) {
  cc::PaintFlags flags;
  flags.setColor(color());
  flags.setAntiAlias(true);
  flags.setStyle(cc::PaintFlags::kFill_Style);

  ui::PaintRecorder recorder(context, gfx::ToCeiledSize(size_));
  gfx::Canvas* canvas = recorder.canvas();
  canvas->DrawRect(GetPaintedBounds(), flags);
}

////////////////////////////////////////////////////////////////////////////////
//
// RoundedRectangleLayerDelegate
//

RoundedRectangleLayerDelegate::RoundedRectangleLayerDelegate(
    SkColor color,
    const gfx::SizeF& size,
    int corner_radius)
    : BasePaintedLayerDelegate(color),
      size_(size),
      corner_radius_(corner_radius) {}

RoundedRectangleLayerDelegate::~RoundedRectangleLayerDelegate() = default;

gfx::RectF RoundedRectangleLayerDelegate::GetPaintedBounds() const {
  return gfx::RectF(size_);
}

void RoundedRectangleLayerDelegate::OnPaintLayer(
    const ui::PaintContext& context) {
  cc::PaintFlags flags;
  flags.setColor(color());
  flags.setAntiAlias(true);
  flags.setStyle(cc::PaintFlags::kFill_Style);

  ui::PaintRecorder recorder(context, gfx::ToCeiledSize(size_));
  const float dsf = recorder.canvas()->UndoDeviceScaleFactor();
  gfx::RectF rect = GetPaintedBounds();
  rect.Scale(dsf);
  recorder.canvas()->DrawRoundRect(gfx::ToEnclosingRect(rect),
                                   dsf * corner_radius_, flags);
}

////////////////////////////////////////////////////////////////////////////////
//
// BorderShadowLayerDelegate
//

BorderShadowLayerDelegate::BorderShadowLayerDelegate(
    const std::vector<gfx::ShadowValue>& shadows,
    const gfx::Rect& shadowed_area_bounds,
    SkColor fill_color,
    int corner_radius)
    : BasePaintedLayerDelegate(gfx::kPlaceholderColor),
      shadows_(shadows),
      bounds_(shadowed_area_bounds),
      fill_color_(fill_color),
      corner_radius_(corner_radius) {}

BorderShadowLayerDelegate::~BorderShadowLayerDelegate() = default;

gfx::RectF BorderShadowLayerDelegate::GetPaintedBounds() const {
  gfx::Rect total_rect(bounds_);
  total_rect.Inset(gfx::ShadowValue::GetMargin(shadows_));
  return gfx::RectF(total_rect);
}

gfx::Vector2dF BorderShadowLayerDelegate::GetCenteringOffset() const {
  return gfx::RectF(bounds_).CenterPoint().OffsetFromOrigin();
}

void BorderShadowLayerDelegate::OnPaintLayer(const ui::PaintContext& context) {
  cc::PaintFlags flags;
  flags.setStyle(cc::PaintFlags::kFill_Style);
  flags.setAntiAlias(true);
  flags.setColor(fill_color_);

  gfx::RectF rrect_bounds =
      gfx::RectF(bounds_) - GetPaintedBounds().OffsetFromOrigin();
  SkRRect r_rect = SkRRect::MakeRectXY(gfx::RectFToSkRect(rrect_bounds),
                                       corner_radius_, corner_radius_);

  // First the fill color.
  ui::PaintRecorder recorder(context,
                             gfx::ToCeiledSize(GetPaintedBounds().size()));
  recorder.canvas()->sk_canvas()->drawRRect(r_rect, flags);

  // Now the shadow.
  flags.setLooper(gfx::CreateShadowDrawLooper(shadows_));
  recorder.canvas()->sk_canvas()->clipRRect(r_rect, SkClipOp::kDifference,
                                            true);
  recorder.canvas()->sk_canvas()->drawRRect(r_rect, flags);
}

}  // namespace views
