// Copyright 2012 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.

#ifndef CC_TREES_OCCLUSION_TRACKER_H_
#define CC_TREES_OCCLUSION_TRACKER_H_

#include <vector>

#include "cc/base/simple_enclosed_region.h"
#include "cc/cc_export.h"
#include "cc/layers/effect_tree_layer_list_iterator.h"
#include "cc/trees/occlusion.h"
#include "ui/gfx/geometry/rect.h"

namespace cc {
class LayerImpl;
class Region;
class RenderSurfaceImpl;

// This class is used to track occlusion of layers while traversing them in a
// front-to-back order. As each layer is visited, one of the methods in this
// class is called to notify it about the current target surface. Then,
// occlusion in the content space of the current layer may be queried, via
// Occlusion from GetCurrentOcclusionForLayer(). If the current layer owns a
// RenderSurfaceImpl, then occlusion on that RenderSurfaceImpl may also be
// queried via surfaceOccluded() and surfaceUnoccludedContentRect(). Finally,
// once finished with the layer, occlusion behind the layer should be marked by
// calling MarkOccludedBehindLayer().
class CC_EXPORT OcclusionTracker {
 public:
  explicit OcclusionTracker(const gfx::Rect& screen_space_clip_rect);
  OcclusionTracker(const OcclusionTracker&) = delete;
  ~OcclusionTracker();

  OcclusionTracker& operator=(const OcclusionTracker&) = delete;

  // Return an occlusion that retains the current state of the tracker
  // and can be used outside of a layer walk to check occlusion.
  Occlusion GetCurrentOcclusionForLayer(
      const gfx::Transform& draw_transform) const;
  Occlusion GetCurrentOcclusionForContributingSurface(
      const gfx::Transform& draw_transform) const;

  const RenderSurfaceImpl* OcclusionSurfaceForContributingSurface() const;
  // Called at the beginning of each step in EffectTreeLayerListIterator's
  // front-to-back traversal.
  void EnterLayer(const EffectTreeLayerListIterator::Position& iterator);
  // Called at the end of each step in EffectTreeLayerListIterator's
  // front-to-back traversal.
  void LeaveLayer(const EffectTreeLayerListIterator::Position& iterator);

  // Gives the region of the screen that is not occluded by something opaque.
  Region ComputeVisibleRegionInScreen(const LayerTreeImpl* layer_tree) const;

  void set_minimum_tracking_size(const gfx::Size& size) {
    minimum_tracking_size_ = size;
  }

 protected:
  struct StackObject {
    StackObject() : target(nullptr) {}
    explicit StackObject(const RenderSurfaceImpl* target) : target(target) {}
    const RenderSurfaceImpl* target;
    SimpleEnclosedRegion occlusion_from_outside_target;
    SimpleEnclosedRegion occlusion_from_inside_target;
    bool ignores_parent_occlusion = false;
  };

  // The stack holds occluded regions for subtrees in the
  // RenderSurfaceImpl-Layer tree, so that when we leave a subtree we may apply
  // a mask to it, but not to the parts outside the subtree.
  // - The first time we see a new subtree under a target, we add that target to
  // the top of the stack. This can happen as a layer representing itself, or as
  // a target surface.
  // - When we visit a target surface, we apply its mask to its subtree, which
  // is at the top of the stack.
  // - When we visit a layer representing itself, we add its occlusion to the
  // current subtree, which is at the top of the stack.
  // - When we visit a layer representing a contributing surface, the current
  // target will never be the top of the stack since we just came from the
  // contributing surface.
  // We merge the occlusion at the top of the stack with the new current
  // subtree. This new target is pushed onto the stack if not already there.
  std::vector<StackObject> stack_;

 private:
  // Called when visiting a layer. If the target was not already current, then
  // this indicates we have entered a new surface subtree.
  void EnterRenderTarget(const RenderSurfaceImpl* new_target_surface);

  // Called when visiting a target surface. This indicates we have visited all
  // the layers within the surface, and we may perform any surface-wide
  // operations.
  void FinishedRenderTarget(const RenderSurfaceImpl* finished_target_surface);

  // Called when visiting a contributing surface. This indicates that we are
  // leaving our current surface, and entering the new one. We then perform any
  // operations required for merging results from the child subtree into its
  // parent.
  void LeaveToRenderTarget(const RenderSurfaceImpl* new_target_surface);

  // Add the layer's occlusion to the tracked state.
  void MarkOccludedBehindLayer(const LayerImpl* layer);

  gfx::Rect screen_space_clip_rect_;
  gfx::Size minimum_tracking_size_;
};

}  // namespace cc

#endif  // CC_TREES_OCCLUSION_TRACKER_H_
