// Copyright (c) 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 UI_GL_GL_SURFACE_H_
#define UI_GL_GL_SURFACE_H_

#include <string>

#include "base/callback.h"
#include "base/macros.h"
#include "base/memory/ref_counted.h"
#include "build/build_config.h"
#include "ui/gfx/geometry/rect.h"
#include "ui/gfx/geometry/rect_f.h"
#include "ui/gfx/geometry/size.h"
#include "ui/gfx/native_widget_types.h"
#include "ui/gfx/overlay_transform.h"
#include "ui/gfx/swap_result.h"
#include "ui/gl/gl_export.h"
#include "ui/gl/gl_image.h"
#include "ui/gl/gl_implementation.h"

namespace gfx {
class VSyncProvider;
}

namespace ui {
struct CARendererLayerParams;
}

namespace gl {

class GLContext;

// Encapsulates a surface that can be rendered to with GL, hiding platform
// specific management.
class GL_EXPORT GLSurface : public base::RefCounted<GLSurface> {
 public:
  GLSurface();

  // Minimum bit depth of surface.
  enum Format {
    SURFACE_ARGB8888,
    SURFACE_RGB565,
    SURFACE_OSMESA_BGRA,
    SURFACE_OSMESA_RGBA,
    SURFACE_SURFACELESS,
    SURFACE_DEFAULT = SURFACE_ARGB8888
  };

  // (Re)create the surface. TODO(apatrick): This is an ugly hack to allow the
  // EGL surface associated to be recreated without destroying the associated
  // context. The implementation of this function for other GLSurface derived
  // classes is in a pending changelist.
  virtual bool Initialize();
  virtual bool Initialize(GLSurface::Format format);

  // Destroys the surface.
  virtual void Destroy() = 0;

  virtual bool Resize(const gfx::Size& size,
                      float scale_factor,
                      bool has_alpha);

  // Recreate the surface without changing the size.
  virtual bool Recreate();

  // Unschedule the CommandExecutor and return true to abort the processing of
  // a GL draw call to this surface and defer it until the CommandExecutor is
  // rescheduled.
  virtual bool DeferDraws();

  // Returns true if this surface is offscreen.
  virtual bool IsOffscreen() = 0;

  // Swaps front and back buffers. This has no effect for off-screen
  // contexts.
  virtual gfx::SwapResult SwapBuffers() = 0;

  // Get the size of the surface.
  virtual gfx::Size GetSize() = 0;

  // Get the underlying platform specific surface "handle".
  virtual void* GetHandle() = 0;

  // Returns whether or not the surface supports SwapBuffersWithDamage
  virtual bool SupportsSwapBuffersWithDamage();

  // Returns whether or not the surface supports PostSubBuffer.
  virtual bool SupportsPostSubBuffer();

  // Returns whether or not the surface supports CommitOverlayPlanes.
  virtual bool SupportsCommitOverlayPlanes();

  // Returns whether SwapBuffersAsync() is supported.
  virtual bool SupportsAsyncSwap();

  // Returns the internal frame buffer object name if the surface is backed by
  // FBO. Otherwise returns 0.
  virtual unsigned int GetBackingFramebufferObject();

  typedef base::Callback<void(gfx::SwapResult)> SwapCompletionCallback;
  // Swaps front and back buffers. This has no effect for off-screen
  // contexts. On some platforms, we want to send SwapBufferAck only after the
  // surface is displayed on screen. The callback can be used to delay sending
  // SwapBufferAck till that data is available. The callback should be run on
  // the calling thread (i.e. same thread SwapBuffersAsync is called)
  virtual void SwapBuffersAsync(const SwapCompletionCallback& callback);

  // Swap buffers with damage rect.
  virtual gfx::SwapResult SwapBuffersWithDamage(int x,
                                                int y,
                                                int width,
                                                int height);

  // Copy part of the backbuffer to the frontbuffer.
  virtual gfx::SwapResult PostSubBuffer(int x, int y, int width, int height);

  // Copy part of the backbuffer to the frontbuffer. On some platforms, we want
  // to send SwapBufferAck only after the surface is displayed on screen. The
  // callback can be used to delay sending SwapBufferAck till that data is
  // available. The callback should be run on the calling thread (i.e. same
  // thread PostSubBufferAsync is called)
  virtual void PostSubBufferAsync(int x,
                                  int y,
                                  int width,
                                  int height,
                                  const SwapCompletionCallback& callback);

  // Show overlay planes but don't swap the front and back buffers. This acts
  // like SwapBuffers from the point of view of the client, but is cheaper when
  // overlays account for all the damage.
  virtual gfx::SwapResult CommitOverlayPlanes();

  // Show overlay planes but don't swap the front and back buffers. On some
  // platforms, we want to send SwapBufferAck only after the overlays are
  // displayed on screen. The callback can be used to delay sending
  // SwapBufferAck till that data is available. The callback should be run on
  // the calling thread (i.e. same thread CommitOverlayPlanesAsync is called).
  virtual void CommitOverlayPlanesAsync(const SwapCompletionCallback& callback);

  // Called after a context is made current with this surface. Returns false
  // on error.
  virtual bool OnMakeCurrent(GLContext* context);

  // Used for explicit buffer management.
  virtual bool SetBackbufferAllocation(bool allocated);
  virtual void SetFrontbufferAllocation(bool allocated);

  // Get a handle used to share the surface with another process. Returns null
  // if this is not possible.
  virtual void* GetShareHandle();

  // Get the platform specific display on which this surface resides, if
  // available.
  virtual void* GetDisplay();

  // Get the platfrom specific configuration for this surface, if available.
  virtual void* GetConfig();

  // Get the key corresponding to the set of GLSurfaces that can be made current
  // with this GLSurface.
  virtual unsigned long GetCompatibilityKey();

  // Get the GL pixel format of the surface, if available.
  virtual GLSurface::Format GetFormat();

  // Get access to a helper providing time of recent refresh and period
  // of screen refresh. If unavailable, returns NULL.
  virtual gfx::VSyncProvider* GetVSyncProvider();

  // Schedule an overlay plane to be shown at swap time, or on the next
  // CommitOverlayPlanes call.
  // |z_order| specifies the stacking order of the plane relative to the
  // main framebuffer located at index 0. For the case where there is no
  // main framebuffer, overlays may be scheduled at 0, taking its place.
  // |transform| specifies how the buffer is to be transformed during
  // composition.
  // |image| to be presented by the overlay.
  // |bounds_rect| specify where it is supposed to be on the screen in pixels.
  // |crop_rect| specifies the region within the buffer to be placed inside
  // |bounds_rect|.
  virtual bool ScheduleOverlayPlane(int z_order,
                                    gfx::OverlayTransform transform,
                                    GLImage* image,
                                    const gfx::Rect& bounds_rect,
                                    const gfx::RectF& crop_rect);

  // Schedule a CALayer to be shown at swap time.
  // All arguments correspond to their CALayer properties.
  virtual bool ScheduleCALayer(const ui::CARendererLayerParams& params);

  struct GL_EXPORT CALayerInUseQuery {
    CALayerInUseQuery();
    explicit CALayerInUseQuery(const CALayerInUseQuery&);
    ~CALayerInUseQuery();
    unsigned texture = 0;
    scoped_refptr<GLImage> image;
  };
  virtual void ScheduleCALayerInUseQuery(
      std::vector<CALayerInUseQuery> queries);

  virtual bool IsSurfaceless() const;

  virtual bool FlipsVertically() const;

  // Returns true if SwapBuffers or PostSubBuffers causes a flip, such that
  // the next buffer may be 2 frames old.
  virtual bool BuffersFlipped() const;

  static GLSurface* GetCurrent();

  // Called when the swap interval for the associated context changes.
  virtual void OnSetSwapInterval(int interval);

 protected:
  virtual ~GLSurface();

  static void SetCurrent(GLSurface* surface);

  static bool ExtensionsContain(const char* extensions, const char* name);

 private:
  friend class base::RefCounted<GLSurface>;
  friend class GLContext;

  DISALLOW_COPY_AND_ASSIGN(GLSurface);
};

// Implementation of GLSurface that forwards all calls through to another
// GLSurface.
class GL_EXPORT GLSurfaceAdapter : public GLSurface {
 public:
  explicit GLSurfaceAdapter(GLSurface* surface);

  bool Initialize(GLSurface::Format format) override;
  void Destroy() override;
  bool Resize(const gfx::Size& size,
              float scale_factor,
              bool has_alpha) override;
  bool Recreate() override;
  bool DeferDraws() override;
  bool IsOffscreen() override;
  gfx::SwapResult SwapBuffers() override;
  void SwapBuffersAsync(const SwapCompletionCallback& callback) override;
  gfx::SwapResult SwapBuffersWithDamage(int x,
                                        int y,
                                        int width,
                                        int height) override;
  gfx::SwapResult PostSubBuffer(int x, int y, int width, int height) override;
  void PostSubBufferAsync(int x,
                          int y,
                          int width,
                          int height,
                          const SwapCompletionCallback& callback) override;
  gfx::SwapResult CommitOverlayPlanes() override;
  void CommitOverlayPlanesAsync(
      const SwapCompletionCallback& callback) override;
  bool SupportsSwapBuffersWithDamage() override;
  bool SupportsPostSubBuffer() override;
  bool SupportsCommitOverlayPlanes() override;
  bool SupportsAsyncSwap() override;
  gfx::Size GetSize() override;
  void* GetHandle() override;
  unsigned int GetBackingFramebufferObject() override;
  bool OnMakeCurrent(GLContext* context) override;
  bool SetBackbufferAllocation(bool allocated) override;
  void SetFrontbufferAllocation(bool allocated) override;
  void* GetShareHandle() override;
  void* GetDisplay() override;
  void* GetConfig() override;
  unsigned long GetCompatibilityKey() override;
  GLSurface::Format GetFormat() override;
  gfx::VSyncProvider* GetVSyncProvider() override;
  bool ScheduleOverlayPlane(int z_order,
                            gfx::OverlayTransform transform,
                            GLImage* image,
                            const gfx::Rect& bounds_rect,
                            const gfx::RectF& crop_rect) override;
  bool IsSurfaceless() const override;
  bool FlipsVertically() const override;
  bool BuffersFlipped() const override;

  GLSurface* surface() const { return surface_.get(); }

 protected:
  ~GLSurfaceAdapter() override;

 private:
  scoped_refptr<GLSurface> surface_;

  DISALLOW_COPY_AND_ASSIGN(GLSurfaceAdapter);
};

// Wraps GLSurface in scoped_refptr and tries to initializes it. Returns a
// scoped_refptr containing the initialized GLSurface or nullptr if
// initialization fails.
GL_EXPORT scoped_refptr<GLSurface> InitializeGLSurface(
    scoped_refptr<GLSurface> surface);

}  // namespace gl

#endif  // UI_GL_GL_SURFACE_H_
