// Copyright 2014 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 CONTENT_BROWSER_COMPOSITOR_REFLECTOR_IMPL_H_
#define CONTENT_BROWSER_COMPOSITOR_REFLECTOR_IMPL_H_

#include "base/id_map.h"
#include "base/memory/scoped_ptr.h"
#include "base/memory/weak_ptr.h"
#include "base/synchronization/lock.h"
#include "content/browser/compositor/image_transport_factory.h"
#include "gpu/command_buffer/common/mailbox_holder.h"
#include "ui/compositor/reflector.h"
#include "ui/gfx/size.h"

namespace base { class MessageLoopProxy; }

namespace gfx { class Rect; }

namespace ui {
class Compositor;
class Layer;
}

namespace content {

class OwnedMailbox;
class BrowserCompositorOutputSurface;

// A reflector implementation that copies the framebuffer content
// to the texture, then draw it onto the mirroring compositor.
class ReflectorImpl : public base::SupportsWeakPtr<ReflectorImpl>,
                      public ui::Reflector {
 public:
  ReflectorImpl(
      ui::Compositor* mirrored_compositor,
      ui::Layer* mirroring_layer,
      IDMap<BrowserCompositorOutputSurface>* output_surface_map,
      base::MessageLoopProxy* compositor_thread_loop,
      int surface_id);

  ui::Compositor* mirrored_compositor() {
    return GetMain().mirrored_compositor;
  }

  void InitOnImplThread(const gpu::MailboxHolder& mailbox_holder);
  void Shutdown();
  void ShutdownOnImplThread();

  // Post a task to attach the reflector to the output surface onto ImplThread.
  void ReattachToOutputSurfaceFromMainThread(
      BrowserCompositorOutputSurface* surface);

  // ui::Reflector implementation.
  virtual void OnMirroringCompositorResized() OVERRIDE;

  // Called in |BrowserCompositorOutputSurface::SwapBuffers| to copy
  // the full screen image to the |texture_id_|. This must be called
  // on ImplThread.
  void OnSwapBuffers();

  // Called in |BrowserCompositorOutputSurface::PostSubBuffer| copy
  // the sub image given by |rect| to the texture.This must be called
  // on ImplThread.
  void OnPostSubBuffer(gfx::Rect rect);

  // Create a shared texture that will be used to copy the content of
  // mirrored compositor to the mirroring compositor.  This should
  // be posted to the main thread when the output is attached in
  // impl thread.
  void CreateSharedTextureOnMainThread(gfx::Size size);

  // Called when the source surface is bound and available. This must
  // be called on ImplThread.
  void OnSourceSurfaceReady(BrowserCompositorOutputSurface* surface);

  void DetachFromOutputSurface();

 private:
  struct MainThreadData {
    MainThreadData(ui::Compositor* mirrored_compositor,
                   ui::Layer* mirroring_layer);
    ~MainThreadData();
    scoped_refptr<OwnedMailbox> mailbox;
    bool needs_set_mailbox;
    ui::Compositor* mirrored_compositor;
    ui::Layer* mirroring_layer;
  };

  struct ImplThreadData {
    explicit ImplThreadData(
        IDMap<BrowserCompositorOutputSurface>* output_surface_map);
    ~ImplThreadData();
    IDMap<BrowserCompositorOutputSurface>* output_surface_map;
    BrowserCompositorOutputSurface* output_surface;
    scoped_ptr<GLHelper> gl_helper;
    unsigned texture_id;
    gpu::MailboxHolder mailbox_holder;
  };

  virtual ~ReflectorImpl();

  void AttachToOutputSurfaceOnImplThread(
      const gpu::MailboxHolder& mailbox_holder,
      BrowserCompositorOutputSurface* surface);

  void UpdateTextureSizeOnMainThread(gfx::Size size);

  // Request full redraw on mirroring compositor.
  void FullRedrawOnMainThread(gfx::Size size);

  void UpdateSubBufferOnMainThread(gfx::Size size, gfx::Rect rect);

  // Request full redraw on mirrored compositor so that
  // the full content will be copied to mirroring compositor.
  void FullRedrawContentOnMainThread();

  // This exists just to hold a reference to a ReflectorImpl in a post task,
  // so the ReflectorImpl gets deleted when the function returns.
  static void DeleteOnMainThread(scoped_refptr<ReflectorImpl> reflector) {}

  MainThreadData& GetMain();
  ImplThreadData& GetImpl();

  // Must be accessed only on ImplThread, through GetImpl().
  ImplThreadData impl_unsafe_;

  // Must be accessed only on MainThread, through GetMain().
  MainThreadData main_unsafe_;

  // Can be accessed on both.
  scoped_refptr<base::MessageLoopProxy> impl_message_loop_;
  scoped_refptr<base::MessageLoopProxy> main_message_loop_;
  int surface_id_;
};

}  // namespace content

#endif  // CONTENT_BROWSER_COMPOSITOR_REFLECTOR_IMPL_H_