// 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 "ash/wm/stacking_controller.h"

#include "ash/display/display_controller.h"
#include "ash/shell.h"
#include "ash/shell_window_ids.h"
#include "ash/wm/always_on_top_controller.h"
#include "ash/wm/coordinate_conversion.h"
#include "ash/wm/window_properties.h"
#include "ui/aura/client/aura_constants.h"
#include "ui/aura/root_window.h"
#include "ui/aura/window.h"
#include "ui/base/ui_base_types.h"

namespace ash {
namespace internal {
namespace {

// Find a root window that matches the |bounds|. If the virtual screen
// coordinates is enabled and the bounds is specified, the root window
// that matches the window's bound will be used. Otherwise, it'll
// return the active root window.
aura::RootWindow* FindContainerRoot(const gfx::Rect& bounds) {
  if (bounds.x() == 0 && bounds.y() == 0 && bounds.IsEmpty())
    return Shell::GetActiveRootWindow();
  return wm::GetRootWindowMatching(bounds);
}

aura::Window* GetContainerById(aura::RootWindow* root, int id) {
  return Shell::GetContainer(root, id);
}

aura::Window* GetContainerForWindow(aura::Window* window) {
  aura::Window* container = window->parent();
  while (container && container->type() != aura::client::WINDOW_TYPE_UNKNOWN)
    container = container->parent();
  return container;
}

bool IsSystemModal(aura::Window* window) {
  return window->GetProperty(aura::client::kModalKey) == ui::MODAL_TYPE_SYSTEM;
}

bool IsWindowModal(aura::Window* window) {
  return window->transient_parent() &&
      window->GetProperty(aura::client::kModalKey) == ui::MODAL_TYPE_WINDOW;
}

}  // namespace

////////////////////////////////////////////////////////////////////////////////
// StackingController, public:

StackingController::StackingController() {
  aura::client::SetStackingClient(this);
}

StackingController::~StackingController() {
}

////////////////////////////////////////////////////////////////////////////////
// StackingController, aura::StackingClient implementation:

aura::Window* StackingController::GetDefaultParent(aura::Window* window,
                                                   const gfx::Rect& bounds) {
  aura::RootWindow* target_root = NULL;
  if (window->transient_parent()) {
    // Transient window should use the same root as its transient parent.
    target_root = window->transient_parent()->GetRootWindow();
  } else {
    target_root = FindContainerRoot(bounds);
  }

  switch (window->type()) {
    case aura::client::WINDOW_TYPE_NORMAL:
    case aura::client::WINDOW_TYPE_POPUP:
      if (IsSystemModal(window))
        return GetSystemModalContainer(target_root, window);
      else if (IsWindowModal(window))
        return GetContainerForWindow(window->transient_parent());
      return GetAlwaysOnTopController(target_root)->GetContainer(window);
    case aura::client::WINDOW_TYPE_PANEL:
      return GetContainerById(target_root,
                              internal::kShellWindowId_PanelContainer);
    case aura::client::WINDOW_TYPE_MENU:
      return GetContainerById(
          target_root, internal::kShellWindowId_MenuContainer);
    case aura::client::WINDOW_TYPE_TOOLTIP:
      return GetContainerById(
          target_root, internal::kShellWindowId_DragImageAndTooltipContainer);
    case aura::client::WINDOW_TYPE_CONTROL:
      return GetContainerById(
          target_root, internal::kShellWindowId_UnparentedControlContainer);
    default:
      NOTREACHED() << "Window " << window->id()
                   << " has unhandled type " << window->type();
      break;
  }
  return NULL;
}

////////////////////////////////////////////////////////////////////////////////
// StackingController, private:

aura::Window* StackingController::GetSystemModalContainer(
    aura::RootWindow* root,
    aura::Window* window) const {
  DCHECK(IsSystemModal(window));

  // If screen lock is not active, all modal windows are placed into the
  // normal modal container.
  aura::Window* lock_container =
      GetContainerById(root, internal::kShellWindowId_LockScreenContainer);
  if (!lock_container->children().size()) {
    return GetContainerById(root,
                            internal::kShellWindowId_SystemModalContainer);
  }

  // Otherwise those that originate from LockScreen container and above are
  // placed in the screen lock modal container.
  int lock_container_id = lock_container->id();
  int window_container_id = window->transient_parent()->parent()->id();

  aura::Window* container = NULL;
  if (window_container_id < lock_container_id) {
    container = GetContainerById(
        root, internal::kShellWindowId_SystemModalContainer);
  } else {
    container = GetContainerById(
        root, internal::kShellWindowId_LockSystemModalContainer);
  }

  return container;
}

// TODO(oshima): Remove this once extended desktop is on by default.
internal::AlwaysOnTopController*
StackingController::GetAlwaysOnTopController(aura::RootWindow* root_window) {
  internal::AlwaysOnTopController* controller =
      root_window->GetProperty(internal::kAlwaysOnTopControllerKey);
  if (!controller) {
    controller = new internal::AlwaysOnTopController;
    controller->SetContainers(
        root_window->GetChildById(internal::kShellWindowId_DefaultContainer),
        root_window->GetChildById(
            internal::kShellWindowId_AlwaysOnTopContainer));
    // RootWindow owns the AlwaysOnTopController object.
    root_window->SetProperty(kAlwaysOnTopControllerKey, controller);
  }
  return controller;
}

}  // namespace internal
}  // namespace ash