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

#include "base/memory/scoped_ptr.h"
#include "base/memory/weak_ptr.h"
#include "content/browser/gamepad/gamepad_data_fetcher.h"
#include "content/browser/gamepad/gamepad_provider.h"
#include "content/browser/gamepad/gamepad_test_helpers.h"
#include "content/common/gamepad_hardware_buffer.h"
#include "content/common/gamepad_messages.h"
#include "testing/gtest/include/gtest/gtest.h"

namespace content {

namespace {

using blink::WebGamepads;

// Helper class to generate and record user gesture callbacks.
class UserGestureListener {
 public:
  UserGestureListener()
      : weak_factory_(this),
        has_user_gesture_(false) {
  }

  base::Closure GetClosure() {
    return base::Bind(&UserGestureListener::GotUserGesture,
                      weak_factory_.GetWeakPtr());
  }

  bool has_user_gesture() const { return has_user_gesture_; }

 private:
  void GotUserGesture() {
    has_user_gesture_ = true;
  }

  base::WeakPtrFactory<UserGestureListener> weak_factory_;
  bool has_user_gesture_;
};

// Main test fixture
class GamepadProviderTest : public testing::Test, public GamepadTestHelper {
 public:
  GamepadProvider* CreateProvider(const WebGamepads& test_data) {
    mock_data_fetcher_ = new MockGamepadDataFetcher(test_data);
    provider_.reset(new GamepadProvider(
        scoped_ptr<GamepadDataFetcher>(mock_data_fetcher_)));
    return provider_.get();
  }

 protected:
  GamepadProviderTest() {
  }

  scoped_ptr<GamepadProvider> provider_;

  // Pointer owned by the provider.
  MockGamepadDataFetcher* mock_data_fetcher_;

  DISALLOW_COPY_AND_ASSIGN(GamepadProviderTest);
};

// Crashes. http://crbug.com/106163
TEST_F(GamepadProviderTest, PollingAccess) {
  WebGamepads test_data;
  test_data.length = 1;
  test_data.items[0].connected = true;
  test_data.items[0].timestamp = 0;
  test_data.items[0].buttonsLength = 1;
  test_data.items[0].axesLength = 2;
  test_data.items[0].buttons[0].value = 1.f;
  test_data.items[0].buttons[0].pressed = true;
  test_data.items[0].axes[0] = -1.f;
  test_data.items[0].axes[1] = .5f;

  GamepadProvider* provider = CreateProvider(test_data);
  provider->Resume();

  message_loop().RunUntilIdle();

  mock_data_fetcher_->WaitForDataRead();

  // Renderer-side, pull data out of poll buffer.
  base::SharedMemoryHandle handle = provider->GetSharedMemoryHandleForProcess(
      base::GetCurrentProcessHandle());
  scoped_ptr<base::SharedMemory> shared_memory(
      new base::SharedMemory(handle, true));
  EXPECT_TRUE(shared_memory->Map(sizeof(GamepadHardwareBuffer)));
  void* mem = shared_memory->memory();

  GamepadHardwareBuffer* hwbuf = static_cast<GamepadHardwareBuffer*>(mem);
  // See gamepad_hardware_buffer.h for details on the read discipline.
  WebGamepads output;

  base::subtle::Atomic32 version;
  do {
    version = hwbuf->sequence.ReadBegin();
    memcpy(&output, &hwbuf->buffer, sizeof(output));
  } while (hwbuf->sequence.ReadRetry(version));

  EXPECT_EQ(1u, output.length);
  EXPECT_EQ(1u, output.items[0].buttonsLength);
  EXPECT_EQ(1.f, output.items[0].buttons[0].value);
  EXPECT_EQ(true, output.items[0].buttons[0].pressed);
  EXPECT_EQ(2u, output.items[0].axesLength);
  EXPECT_EQ(-1.f, output.items[0].axes[0]);
  EXPECT_EQ(0.5f, output.items[0].axes[1]);
}

// Tests that waiting for a user gesture works properly.
TEST_F(GamepadProviderTest, UserGesture) {
  WebGamepads no_button_data;
  no_button_data.length = 1;
  no_button_data.items[0].connected = true;
  no_button_data.items[0].timestamp = 0;
  no_button_data.items[0].buttonsLength = 1;
  no_button_data.items[0].axesLength = 2;
  no_button_data.items[0].buttons[0].value = 0.f;
  no_button_data.items[0].buttons[0].pressed = false;
  no_button_data.items[0].axes[0] = -1.f;
  no_button_data.items[0].axes[1] = .5f;

  WebGamepads button_down_data = no_button_data;
  button_down_data.items[0].buttons[0].value = 1.f;
  button_down_data.items[0].buttons[0].pressed = true;

  UserGestureListener listener;
  GamepadProvider* provider = CreateProvider(no_button_data);
  provider->Resume();

  // Register for a user gesture and make sure the provider reads it twice
  // see below for why).
  provider->RegisterForUserGesture(listener.GetClosure());
  mock_data_fetcher_->WaitForDataRead();
  mock_data_fetcher_->WaitForDataRead();

  // It should not have issued our callback.
  message_loop().RunUntilIdle();
  EXPECT_FALSE(listener.has_user_gesture());

  // Set a button down and wait for it to be read twice.
  //
  // We wait for two reads before calling RunAllPending because the provider
  // will read the data on the background thread (setting the event) and *then*
  // will issue the callback on our thread. Waiting for it to read twice
  // ensures that it was able to issue callbacks for the first read (if it
  // issued one) before we try to check for it.
  mock_data_fetcher_->SetTestData(button_down_data);
  mock_data_fetcher_->WaitForDataRead();
  mock_data_fetcher_->WaitForDataRead();

  // It should have issued our callback.
  message_loop().RunUntilIdle();
  EXPECT_TRUE(listener.has_user_gesture());
}

}  // namespace

}  // namespace content