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

#ifndef PDF_PAINT_MANAGER_H_
#define PDF_PAINT_MANAGER_H_

#include <vector>

#include "base/memory/raw_ptr.h"
#include "base/memory/weak_ptr.h"
#include "pdf/paint_aggregator.h"
#include "third_party/skia/include/core/SkRefCnt.h"
#include "ui/gfx/geometry/size.h"
#if BUILDFLAG(ARKWEB_PDF)
#include "base/threading/thread.h"
#endif

class SkImage;
class SkSurface;

namespace gfx {
class Point;
class Rect;
class Vector2d;
class Vector2dF;
}  // namespace gfx

namespace chrome_pdf {

// Custom PaintManager for the PDF plugin.  This is branched from the Pepper
// version.  The difference is that this supports progressive rendering of dirty
// rects, where multiple calls to the rendering engine are needed.  It also
// supports having higher-priority rects flushing right away, i.e. the
// scrollbars.
//
// The client's OnPaint
class PaintManager {
 public:
  class Client {
   public:
    // Invalidates the entire plugin container, scheduling a repaint.
    virtual void InvalidatePluginContainer() = 0;

    // Paints the given invalid area of the plugin to the given graphics
    // device. Returns true if anything was painted.
    //
    // You are given the list of rects to paint in `paint_rects`.  You can
    // combine painting into less rectangles if it's more efficient.  When a
    // rect is painted, information about that paint should be inserted into
    // `ready`.  Otherwise if a paint needs more work, add the rect to
    // `pending`.  If `pending` is not empty, your OnPaint function will get
    // called again.  Once OnPaint is called and it returns no pending rects,
    // all the previously ready rects will be flushed on screen.  The exception
    // is for ready rects that have `flush_now` set to true.  These will be
    // flushed right away.
    //
    // Do not call Flush() on the graphics device, this will be done
    // automatically if you return true from this function since the
    // PaintManager needs to handle the callback.
    //
    // Calling Invalidate/Scroll is not allowed while inside an OnPaint
    virtual void OnPaint(const std::vector<gfx::Rect>& paint_rects,
                         std::vector<PaintReadyRect>& ready,
                         std::vector<gfx::Rect>& pending) = 0;

    // Updates the client with the latest snapshot created by `Flush()`.
    virtual void UpdateSnapshot(sk_sp<SkImage> snapshot) = 0;

    // Updates the client with the latest output scale.
    virtual void UpdateScale(float scale) = 0;

    // Updates the client with the latest output layer transform.
    virtual void UpdateLayerTransform(float scale,
                                      const gfx::Vector2dF& translate) = 0;

#if BUILDFLAG(ARKWEB_PDF)
    virtual gfx::Rect GetAvailableArea() = 0;
#endif
   protected:
    // You shouldn't delete through this interface.
    ~Client() = default;
  };

  // The Client is a non-owning pointer and must remain valid (normally the
  // object implementing the Client interface will own the paint manager).
  //
  // You will need to call SetSize() before this class will do anything.
  // Normally you do this from UpdateGeometryOnViewChanged() of your plugin
  // instance.
  explicit PaintManager(Client* client);
  PaintManager(const PaintManager&) = delete;
  PaintManager& operator=(const PaintManager&) = delete;
  ~PaintManager();

  // Returns the size of the graphics context to allocate for a given plugin
  // size. We may allocated a slightly larger buffer than required so that we
  // don't have to resize the context when scrollbars appear/dissapear due to
  // zooming (which can result in flickering).
  static gfx::Size GetNewContextSize(const gfx::Size& current_context_size,
                                     const gfx::Size& plugin_size);

  // Sets the size of the plugin. If the size is the same as the previous call,
  // this will be a NOP. If the size has changed, a new device will be
  // allocated to the given size and a paint to that device will be scheduled.
  //
  // This is intended to be called from ViewChanged with the size of the
  // plugin. Since it tracks the old size and only allocates when the size
  // changes, you can always call this function without worrying about whether
  // the size changed or ViewChanged is called for another reason (like the
  // position changed).
  void SetSize(const gfx::Size& new_size, float new_device_scale);

  // Invalidate the entire plugin.
  void Invalidate();

  // Invalidate the given rect.
  void InvalidateRect(const gfx::Rect& rect);

  // The given rect should be scrolled by the given amounts.
  void ScrollRect(const gfx::Rect& clip_rect, const gfx::Vector2d& amount);

  // Returns the size of the graphics context for the next paint operation.
  // This is the pending size if a resize is pending (the plugin has called
  // SetSize but we haven't actually painted it yet), or the current size of
  // no resize is pending.
  gfx::Size GetEffectiveSize() const;
  float GetEffectiveDeviceScale() const;

  // Set the transform for the graphics layer.
  // If `schedule_flush` is true, it ensures a flush will be scheduled for
  // this change. If `schedule_flush` is false, then the change will not take
  // effect until another change causes a flush.
  void SetTransform(float scale,
                    const gfx::Point& origin,
                    const gfx::Vector2d& translate,
                    bool schedule_flush);
  // Resets any transform for the graphics layer.
  // This does not schedule a flush.
  void ClearTransform();

#if BUILDFLAG(ARKWEB_PDF)
  void DoPaintAtScrollStopped();
#endif  // BUILDFLAG(ARKWEB_PDF)

 private:
  // Makes sure there is a callback that will trigger a paint at a later time.
  // This will be either a Flush callback telling us we're allowed to generate
  // more data, or, if there's no flush callback pending, a manual call back
  // to the message loop via ExecuteOnMainThread.
  void EnsureCallbackPending();

#if BUILDFLAG(ARKWEB_PDF)
  void DoPaint(bool is_repainting);
#else
  // Does the client paint and executes a Flush if necessary.
  void DoPaint();
#endif

  // Executes a Flush.
  void Flush();

  // Callback for asynchronous completion of Flush.
  void OnFlushComplete();

  // Callback for manual scheduling of paints when there is no flush callback
  // pending.
  void OnManualCallbackComplete();

  // Non-owning pointer. See the constructor.
  const raw_ptr<Client> client_;

  // Backing Skia surface.
  sk_sp<SkSurface> surface_;

  PaintAggregator aggregator_;

  // See comment for EnsureCallbackPending for more on how these work.
  bool manual_callback_pending_ = false;
  bool flush_pending_ = false;
  bool flush_requested_ = false;

  // When we get a resize, we don't do so right away (see `SetSize()`). The
  // `has_pending_resize_` tells us that we need to do a resize for the next
  // paint operation. When true, the new size is in `pending_size_`.
  bool has_pending_resize_ = false;
  gfx::Size pending_size_;
  gfx::Size plugin_size_;
  float pending_device_scale_ = 1.0f;
  float device_scale_ = 1.0f;

  // True iff we're in the middle of a paint.
  bool in_paint_ = false;

  // True if we haven't painted the plugin viewport yet.
  bool first_paint_ = true;

  // True when the view size just changed and we're waiting for a paint.
  bool view_size_changed_waiting_for_paint_ = false;

#if BUILDFLAG(ARKWEB_PDF)
  scoped_refptr<base::SingleThreadTaskRunner> main_thread_task_runner_;
  PaintAggregator::PaintUpdate last_update_rect;
#endif

  base::WeakPtrFactory<PaintManager> weak_factory_{this};
};

}  // namespace chrome_pdf

#endif  // PDF_PAINT_MANAGER_H_