// Copyright 2019 The Chromium 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 "cc/trees/layer_tree_host.h"

#include "cc/test/fake_content_layer_client.h"
#include "cc/test/fake_picture_layer.h"
#include "cc/test/layer_tree_test.h"
#include "cc/trees/transform_node.h"

namespace cc {
namespace {

class FakeTextHolder {
 public:
  FakeTextHolder(const std::string& text, const gfx::Rect& rect, NodeId node_id)
      : text_(text), rect_(rect), node_id_(node_id) {}
  std::string text() const { return text_; }
  gfx::Rect rect() const { return rect_; }
  NodeId node_id() const { return node_id_; }

 private:
  std::string text_;
  gfx::Rect rect_;
  NodeId node_id_;
};

class FakeCaptureContentLayerClient : public FakeContentLayerClient {
 public:
  void addTextHolder(const FakeTextHolder& holder) {
    holders_.push_back(holder);
  }

  scoped_refptr<DisplayItemList> PaintContentsToDisplayList(
      PaintingControlSetting painting_control) override {
    auto display_list = base::MakeRefCounted<DisplayItemList>();
    for (auto& holder : holders_) {
      display_list->StartPaint();
      display_list->push<DrawTextBlobOp>(
          SkTextBlob::MakeFromString(holder.text().data(), SkFont()),
          holder.rect().x(), holder.rect().y(), holder.node_id(), PaintFlags());
      display_list->EndPaintOfUnpaired(holder.rect());
    }
    display_list->Finalize();
    return display_list;
  }

 private:
  std::vector<const FakeTextHolder> holders_;
};

// These tests are for LayerTreeHost::CaptureContent().
class LayerTreeHostCaptureContentTest : public LayerTreeTest {
 public:
  ~LayerTreeHostCaptureContentTest() override = default;

 protected:
  LayerTreeHostCaptureContentTest() : device_bounds_(10, 10) {}

  void BeginTest() override { PostSetNeedsCommitToMainThread(); }

  void DidCommit() override { PostCaptureContentToMainThread(); }

  void SetupRootPictureLayer(const gfx::Size& size) {
    scoped_refptr<Layer> root = Layer::Create();
    root->SetBounds(size);

    client_.set_bounds(size);
    root_picture_layer_ = FakePictureLayer::Create(&client_);
    root_picture_layer_->SetBounds(size);
    root->AddChild(root_picture_layer_);

    layer_tree_host()->SetRootLayer(root);
    layer_tree_host()->SetViewportVisibleRect(gfx::Rect(device_bounds_));
  }

  void VerifyCapturedContent(std::vector<FakeTextHolder>* expected_result) {
    EXPECT_EQ(expected_result->size(), captured_content_.size());
    size_t expected_left_result = expected_result->size();
    for (auto& c : captured_content_) {
      for (auto it = expected_result->begin(); it != expected_result->end();
           ++it) {
        if (it->node_id() == c.node_id) {
          expected_result->erase(it);
          break;
        }
      }
      EXPECT_EQ(--expected_left_result, expected_result->size());
    }
  }

  FakeCaptureContentLayerClient client_;

 private:
  void PostCaptureContentToMainThread() {
    MainThreadTaskRunner()->PostTask(
        FROM_HERE,
        base::BindOnce(&LayerTreeHostCaptureContentTest::CaptureContent,
                       weak_factory_.GetWeakPtr()));
  }

  void CaptureContent() {
    captured_content_.clear();
    layer_tree_host()->CaptureContent(&captured_content_);
    EndTest();
  }

  scoped_refptr<FakePictureLayer> root_picture_layer_;
  std::vector<NodeInfo> captured_content_;
  const gfx::Size device_bounds_;
  base::WeakPtrFactory<LayerTreeHostCaptureContentTest> weak_factory_{this};
};

class LayerTreeHostCaptureContentTestBasic
    : public LayerTreeHostCaptureContentTest {
 protected:
  void SetupTextHolders(const gfx::Rect& rect1, const gfx::Rect& rect2) {
    text_holder_1_ = std::make_unique<FakeTextHolder>("Text1", rect1, 1);
    client_.addTextHolder(*text_holder_1_);
    text_holder_2_ = std::make_unique<FakeTextHolder>("Text2", rect2, 2);
    client_.addTextHolder(*text_holder_2_);
  }

  std::unique_ptr<FakeTextHolder> text_holder_1_;
  std::unique_ptr<FakeTextHolder> text_holder_2_;
};

// Test that one DrawTextBlobOp is on-screen, another isn't.
class LayerTreeHostCaptureContentTestOneVisible
    : public LayerTreeHostCaptureContentTestBasic {
 protected:
  void SetupTree() override {
    // One is visible.
    SetupTextHolders(gfx::Rect(0, 0, 5, 5), gfx::Rect(11, 0, 5, 5));
    SetupRootPictureLayer(gfx::Size(10, 10));
  }

  void AfterTest() override {
    std::vector<FakeTextHolder> expected_result;
    expected_result.push_back(*text_holder_1_);
    VerifyCapturedContent(&expected_result);
  }
};

SINGLE_AND_MULTI_THREAD_TEST_F(LayerTreeHostCaptureContentTestOneVisible);

// Test that both DrawTextBlobOps are on-screen.
class LayerTreeHostCaptureContentTestTwoVisible
    : public LayerTreeHostCaptureContentTestBasic {
 protected:
  void SetupTree() override {
    // One is visible, another is partial visible.
    SetupTextHolders(gfx::Rect(0, 0, 5, 5), gfx::Rect(9, 0, 5, 5));
    SetupRootPictureLayer(gfx::Size(10, 10));
  }

  void AfterTest() override {
    std::vector<FakeTextHolder> expected_result;
    expected_result.push_back(*text_holder_1_);
    expected_result.push_back(*text_holder_2_);
    VerifyCapturedContent(&expected_result);
  }
};

SINGLE_AND_MULTI_THREAD_TEST_F(LayerTreeHostCaptureContentTestTwoVisible);

// Base class for two layers tests.
class LayerTreeHostCaptureContentTestTwoLayers
    : public LayerTreeHostCaptureContentTestBasic {
 protected:
  void SetupTree() override {
    SetupTextHolders(gfx::Rect(0, 0, 5, 5), gfx::Rect(5, 5, 5, 5));
    SetupRootPictureLayer(gfx::Size(10, 10));
    SetupSecondaryPictureLayer(gfx::Size(10, 10));
  }

  void DidCommit() override {
    LayerTreeHostCaptureContentTestBasic::DidCommit();
    OnSetupSecondaryLayTransform();
  }

  virtual void OnSetupSecondaryLayTransform() {}

  virtual bool IsSecondaryPictureLayerContentsOpaque() { return true; }

  // Setup transform node for secondary picture layer, must be called in
  // OnSetupSecondaryLayTransform().
  void SetupTransform(const gfx::Vector2dF& translate) {
    TransformNode transform_node;
    transform_node.local.Translate(translate);
    transform_node.id =
        layer_tree_host()->property_trees()->transform_tree.Insert(
            transform_node, 0);
    picture_layer->SetTransformTreeIndex(transform_node.id);
    layer_tree_host()->property_trees()->transform_tree.UpdateTransforms(
        transform_node.id);
  }

  void SetupSecondaryPictureLayer(const gfx::Size& size) {
    // Add text to layer.
    text_holder_21_ =
        std::make_unique<FakeTextHolder>("Text21", gfx::Rect(0, 0, 10, 5), 21);
    client2_.addTextHolder(*text_holder_21_);
    text_holder_22_ =
        std::make_unique<FakeTextHolder>("Text22", gfx::Rect(0, 5, 10, 5), 22);
    client2_.addTextHolder(*text_holder_22_);
    client2_.set_bounds(size);

    // Create layer.
    picture_layer = FakePictureLayer::Create(&client2_);
    picture_layer->SetBounds(size);
    picture_layer->SetContentsOpaque(IsSecondaryPictureLayerContentsOpaque());
    layer_tree_host()->root_layer()->AddChild(picture_layer);
  }

  scoped_refptr<FakePictureLayer> picture_layer;
  std::unique_ptr<FakeTextHolder> text_holder_21_;
  std::unique_ptr<FakeTextHolder> text_holder_22_;
  FakeCaptureContentLayerClient client2_;
};

// Test that one layer is within screen, another isn't.
class LayerTreeHostCaptureContentTestOneLayerVisible
    : public LayerTreeHostCaptureContentTestTwoLayers {
  void OnSetupSecondaryLayTransform() override {
    // Moves the layer out of visual port.
    SetupTransform(gfx::Vector2dF(0, 10));
  }

  void AfterTest() override {
    std::vector<FakeTextHolder> expected_result;
    expected_result.push_back(*text_holder_1_);
    expected_result.push_back(*text_holder_2_);
    VerifyCapturedContent(&expected_result);
  }
};

SINGLE_AND_MULTI_THREAD_TEST_F(LayerTreeHostCaptureContentTestOneLayerVisible);

// Test that the upper layer is partially on-screen, the under layer fully
// occupies the screen, all layer's on-screen content are returned even the
// upper layer is opaque.
class LayerTreeHostCaptureContentTestTwoLayersVisible
    : public LayerTreeHostCaptureContentTestTwoLayers {
  void OnSetupSecondaryLayTransform() override {
    SetupTransform(gfx::Vector2dF(0, 8));
  }

  void AfterTest() override {
    std::vector<FakeTextHolder> expected_result;
    expected_result.push_back(*text_holder_1_);
    expected_result.push_back(*text_holder_2_);
    expected_result.push_back(*text_holder_21_);
    VerifyCapturedContent(&expected_result);
  }
};

SINGLE_AND_MULTI_THREAD_TEST_F(LayerTreeHostCaptureContentTestTwoLayersVisible);

// Test that the upper layer is transparent, the under layer fully occupies the
// screen, all layer's on-screen content are returned.
class LayerTreeHostCaptureContentTestTwoLayersVisibleAndTransparent
    : public LayerTreeHostCaptureContentTestTwoLayersVisible {
  bool IsSecondaryPictureLayerContentsOpaque() override { return false; }

  void AfterTest() override {
    // All 3  TextHolders are returned.
    std::vector<FakeTextHolder> expected_result;
    expected_result.push_back(*text_holder_1_);
    expected_result.push_back(*text_holder_2_);
    expected_result.push_back(*text_holder_21_);
    VerifyCapturedContent(&expected_result);
  }
};

SINGLE_AND_MULTI_THREAD_TEST_F(
    LayerTreeHostCaptureContentTestTwoLayersVisibleAndTransparent);

// Test that the upper layer is partially visible, but doesn't overlay screen in
// either direction, the under layer's content will be captured even it is fully
// overlaid by the upper layer.
class LayerTreeHostCaptureContentTestUpperLayerPartialOverlay
    : public LayerTreeHostCaptureContentTestTwoLayers {
  void OnSetupSecondaryLayTransform() override {
    SetupTransform(gfx::Vector2dF(2, 2));
  }

  void AfterTest() override {
    std::vector<FakeTextHolder> expected_result;
    expected_result.push_back(*text_holder_1_);
    expected_result.push_back(*text_holder_2_);
    expected_result.push_back(*text_holder_21_);
    expected_result.push_back(*text_holder_22_);
    VerifyCapturedContent(&expected_result);
  }
};

SINGLE_AND_MULTI_THREAD_TEST_F(
    LayerTreeHostCaptureContentTestUpperLayerPartialOverlay);

}  // namespace
}  // namespace cc
