#include "android_webview/browser/test/fake_window.h"

#include "android_webview/browser/browser_view_renderer.h"
#include "android_webview/public/browser/draw_gl.h"
#include "base/message_loop/message_loop_proxy.h"
#include "base/synchronization/waitable_event.h"
#include "base/threading/thread.h"
#include "ui/gl/gl_bindings.h"

namespace android_webview {

class FakeWindow::ScopedMakeCurrent {
 public:
  ScopedMakeCurrent(FakeWindow* view_root) : view_root_(view_root) {
    DCHECK(!view_root_->context_current_);
    view_root_->context_current_ = true;
    bool result = view_root_->context_->MakeCurrent(view_root_->surface_.get());
    DCHECK(result);
  };

  ~ScopedMakeCurrent() {
    DCHECK(view_root_->context_current_);
    view_root_->context_current_ = false;

    eglMakeCurrent(view_root_->surface_->GetDisplay(), EGL_NO_SURFACE,
                   EGL_NO_SURFACE, EGL_NO_CONTEXT);
    view_root_->context_->ReleaseCurrent(view_root_->surface_.get());
  }

 private:
  FakeWindow* view_root_;
};

FakeWindow::FakeWindow(BrowserViewRenderer* view,
                       WindowHooks* hooks,
                       gfx::Rect location)
    : view_(view),
      hooks_(hooks),
      surface_size_(100, 100),
      location_(location),
      on_draw_hardware_pending_(false),
      functor_(nullptr),
      context_current_(false),
      weak_ptr_factory_(this) {
  CheckCurrentlyOnUIThread();
  DCHECK(view_);
  view_->OnAttachedToWindow(location_.width(), location_.height());
  view_->SetWindowVisibility(true);
  view_->SetViewVisibility(true);
}

FakeWindow::~FakeWindow() {
  CheckCurrentlyOnUIThread();
}

void FakeWindow::Detach() {
  CheckCurrentlyOnUIThread();
  view_->OnDetachedFromWindow();

  if (render_thread_loop_) {
    base::WaitableEvent completion(true, false);
    render_thread_loop_->PostTask(
        FROM_HERE, base::Bind(&FakeWindow::DestroyOnRT, base::Unretained(this),
                              &completion));
    completion.Wait();
  }

  render_thread_.reset();
  functor_ = nullptr;
}

void FakeWindow::RequestDrawGL(bool wait_for_completion) {
  CheckCurrentlyOnUIThread();
  base::WaitableEvent completion(true, false);
  render_thread_loop_->PostTask(
      FROM_HERE,
      base::Bind(&FakeWindow::ProcessFunctorOnRT, base::Unretained(this),
                 wait_for_completion ? &completion : nullptr));
  if (wait_for_completion)
    completion.Wait();
}

void FakeWindow::ProcessFunctorOnRT(base::WaitableEvent* sync) {
  CheckCurrentlyOnRT();
  AwDrawGLInfo process_info;
  process_info.version = kAwDrawGLInfoVersion;
  process_info.mode = AwDrawGLInfo::kModeProcess;

  hooks_->WillProcessOnRT(functor_);
  {
    ScopedMakeCurrent make_current(this);
    functor_->DrawGL(&process_info);
  }
  hooks_->DidProcessOnRT(functor_);

  if (sync)
    sync->Signal();
}

void FakeWindow::PostInvalidate() {
  CheckCurrentlyOnUIThread();
  if (on_draw_hardware_pending_)
    return;
  on_draw_hardware_pending_ = true;
  base::MessageLoopProxy::current()->PostTask(
      FROM_HERE,
      base::Bind(&FakeWindow::OnDrawHardware, weak_ptr_factory_.GetWeakPtr()));
}

void FakeWindow::OnDrawHardware() {
  CheckCurrentlyOnUIThread();
  DCHECK(on_draw_hardware_pending_);
  on_draw_hardware_pending_ = false;

  hooks_->WillOnDraw();
  bool success = view_->OnDrawHardware();
  hooks_->DidOnDraw(success);
  if (success) {
    CreateRenderThreadIfNeeded();

    base::WaitableEvent completion(true, false);
    render_thread_loop_->PostTask(
        FROM_HERE, base::Bind(&FakeWindow::DrawFunctorOnRT,
                              base::Unretained(this), &completion));
    completion.Wait();
  }
}

void FakeWindow::DrawFunctorOnRT(base::WaitableEvent* sync) {
  CheckCurrentlyOnRT();

  gfx::Rect location = location_;
  {
    AwDrawGLInfo process_info;
    process_info.version = kAwDrawGLInfoVersion;
    process_info.mode = AwDrawGLInfo::kModeSync;

    hooks_->WillSyncOnRT(functor_);
    functor_->DrawGL(&process_info);
    hooks_->DidSyncOnRT(functor_);
  }
  sync->Signal();

  AwDrawGLInfo draw_info;
  draw_info.version = kAwDrawGLInfoVersion;
  draw_info.mode = AwDrawGLInfo::kModeDraw;
  draw_info.clip_left = location.x();
  draw_info.clip_top = location.y();
  draw_info.clip_right = location.x() + location.width();
  draw_info.clip_bottom = location.y() + location.height();
  draw_info.width = surface_size_.width();
  draw_info.height = surface_size_.height();
  draw_info.is_layer = false;

  draw_info.transform[0] = 1.0;
  draw_info.transform[1] = 0.0;
  draw_info.transform[2] = 0.0;
  draw_info.transform[3] = 0.0;

  draw_info.transform[4] = 0.0;
  draw_info.transform[5] = 1.0;
  draw_info.transform[6] = 0.0;
  draw_info.transform[7] = 0.0;

  draw_info.transform[8] = 0.0;
  draw_info.transform[9] = 0.0;
  draw_info.transform[10] = 1.0;
  draw_info.transform[11] = 0.0;

  draw_info.transform[12] = 0.0;
  draw_info.transform[13] = 0.0;
  draw_info.transform[14] = 0.0;
  draw_info.transform[15] = 1.0;

  hooks_->WillDrawOnRT(functor_);
  {
    ScopedMakeCurrent make_current(this);
    functor_->DrawGL(&draw_info);
  }
  hooks_->DidDrawOnRT(functor_);
}

void FakeWindow::CheckCurrentlyOnUIThread() {
  DCHECK(ui_checker_.CalledOnValidSequencedThread());
}

void FakeWindow::CreateRenderThreadIfNeeded() {
  CheckCurrentlyOnUIThread();
  if (functor_) {
    DCHECK(render_thread_.get());
    DCHECK(render_thread_loop_.get());
    return;
  }
  functor_ = view_->GetAwDrawGLViewContext();
  render_thread_.reset(new base::Thread("TestRenderThread"));
  render_thread_->Start();
  render_thread_loop_ = render_thread_->task_runner();
  rt_checker_.DetachFromSequence();

  base::WaitableEvent completion(true, false);
  render_thread_loop_->PostTask(
      FROM_HERE, base::Bind(&FakeWindow::InitializeOnRT, base::Unretained(this),
                            &completion));
  completion.Wait();
}

void FakeWindow::InitializeOnRT(base::WaitableEvent* sync) {
  CheckCurrentlyOnRT();
  surface_ = gfx::GLSurface::CreateOffscreenGLSurface(surface_size_);
  DCHECK(surface_.get());
  DCHECK(surface_->GetHandle());
  context_ = gfx::GLContext::CreateGLContext(nullptr, surface_.get(),
                                             gfx::PreferDiscreteGpu);
  DCHECK(context_.get());
  sync->Signal();
}

void FakeWindow::DestroyOnRT(base::WaitableEvent* sync) {
  CheckCurrentlyOnRT();
  if (context_) {
    DCHECK(!context_->IsCurrent(surface_.get()));
    context_ = nullptr;
    surface_ = nullptr;
  }
  sync->Signal();
}

void FakeWindow::CheckCurrentlyOnRT() {
  DCHECK(rt_checker_.CalledOnValidSequencedThread());
}

}
// 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.

#include "android_webview/browser/test/fake_window.h"

#include "android_webview/browser/browser_view_renderer.h"
#include "android_webview/public/browser/draw_gl.h"
#include "base/message_loop/message_loop_proxy.h"
#include "base/synchronization/waitable_event.h"
#include "base/threading/thread.h"
#include "ui/gl/gl_bindings.h"

namespace android_webview {

class FakeWindow::ScopedMakeCurrent {
 public:
  ScopedMakeCurrent(FakeWindow* view_root) : view_root_(view_root) {
    DCHECK(!view_root_->context_current_);
    view_root_->context_current_ = true;
    bool result = view_root_->context_->MakeCurrent(view_root_->surface_.get());
    DCHECK(result);
  };

  ~ScopedMakeCurrent() {
    DCHECK(view_root_->context_current_);
    view_root_->context_current_ = false;

    // Release the underlying EGLContext. This is required because the real
    // GLContextEGL may no longer be current here and to satisfy DCHECK in
    // GLContextEGL::IsCurrent.
    eglMakeCurrent(view_root_->surface_->GetDisplay(), EGL_NO_SURFACE,
                   EGL_NO_SURFACE, EGL_NO_CONTEXT);
    view_root_->context_->ReleaseCurrent(view_root_->surface_.get());
  }

 private:
  FakeWindow* view_root_;
};

FakeWindow::FakeWindow(BrowserViewRenderer* view,
                       WindowHooks* hooks,
                       gfx::Rect location)
    : view_(view),
      hooks_(hooks),
      surface_size_(100, 100),
      location_(location),
      on_draw_hardware_pending_(false),
      functor_(nullptr),
      context_current_(false),
      weak_ptr_factory_(this) {
  CheckCurrentlyOnUIThread();
  DCHECK(view_);
  view_->OnAttachedToWindow(location_.width(), location_.height());
  view_->SetWindowVisibility(true);
  view_->SetViewVisibility(true);
}

FakeWindow::~FakeWindow() {
  CheckCurrentlyOnUIThread();
}

void FakeWindow::Detach() {
  CheckCurrentlyOnUIThread();
  view_->OnDetachedFromWindow();

  if (render_thread_loop_) {
    base::WaitableEvent completion(true, false);
    render_thread_loop_->PostTask(
        FROM_HERE, base::Bind(&FakeWindow::DestroyOnRT, base::Unretained(this),
                              &completion));
    completion.Wait();
  }

  render_thread_.reset();
  functor_ = nullptr;
}

void FakeWindow::RequestDrawGL(bool wait_for_completion) {
  CheckCurrentlyOnUIThread();
  base::WaitableEvent completion(true, false);
  render_thread_loop_->PostTask(
      FROM_HERE,
      base::Bind(&FakeWindow::ProcessFunctorOnRT, base::Unretained(this),
                 wait_for_completion ? &completion : nullptr));
  if (wait_for_completion)
    completion.Wait();
}

void FakeWindow::ProcessFunctorOnRT(base::WaitableEvent* sync) {
  CheckCurrentlyOnRT();
  AwDrawGLInfo process_info;
  process_info.version = kAwDrawGLInfoVersion;
  process_info.mode = AwDrawGLInfo::kModeProcess;

  hooks_->WillProcessOnRT(functor_);
  {
    ScopedMakeCurrent make_current(this);
    functor_->DrawGL(&process_info);
  }
  hooks_->DidProcessOnRT(functor_);

  if (sync)
    sync->Signal();
}

void FakeWindow::PostInvalidate() {
  CheckCurrentlyOnUIThread();
  if (on_draw_hardware_pending_)
    return;
  on_draw_hardware_pending_ = true;
  base::MessageLoopProxy::current()->PostTask(
      FROM_HERE,
      base::Bind(&FakeWindow::OnDrawHardware, weak_ptr_factory_.GetWeakPtr()));
}

void FakeWindow::OnDrawHardware() {
  CheckCurrentlyOnUIThread();
  DCHECK(on_draw_hardware_pending_);
  on_draw_hardware_pending_ = false;

  hooks_->WillOnDraw();
  bool success = view_->OnDrawHardware();
  hooks_->DidOnDraw(success);
  if (success) {
    CreateRenderThreadIfNeeded();

    base::WaitableEvent completion(true, false);
    render_thread_loop_->PostTask(
        FROM_HERE, base::Bind(&FakeWindow::DrawFunctorOnRT,
                              base::Unretained(this), &completion));
    completion.Wait();
  }
}

void FakeWindow::DrawFunctorOnRT(base::WaitableEvent* sync) {
  CheckCurrentlyOnRT();
  // Ok to access UI functions until sync is signalled.
  gfx::Rect location = location_;
  {
    AwDrawGLInfo process_info;
    process_info.version = kAwDrawGLInfoVersion;
    process_info.mode = AwDrawGLInfo::kModeSync;

    hooks_->WillSyncOnRT(functor_);
    functor_->DrawGL(&process_info);
    hooks_->DidSyncOnRT(functor_);
  }
  sync->Signal();

  AwDrawGLInfo draw_info;
  draw_info.version = kAwDrawGLInfoVersion;
  draw_info.mode = AwDrawGLInfo::kModeDraw;
  draw_info.clip_left = location.x();
  draw_info.clip_top = location.y();
  draw_info.clip_right = location.x() + location.width();
  draw_info.clip_bottom = location.y() + location.height();
  draw_info.width = surface_size_.width();
  draw_info.height = surface_size_.height();
  draw_info.is_layer = false;

  draw_info.transform[0] = 1.0;
  draw_info.transform[1] = 0.0;
  draw_info.transform[2] = 0.0;
  draw_info.transform[3] = 0.0;

  draw_info.transform[4] = 0.0;
  draw_info.transform[5] = 1.0;
  draw_info.transform[6] = 0.0;
  draw_info.transform[7] = 0.0;

  draw_info.transform[8] = 0.0;
  draw_info.transform[9] = 0.0;
  draw_info.transform[10] = 1.0;
  draw_info.transform[11] = 0.0;

  draw_info.transform[12] = 0.0;
  draw_info.transform[13] = 0.0;
  draw_info.transform[14] = 0.0;
  draw_info.transform[15] = 1.0;

  hooks_->WillDrawOnRT(functor_);
  {
    ScopedMakeCurrent make_current(this);
    functor_->DrawGL(&draw_info);
  }
  hooks_->DidDrawOnRT(functor_);
}

void FakeWindow::CheckCurrentlyOnUIThread() {
  DCHECK(ui_checker_.CalledOnValidSequencedThread());
}

void FakeWindow::CreateRenderThreadIfNeeded() {
  CheckCurrentlyOnUIThread();
  if (functor_) {
    DCHECK(render_thread_.get());
    DCHECK(render_thread_loop_.get());
    return;
  }
  functor_ = view_->GetAwDrawGLViewContext();
  render_thread_.reset(new base::Thread("TestRenderThread"));
  render_thread_->Start();
  render_thread_loop_ = render_thread_->task_runner();
  rt_checker_.DetachFromSequence();

  base::WaitableEvent completion(true, false);
  render_thread_loop_->PostTask(
      FROM_HERE, base::Bind(&FakeWindow::InitializeOnRT, base::Unretained(this),
                            &completion));
  completion.Wait();
}

void FakeWindow::InitializeOnRT(base::WaitableEvent* sync) {
  CheckCurrentlyOnRT();
  surface_ = gfx::GLSurface::CreateOffscreenGLSurface(surface_size_);
  DCHECK(surface_.get());
  DCHECK(surface_->GetHandle());
  context_ = gfx::GLContext::CreateGLContext(nullptr, surface_.get(),
                                             gfx::PreferDiscreteGpu);
  DCHECK(context_.get());
  sync->Signal();
}

void FakeWindow::DestroyOnRT(base::WaitableEvent* sync) {
  CheckCurrentlyOnRT();
  if (context_) {
    DCHECK(!context_->IsCurrent(surface_.get()));
    context_ = nullptr;
    surface_ = nullptr;
  }
  sync->Signal();
}

void FakeWindow::CheckCurrentlyOnRT() {
  DCHECK(rt_checker_.CalledOnValidSequencedThread());
}

}  // namespace android_webview
