// Copyright (c) 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 "chrome/browser/ui/ash/session_state_delegate_chromeos.h"

#include "ash/content/shell_content_state.h"
#include "ash/multi_profile_uma.h"
#include "ash/session/session_state_observer.h"
#include "ash/system/chromeos/multi_user/user_switch_util.h"
#include "base/bind.h"
#include "base/callback.h"
#include "base/command_line.h"
#include "base/logging.h"
#include "base/prefs/pref_service.h"
#include "chrome/browser/chromeos/login/lock/screen_locker.h"
#include "chrome/browser/chromeos/login/ui/user_adding_screen.h"
#include "chrome/browser/chromeos/login/users/multi_profile_user_controller.h"
#include "chrome/browser/chromeos/profiles/profile_helper.h"
#include "chrome/browser/profiles/profile.h"
#include "chrome/browser/profiles/profile_manager.h"
#include "chrome/browser/ui/ash/multi_user/multi_user_window_manager.h"
#include "chrome/browser/ui/ash/session_util.h"
#include "chrome/common/pref_names.h"
#include "chromeos/chromeos_switches.h"
#include "chromeos/dbus/dbus_thread_manager.h"
#include "chromeos/dbus/session_manager_client.h"
#include "chromeos/login/login_state.h"
#include "components/user_manager/user.h"
#include "components/user_manager/user_info.h"
#include "components/user_manager/user_manager.h"
#include "google_apis/gaia/gaia_auth_util.h"
#include "ui/gfx/image/image_skia.h"

SessionStateDelegateChromeos::SessionStateDelegateChromeos()
    : session_state_(SESSION_STATE_LOGIN_PRIMARY) {
  user_manager::UserManager::Get()->AddSessionStateObserver(this);
  chromeos::UserAddingScreen::Get()->AddObserver(this);

  // LoginState is not initialized in unit_tests.
  if (chromeos::LoginState::IsInitialized()) {
    chromeos::LoginState::Get()->AddObserver(this);
    SetSessionState(chromeos::LoginState::Get()->IsUserLoggedIn() ?
        SESSION_STATE_ACTIVE : SESSION_STATE_LOGIN_PRIMARY, true);
  }
}

SessionStateDelegateChromeos::~SessionStateDelegateChromeos() {
  user_manager::UserManager::Get()->RemoveSessionStateObserver(this);
  chromeos::UserAddingScreen::Get()->RemoveObserver(this);

  // LoginState is not initialized in unit_tests.
  if (chromeos::LoginState::IsInitialized())
    chromeos::LoginState::Get()->RemoveObserver(this);
}

int SessionStateDelegateChromeos::GetMaximumNumberOfLoggedInUsers() const {
  // We limit list of logged in users to 10 due to memory constraints.
  // Note that 10 seems excessive, but we want to test how many users are
  // actually added to a session.
  // TODO(nkostylev): Adjust this limitation based on device capabilites.
  // http://crbug.com/230865
  return 10;
}

int SessionStateDelegateChromeos::NumberOfLoggedInUsers() const {
  return user_manager::UserManager::Get()->GetLoggedInUsers().size();
}

bool SessionStateDelegateChromeos::CanAddUserToMultiProfile(
    AddUserError* error) const {
  if (user_manager::UserManager::Get()
          ->GetUsersAllowedForMultiProfile()
          .size() == 0) {
    if (error)
      *error = ADD_USER_ERROR_OUT_OF_USERS;
    return false;
  }
  return SessionStateDelegate::CanAddUserToMultiProfile(error);
}

bool SessionStateDelegateChromeos::IsActiveUserSessionStarted() const {
  return user_manager::UserManager::Get()->IsSessionStarted();
}

bool SessionStateDelegateChromeos::CanLockScreen() const {
  const user_manager::UserList unlock_users =
      user_manager::UserManager::Get()->GetUnlockUsers();
  return !unlock_users.empty();
}

bool SessionStateDelegateChromeos::IsScreenLocked() const {
  return chromeos::ScreenLocker::default_screen_locker() &&
         chromeos::ScreenLocker::default_screen_locker()->locked();
}

bool SessionStateDelegateChromeos::ShouldLockScreenBeforeSuspending() const {
  const user_manager::UserList logged_in_users =
      user_manager::UserManager::Get()->GetLoggedInUsers();
  for (user_manager::UserList::const_iterator it = logged_in_users.begin();
       it != logged_in_users.end();
       ++it) {
    user_manager::User* user = (*it);
    Profile* profile = chromeos::ProfileHelper::Get()->GetProfileByUser(user);
    if (profile &&
        profile->GetPrefs()->GetBoolean(prefs::kEnableAutoScreenLock)) {
      return true;
    }
  }
  return false;
}

void SessionStateDelegateChromeos::LockScreen() {
  if (!CanLockScreen())
    return;

  VLOG(1) << "Requesting screen lock from SessionStateDelegate";
  chromeos::DBusThreadManager::Get()->GetSessionManagerClient()->
      RequestLockScreen();
}

void SessionStateDelegateChromeos::UnlockScreen() {
  // This is used only for testing thus far.
  NOTIMPLEMENTED();
}

bool SessionStateDelegateChromeos::IsUserSessionBlocked() const {
  bool has_login_manager = base::CommandLine::ForCurrentProcess()->HasSwitch(
      chromeos::switches::kLoginManager);
  return (has_login_manager && !IsActiveUserSessionStarted()) ||
         IsScreenLocked() ||
         chromeos::UserAddingScreen::Get()->IsRunning();
}

ash::SessionStateDelegate::SessionState
SessionStateDelegateChromeos::GetSessionState() const {
  return session_state_;
}

const user_manager::UserInfo* SessionStateDelegateChromeos::GetUserInfo(
    ash::UserIndex index) const {
  DCHECK_LT(index, NumberOfLoggedInUsers());
  return user_manager::UserManager::Get()->GetLRULoggedInUsers()[index];
}

bool SessionStateDelegateChromeos::ShouldShowAvatar(
    aura::Window* window) const {
  return chrome::MultiUserWindowManager::GetInstance()->
      ShouldShowAvatar(window);
}

gfx::ImageSkia SessionStateDelegateChromeos::GetAvatarImageForWindow(
    aura::Window* window) const {
  content::BrowserContext* context =
      ash::ShellContentState::GetInstance()->GetBrowserContextForWindow(window);
  return GetAvatarImageForContext(context);
}

void SessionStateDelegateChromeos::SwitchActiveUser(
    const std::string& user_id) {
  // Disallow switching to an already active user since that might crash.
  // Also check that we got a user id and not an email address.
  DCHECK_EQ(user_id,
            gaia::CanonicalizeEmail(gaia::SanitizeEmail(user_id)));
  if (user_id == user_manager::UserManager::Get()->GetActiveUser()->email())
    return;
  TryToSwitchUser(user_id);
}

void SessionStateDelegateChromeos::CycleActiveUser(CycleUser cycle_user) {
  // Make sure there is a user to switch to.
  if (NumberOfLoggedInUsers() <= 1)
    return;

  const user_manager::UserList& logged_in_users =
      user_manager::UserManager::Get()->GetLoggedInUsers();

  std::string user_id =
      user_manager::UserManager::Get()->GetActiveUser()->email();

  // Get an iterator positioned at the active user.
  user_manager::UserList::const_iterator it;
  for (it = logged_in_users.begin();
       it != logged_in_users.end(); ++it) {
    if ((*it)->email() == user_id)
      break;
  }

  // Active user not found.
  if (it == logged_in_users.end())
    return;

  // Get the user's email to select, wrapping to the start/end of the list if
  // necessary.
  switch (cycle_user) {
    case CYCLE_TO_NEXT_USER:
      if (++it == logged_in_users.end())
        user_id = (*logged_in_users.begin())->email();
      else
        user_id = (*it)->email();
      break;
    case CYCLE_TO_PREVIOUS_USER:
      if (it == logged_in_users.begin())
        it = logged_in_users.end();
      user_id = (*(--it))->email();
      break;
  }

  // Switch using the transformed |user_id|.
  TryToSwitchUser(user_id);
}

bool SessionStateDelegateChromeos::IsMultiProfileAllowedByPrimaryUserPolicy()
    const {
  return chromeos::MultiProfileUserController::GetPrimaryUserPolicy() ==
         chromeos::MultiProfileUserController::ALLOWED;
}

void SessionStateDelegateChromeos::AddSessionStateObserver(
    ash::SessionStateObserver* observer) {
  session_state_observer_list_.AddObserver(observer);
}

void SessionStateDelegateChromeos::RemoveSessionStateObserver(
    ash::SessionStateObserver* observer) {
  session_state_observer_list_.RemoveObserver(observer);
}

void SessionStateDelegateChromeos::LoggedInStateChanged() {
  SetSessionState(chromeos::LoginState::Get()->IsUserLoggedIn() ?
      SESSION_STATE_ACTIVE : SESSION_STATE_LOGIN_PRIMARY, false);
}

void SessionStateDelegateChromeos::ActiveUserChanged(
    const user_manager::User* active_user) {
  FOR_EACH_OBSERVER(ash::SessionStateObserver,
                    session_state_observer_list_,
                    ActiveUserChanged(active_user->email()));
}

void SessionStateDelegateChromeos::UserAddedToSession(
    const user_manager::User* added_user) {
  FOR_EACH_OBSERVER(ash::SessionStateObserver,
                    session_state_observer_list_,
                    UserAddedToSession(added_user->email()));
}

void SessionStateDelegateChromeos::OnUserAddingStarted() {
  SetSessionState(SESSION_STATE_LOGIN_SECONDARY, false);
}

void SessionStateDelegateChromeos::OnUserAddingFinished() {
  SetSessionState(SESSION_STATE_ACTIVE, false);
}

void SessionStateDelegateChromeos::SetSessionState(SessionState new_state,
                                                   bool force) {
  if (session_state_ == new_state && !force)
    return;

  session_state_ = new_state;
  NotifySessionStateChanged();
}

void SessionStateDelegateChromeos::NotifySessionStateChanged() {
  FOR_EACH_OBSERVER(ash::SessionStateObserver,
                    session_state_observer_list_,
                    SessionStateChanged(session_state_));
}

void DoSwitchUser(const std::string& user_id) {
  user_manager::UserManager::Get()->SwitchActiveUser(user_id);
}

void SessionStateDelegateChromeos::TryToSwitchUser(
    const std::string& user_id) {
  ash::TrySwitchingActiveUser(base::Bind(&DoSwitchUser, user_id));
}