// Copyright 2015 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/toolbar/media_router_action.h"

#include "base/metrics/user_metrics.h"
#include "base/strings/utf_string_conversions.h"
#include "chrome/browser/media/router/issue.h"
#include "chrome/browser/media/router/media_route.h"
#include "chrome/browser/media/router/media_router.h"
#include "chrome/browser/media/router/media_router_factory.h"
#include "chrome/browser/media/router/media_router_metrics.h"
#include "chrome/browser/profiles/profile.h"
#include "chrome/browser/ui/browser.h"
#include "chrome/browser/ui/tabs/tab_strip_model.h"
#include "chrome/browser/ui/toolbar/component_toolbar_actions_factory.h"
#include "chrome/browser/ui/toolbar/media_router_action_platform_delegate.h"
#include "chrome/browser/ui/toolbar/toolbar_action_view_delegate.h"
#include "chrome/browser/ui/webui/media_router/media_router_dialog_controller_impl.h"
#include "chrome/grit/generated_resources.h"
#include "grit/theme_resources.h"
#include "ui/base/l10n/l10n_util.h"
#include "ui/base/resource/resource_bundle.h"
#include "ui/gfx/image/image_skia.h"

using media_router::MediaRouterDialogControllerImpl;

namespace {

media_router::MediaRouter* GetMediaRouter(Browser* browser) {
  return media_router::MediaRouterFactory::GetApiForBrowserContext(
      browser->profile());
}

}  // namespace

MediaRouterAction::MediaRouterAction(Browser* browser,
                                     ToolbarActionsBar* toolbar_actions_bar)
    : media_router::IssuesObserver(GetMediaRouter(browser)),
      media_router::MediaRoutesObserver(GetMediaRouter(browser)),
      media_router_active_icon_(
          ui::ResourceBundle::GetSharedInstance().GetImageNamed(
              IDR_MEDIA_ROUTER_ACTIVE_ICON)),
      media_router_error_icon_(
          ui::ResourceBundle::GetSharedInstance().GetImageNamed(
              IDR_MEDIA_ROUTER_ERROR_ICON)),
      media_router_idle_icon_(
          ui::ResourceBundle::GetSharedInstance().GetImageNamed(
              IDR_MEDIA_ROUTER_IDLE_ICON)),
      media_router_warning_icon_(
          ui::ResourceBundle::GetSharedInstance().GetImageNamed(
              IDR_MEDIA_ROUTER_WARNING_ICON)),
      current_icon_(&media_router_idle_icon_),
      has_local_display_route_(false),
      delegate_(nullptr),
      browser_(browser),
      toolbar_actions_bar_(toolbar_actions_bar),
      platform_delegate_(MediaRouterActionPlatformDelegate::Create(browser)),
      contextual_menu_(browser),
      tab_strip_model_observer_(this),
      weak_ptr_factory_(this) {
  DCHECK(browser_);
  DCHECK(toolbar_actions_bar_);
  tab_strip_model_observer_.Add(browser_->tab_strip_model());

  RegisterObserver();
}

MediaRouterAction::~MediaRouterAction() {
  UnregisterObserver();
}

std::string MediaRouterAction::GetId() const {
  return ComponentToolbarActionsFactory::kMediaRouterActionId;
}

void MediaRouterAction::SetDelegate(ToolbarActionViewDelegate* delegate) {
  delegate_ = delegate;

  // Updates the current popup state if |delegate_| is non-null and has
  // WebContents ready.
  // In cases such as opening a new browser window, SetDelegate() will be
  // called before the WebContents is set. In those cases, we update the popup
  // state when ActiveTabChanged() is called.
  if (delegate_ && delegate_->GetCurrentWebContents())
    UpdatePopupState();
}

gfx::Image MediaRouterAction::GetIcon(content::WebContents* web_contents,
                                      const gfx::Size& size) {
  return *current_icon_;
}

base::string16 MediaRouterAction::GetActionName() const {
  return l10n_util::GetStringUTF16(IDS_MEDIA_ROUTER_TITLE);
}

base::string16 MediaRouterAction::GetAccessibleName(
    content::WebContents* web_contents) const {
  return GetTooltip(web_contents);
}

base::string16 MediaRouterAction::GetTooltip(
    content::WebContents* web_contents) const {
  return l10n_util::GetStringUTF16(IDS_MEDIA_ROUTER_ICON_TOOLTIP_TEXT);
}

bool MediaRouterAction::IsEnabled(
    content::WebContents* web_contents) const {
  return true;
}

bool MediaRouterAction::WantsToRun(
    content::WebContents* web_contents) const {
  return false;
}

bool MediaRouterAction::HasPopup(
    content::WebContents* web_contents) const {
  return true;
}

void MediaRouterAction::HidePopup() {
  GetMediaRouterDialogController()->HideMediaRouterDialog();
  OnPopupHidden();
}

gfx::NativeView MediaRouterAction::GetPopupNativeView() {
  return nullptr;
}

ui::MenuModel* MediaRouterAction::GetContextMenu() {
  return contextual_menu_.menu_model();
}

bool MediaRouterAction::ExecuteAction(bool by_user) {
  base::RecordAction(base::UserMetricsAction("MediaRouter_Icon_Click"));

  if (GetMediaRouterDialogController()->IsShowingMediaRouterDialog()) {
    GetMediaRouterDialogController()->HideMediaRouterDialog();
    return false;
  }

  GetMediaRouterDialogController()->ShowMediaRouterDialog();
  if (GetPlatformDelegate()) {
    media_router::MediaRouterMetrics::RecordMediaRouterDialogOrigin(
        GetPlatformDelegate()->CloseOverflowMenuIfOpen() ?
        media_router::MediaRouterDialogOpenOrigin::OVERFLOW_MENU :
        media_router::MediaRouterDialogOpenOrigin::TOOLBAR);
  }
  return true;
}

void MediaRouterAction::UpdateState() {
  if (delegate_)
    delegate_->UpdateState();
}

bool MediaRouterAction::DisabledClickOpensMenu() const {
  return false;
}

void MediaRouterAction::OnIssueUpdated(const media_router::Issue* issue) {
  issue_.reset(issue ? new media_router::Issue(*issue) : nullptr);

  MaybeUpdateIcon();
}

void MediaRouterAction::OnRoutesUpdated(
    const std::vector<media_router::MediaRoute>& routes,
    const std::vector<media_router::MediaRoute::Id>& joinable_route_ids) {
  has_local_display_route_ =
      std::find_if(routes.begin(), routes.end(),
                   [](const media_router::MediaRoute& route) {
                     return route.is_local() && route.for_display();
                   }) != routes.end();
  MaybeUpdateIcon();
}

void MediaRouterAction::ActiveTabChanged(content::WebContents* old_contents,
                                         content::WebContents* new_contents,
                                         int index,
                                         int reason) {
  UpdatePopupState();
}

void MediaRouterAction::OnPopupHidden() {
  if (delegate_)
    delegate_->OnPopupClosed();
}

void MediaRouterAction::OnPopupShown() {
  // We depress the action regardless of whether ExecuteAction() was user
  // initiated.
  if (delegate_)
    delegate_->OnPopupShown(true);
}

void MediaRouterAction::UpdatePopupState() {
  MediaRouterDialogControllerImpl* controller =
      GetMediaRouterDialogController();

  if (!controller)
    return;

  // When each browser window is created, its toolbar creates a
  // MediaRouterAction that is only destroyed when the browser window is torn
  // down. |controller| will keep track of that instance. If |this| was created
  // in overflow mode, it will be destroyed when the overflow menu is closed.
  // If SetMediaRouterAction() was previously called, this is a no-op.
  if (!toolbar_actions_bar_->in_overflow_mode())
    controller->SetMediaRouterAction(weak_ptr_factory_.GetWeakPtr());

  // Update the button in case the pressed state is out of sync with dialog
  // visibility.
  if (controller->IsShowingMediaRouterDialog())
    OnPopupShown();
  else
    OnPopupHidden();
}

MediaRouterDialogControllerImpl*
MediaRouterAction::GetMediaRouterDialogController() {
  DCHECK(delegate_);
  content::WebContents* web_contents = delegate_->GetCurrentWebContents();
  DCHECK(web_contents);
  return MediaRouterDialogControllerImpl::GetOrCreateForWebContents(
      web_contents);
}

MediaRouterActionPlatformDelegate* MediaRouterAction::GetPlatformDelegate() {
  return platform_delegate_.get();
}

void MediaRouterAction::MaybeUpdateIcon() {
  const gfx::Image* new_icon = GetCurrentIcon();

  // Update the current state if it has changed.
  if (new_icon != current_icon_) {
    current_icon_ = new_icon;

    // Tell the associated view to update its icon to reflect the change made
    // above.
    if (delegate_)
      delegate_->UpdateState();
  }
}

const gfx::Image* MediaRouterAction::GetCurrentIcon() const {
  // Highest priority is to indicate whether there's an issue.
  if (issue_) {
    if (issue_->severity() == media_router::Issue::FATAL)
      return &media_router_error_icon_;
    if (issue_->severity() == media_router::Issue::WARNING)
      return &media_router_warning_icon_;
  }

  return has_local_display_route_ ? &media_router_active_icon_
                                  : &media_router_idle_icon_;
}
