// Copyright 2013 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 "ash/shelf/shelf_window_watcher.h"

#include "ash/ash_switches.h"
#include "ash/shelf/shelf_item_types.h"
#include "ash/shelf/shelf_model.h"
#include "ash/shelf/shelf_util.h"
#include "ash/shell.h"
#include "ash/shell_window_ids.h"
#include "ash/test/ash_test_base.h"
#include "ash/test/shell_test_api.h"
#include "ash/wm/window_resizer.h"
#include "ash/wm/window_state.h"
#include "ash/wm/window_util.h"
#include "base/command_line.h"
#include "ui/aura/client/aura_constants.h"
#include "ui/aura/window.h"
#include "ui/base/hit_test.h"

namespace ash {

class ShelfWindowWatcherTest : public test::AshTestBase {
 public:
  ShelfWindowWatcherTest() : model_(NULL) {}
  virtual ~ShelfWindowWatcherTest() {}

  virtual void SetUp() OVERRIDE {
    test::AshTestBase::SetUp();
    model_ = test::ShellTestApi(Shell::GetInstance()).shelf_model();
  }

  virtual void TearDown() OVERRIDE {
    model_ = NULL;
    test::AshTestBase::TearDown();
  }

  ShelfID CreateShelfItem(aura::Window* window) {
    ShelfID id = model_->next_id();
    ShelfItemDetails item_details;
    item_details.type = TYPE_PLATFORM_APP;
    SetShelfItemDetailsForWindow(window, item_details);
    return id;
  }

 protected:
  ShelfModel* model_;

 private:
  DISALLOW_COPY_AND_ASSIGN(ShelfWindowWatcherTest);
};

TEST_F(ShelfWindowWatcherTest, CreateAndRemoveShelfItem) {
  // ShelfModel only has an APP_LIST item.
  EXPECT_EQ(1, model_->item_count());

  scoped_ptr<aura::Window> w1(CreateTestWindowInShellWithId(0));
  scoped_ptr<aura::Window> w2(CreateTestWindowInShellWithId(0));

  // Create a ShelfItem for w1.
  ShelfID id_w1 = CreateShelfItem(w1.get());
  EXPECT_EQ(2, model_->item_count());

  int index_w1 = model_->ItemIndexByID(id_w1);
  EXPECT_EQ(STATUS_RUNNING, model_->items()[index_w1].status);

  // Create a ShelfItem for w2.
  ShelfID id_w2 = CreateShelfItem(w2.get());
  EXPECT_EQ(3, model_->item_count());

  int index_w2 = model_->ItemIndexByID(id_w2);
  EXPECT_EQ(STATUS_RUNNING, model_->items()[index_w2].status);

  // ShelfItem is removed when assoicated window is destroyed.
  ClearShelfItemDetailsForWindow(w1.get());
  EXPECT_EQ(2, model_->item_count());
  ClearShelfItemDetailsForWindow(w2.get());
  EXPECT_EQ(1, model_->item_count());
  // Clears twice doesn't do anything.
  ClearShelfItemDetailsForWindow(w2.get());
  EXPECT_EQ(1, model_->item_count());

}

TEST_F(ShelfWindowWatcherTest, ActivateWindow) {
  // ShelfModel only have APP_LIST item.
  EXPECT_EQ(1, model_->item_count());
  scoped_ptr<aura::Window> w1(CreateTestWindowInShellWithId(0));
  scoped_ptr<aura::Window> w2(CreateTestWindowInShellWithId(0));

  // Create a ShelfItem for w1.
  ShelfID id_w1 = CreateShelfItem(w1.get());
  EXPECT_EQ(2, model_->item_count());
  int index_w1 = model_->ItemIndexByID(id_w1);
  EXPECT_EQ(STATUS_RUNNING, model_->items()[index_w1].status);

  // Create a ShelfItem for w2.
  ShelfID id_w2 = CreateShelfItem(w2.get());
  EXPECT_EQ(3, model_->item_count());
  int index_w2 = model_->ItemIndexByID(id_w2);
  EXPECT_EQ(STATUS_RUNNING, model_->items()[index_w1].status);
  EXPECT_EQ(STATUS_RUNNING, model_->items()[index_w2].status);

  // ShelfItem for w1 is active when w1 is activated.
  wm::ActivateWindow(w1.get());
  EXPECT_EQ(STATUS_ACTIVE, model_->items()[index_w1].status);

  // ShelfItem for w2 is active state when w2 is activated.
  wm::ActivateWindow(w2.get());
  EXPECT_EQ(STATUS_RUNNING, model_->items()[index_w1].status);
  EXPECT_EQ(STATUS_ACTIVE, model_->items()[index_w2].status);
}

TEST_F(ShelfWindowWatcherTest, UpdateWindowProperty) {
  // ShelfModel only has an APP_LIST item.
  EXPECT_EQ(1, model_->item_count());

  scoped_ptr<aura::Window> window(CreateTestWindowInShellWithId(0));

  // Create a ShelfItem for |window|.
  ShelfID id = CreateShelfItem(window.get());
  EXPECT_EQ(2, model_->item_count());

  int index = model_->ItemIndexByID(id);
  EXPECT_EQ(STATUS_RUNNING, model_->items()[index].status);

  // Update ShelfItem for |window|.
  ShelfItemDetails details;
  details.type = TYPE_PLATFORM_APP;

  SetShelfItemDetailsForWindow(window.get(), details);
  // No new item is created after updating a launcher item.
  EXPECT_EQ(2, model_->item_count());
  // index and id are not changed after updating a launcher item.
  EXPECT_EQ(index, model_->ItemIndexByID(id));
  EXPECT_EQ(id, model_->items()[index].id);
}

TEST_F(ShelfWindowWatcherTest, MaximizeAndRestoreWindow) {
  // ShelfModel only has an APP_LIST item.
  EXPECT_EQ(1, model_->item_count());

  scoped_ptr<aura::Window> window(CreateTestWindowInShellWithId(0));
  wm::WindowState* window_state = wm::GetWindowState(window.get());

  // Create a ShelfItem for |window|.
  ShelfID id = CreateShelfItem(window.get());
  EXPECT_EQ(2, model_->item_count());

  int index = model_->ItemIndexByID(id);
  EXPECT_EQ(STATUS_RUNNING, model_->items()[index].status);

  // Maximize window |window|.
  EXPECT_FALSE(window_state->IsMaximized());
  window_state->Maximize();
  EXPECT_TRUE(window_state->IsMaximized());
  // No new item is created after maximizing a window |window|.
  EXPECT_EQ(2, model_->item_count());
  // index and id are not changed after maximizing a window |window|.
  EXPECT_EQ(index, model_->ItemIndexByID(id));
  EXPECT_EQ(id, model_->items()[index].id);

  // Restore window |window|.
  window_state->Restore();
  EXPECT_FALSE(window_state->IsMaximized());
  // No new item is created after restoring a window |window|.
  EXPECT_EQ(2, model_->item_count());
  // Index and id are not changed after maximizing a window |window|.
  EXPECT_EQ(index, model_->ItemIndexByID(id));
  EXPECT_EQ(id, model_->items()[index].id);
}

// Check that an item is removed when its associated Window is re-parented.
TEST_F(ShelfWindowWatcherTest, ReparentWindow) {
  // ShelfModel only has an APP_LIST item.
  EXPECT_EQ(1, model_->item_count());

  scoped_ptr<aura::Window> window(CreateTestWindowInShellWithId(0));
  window->set_owned_by_parent(false);

  // Create a ShelfItem for |window|.
  ShelfID id = CreateShelfItem(window.get());
  EXPECT_EQ(2, model_->item_count());

  int index = model_->ItemIndexByID(id);
  EXPECT_EQ(STATUS_RUNNING, model_->items()[index].status);

  aura::Window* root_window = window->GetRootWindow();
  aura::Window* default_container = Shell::GetContainer(
      root_window,
      kShellWindowId_DefaultContainer);
  EXPECT_EQ(default_container, window->parent());

  aura::Window* new_parent = Shell::GetContainer(
      root_window,
      kShellWindowId_PanelContainer);

  // Check |window|'s item is removed when it is re-parented to |new_parent|
  // which is not default container.
  new_parent->AddChild(window.get());
  EXPECT_EQ(1, model_->item_count());

  // Check |window|'s item is added when it is re-parented to
  // |default_container|.
  default_container->AddChild(window.get());
  EXPECT_EQ(2, model_->item_count());
}

// Check |window|'s item is not changed during the dragging.
// TODO(simonhong): Add a test for removing a Window during the dragging.
TEST_F(ShelfWindowWatcherTest, DragWindow) {
  // ShelfModel only has an APP_LIST item.
  EXPECT_EQ(1, model_->item_count());

  scoped_ptr<aura::Window> window(CreateTestWindowInShellWithId(0));

  // Create a ShelfItem for |window|.
  ShelfID id = CreateShelfItem(window.get());
  EXPECT_EQ(2, model_->item_count());

  int index = model_->ItemIndexByID(id);
  EXPECT_EQ(STATUS_RUNNING, model_->items()[index].status);

  // Simulate dragging of |window| and check its item is not changed.
  scoped_ptr<WindowResizer> resizer(
      CreateWindowResizer(window.get(),
                          gfx::Point(),
                          HTCAPTION,
                          aura::client::WINDOW_MOVE_SOURCE_MOUSE));
  ASSERT_TRUE(resizer.get());
  resizer->Drag(gfx::Point(50, 50), 0);
  resizer->CompleteDrag();

  //Index and id are not changed after dragging a |window|.
  EXPECT_EQ(index, model_->ItemIndexByID(id));
  EXPECT_EQ(id, model_->items()[index].id);
}

// Check |window|'s item is removed when it is re-parented not to default
// container during the dragging.
TEST_F(ShelfWindowWatcherTest, ReparentWindowDuringTheDragging) {
  // ShelfModel only has an APP_LIST item.
  EXPECT_EQ(1, model_->item_count());

  scoped_ptr<aura::Window> window(CreateTestWindowInShellWithId(0));
  window->set_owned_by_parent(false);

  // Create a ShelfItem for |window|.
  ShelfID id = CreateShelfItem(window.get());
  EXPECT_EQ(2, model_->item_count());
  int index = model_->ItemIndexByID(id);
  EXPECT_EQ(STATUS_RUNNING, model_->items()[index].status);

  aura::Window* root_window = window->GetRootWindow();
  aura::Window* default_container = Shell::GetContainer(
      root_window,
      kShellWindowId_DefaultContainer);
  EXPECT_EQ(default_container, window->parent());

  aura::Window* new_parent = Shell::GetContainer(
      root_window,
      kShellWindowId_PanelContainer);

  // Simulate re-parenting to |new_parent| during the dragging.
  {
    scoped_ptr<WindowResizer> resizer(
        CreateWindowResizer(window.get(),
                            gfx::Point(),
                            HTCAPTION,
                            aura::client::WINDOW_MOVE_SOURCE_MOUSE));
    ASSERT_TRUE(resizer.get());
    resizer->Drag(gfx::Point(50, 50), 0);
    resizer->CompleteDrag();
    EXPECT_EQ(2, model_->item_count());

    // Item should be removed when |window| is re-parented not to default
    // container before fininshing the dragging.
    EXPECT_TRUE(wm::GetWindowState(window.get())->is_dragged());
    new_parent->AddChild(window.get());
    EXPECT_EQ(1, model_->item_count());
  }
  EXPECT_FALSE(wm::GetWindowState(window.get())->is_dragged());
  EXPECT_EQ(1, model_->item_count());
}

}  // namespace ash