// 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 "chrome/browser/ui/browser_list_impl.h"

#include "base/command_line.h"
#include "base/logging.h"
#include "chrome/browser/browser_process.h"
#include "chrome/browser/browser_shutdown.h"
#include "chrome/browser/lifetime/application_lifetime.h"
#include "chrome/browser/profiles/profile.h"
#include "chrome/browser/ui/browser.h"
#include "chrome/browser/ui/browser_list_observer.h"
#include "chrome/browser/ui/browser_window.h"
#include "chrome/common/chrome_notification_types.h"
#include "chrome/common/chrome_switches.h"
#include "content/public/browser/notification_service.h"

// #include "build/build_config.h"
// #include "chrome/browser/prefs/pref_service.h"


#if defined(OS_CHROMEOS)
#include "chrome/browser/chromeos/login/user_manager.h"
#endif

namespace chrome {

// static
BrowserListImpl* BrowserListImpl::native_instance_ = NULL;
BrowserListImpl* BrowserListImpl::ash_instance_ = NULL;

////////////////////////////////////////////////////////////////////////////////
// BrowserListImpl, public:

// static
BrowserListImpl* BrowserListImpl::GetInstance(HostDesktopType type) {
  BrowserListImpl** list = NULL;
  if (type == HOST_DESKTOP_TYPE_NATIVE)
    list = &native_instance_;
  else if (type == HOST_DESKTOP_TYPE_ASH)
    list = &ash_instance_;
  else
    NOTREACHED();
  if (!*list)
    *list = new BrowserListImpl;
  return *list;
}

void BrowserListImpl::AddBrowser(Browser* browser) {
  DCHECK(browser);
  browsers_.push_back(browser);

  g_browser_process->AddRefModule();

  content::NotificationService::current()->Notify(
      chrome::NOTIFICATION_BROWSER_OPENED,
      content::Source<Browser>(browser),
      content::NotificationService::NoDetails());

  // Send out notifications after add has occurred. Do some basic checking to
  // try to catch evil observers that change the list from under us.
  size_t original_count = observers_.size();
  FOR_EACH_OBSERVER(BrowserListObserver, observers_, OnBrowserAdded(browser));
  DCHECK_EQ(original_count, observers_.size())
      << "observer list modified during notification";
}

void BrowserListImpl::RemoveBrowser(Browser* browser) {
  RemoveBrowserFrom(browser, &last_active_browsers_);

  content::NotificationService::current()->Notify(
      chrome::NOTIFICATION_BROWSER_CLOSED,
      content::Source<Browser>(browser),
      content::NotificationService::NoDetails());

  RemoveBrowserFrom(browser, &browsers_);

  FOR_EACH_OBSERVER(BrowserListObserver, observers_, OnBrowserRemoved(browser));

  g_browser_process->ReleaseModule();

  // If we're exiting, send out the APP_TERMINATING notification to allow other
  // modules to shut themselves down.
  if (browsers_.empty() &&
      (browser_shutdown::IsTryingToQuit() ||
       g_browser_process->IsShuttingDown())) {
    // Last browser has just closed, and this is a user-initiated quit or there
    // is no module keeping the app alive, so send out our notification. No need
    // to call ProfileManager::ShutdownSessionServices() as part of the
    // shutdown, because Browser::WindowClosing() already makes sure that the
    // SessionService is created and notified.
    browser::NotifyAppTerminating();
    browser::OnAppExiting();
  }
}

void BrowserListImpl::AddObserver(BrowserListObserver* observer) {
  observers_.AddObserver(observer);
}

void BrowserListImpl::RemoveObserver(BrowserListObserver* observer) {
  observers_.RemoveObserver(observer);
}

void BrowserListImpl::SetLastActive(Browser* browser) {
  RemoveBrowserFrom(browser, &last_active_browsers_);
  last_active_browsers_.push_back(browser);

  FOR_EACH_OBSERVER(BrowserListObserver, observers_,
                    OnBrowserSetLastActive(browser));
}

Browser* BrowserListImpl::GetLastActive() {
  if (!last_active_browsers_.empty())
    return *(last_active_browsers_.rbegin());
  return NULL;
}

void BrowserListImpl::CloseAllBrowsersWithProfile(Profile* profile) {
  BrowserVector browsers_to_close;
  for (BrowserListImpl::const_iterator i = BrowserListImpl::begin();
       i != BrowserListImpl::end(); ++i) {
    if ((*i)->profile()->GetOriginalProfile() == profile->GetOriginalProfile())
      browsers_to_close.push_back(*i);
  }

  for (BrowserVector::const_iterator i = browsers_to_close.begin();
       i != browsers_to_close.end(); ++i) {
    (*i)->window()->Close();
  }
}

bool BrowserListImpl::IsIncognitoWindowOpen() {
  for (BrowserListImpl::const_iterator i = BrowserListImpl::begin();
       i != BrowserListImpl::end(); ++i) {
    if ((*i)->profile()->IsOffTheRecord())
      return true;
  }
  return false;
}

bool BrowserListImpl::IsIncognitoWindowOpenForProfile(Profile* profile) {
#if defined(OS_CHROMEOS)
  // In ChromeOS, we assume that the default profile is always valid, so if
  // we are in guest mode, keep the OTR profile active so it won't be deleted.
  if (chromeos::UserManager::Get()->IsLoggedInAsGuest())
    return true;
#endif
  for (BrowserListImpl::const_iterator i = BrowserListImpl::begin();
       i != BrowserListImpl::end(); ++i) {
    if ((*i)->profile()->IsSameProfile(profile) &&
        (*i)->profile()->IsOffTheRecord()) {
      return true;
    }
  }
  return false;
}

////////////////////////////////////////////////////////////////////////////////
// BrowserListImpl, private:

BrowserListImpl::BrowserListImpl() {
}

BrowserListImpl::~BrowserListImpl() {
}

void BrowserListImpl::RemoveBrowserFrom(Browser* browser,
                                        BrowserVector* browser_list) {
  const iterator remove_browser =
      std::find(browser_list->begin(), browser_list->end(), browser);
  if (remove_browser != browser_list->end())
    browser_list->erase(remove_browser);
}

}  // namespace chrome