// Copyright 2020 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#import "ios/chrome/browser/infobars/model/overlays/browser_agent/infobar_overlay_browser_agent.h"

#import "base/check.h"
#import "ios/chrome/browser/infobars/model/overlays/browser_agent/interaction_handlers/default/default_infobar_banner_interaction_handler.h"
#import "ios/chrome/browser/infobars/model/overlays/browser_agent/interaction_handlers/infobar_interaction_handler.h"
#import "ios/chrome/browser/infobars/model/overlays/infobar_overlay_util.h"

#pragma mark - InfobarOverlayBrowserAgent

InfobarOverlayBrowserAgent::InfobarOverlayBrowserAgent(Browser* browser)
    : OverlayBrowserAgentBase(browser),
      BrowserUserData(browser),
      overlay_visibility_observer_(browser, this) {}

InfobarOverlayBrowserAgent::~InfobarOverlayBrowserAgent() {
  // Clear installers before destroying interaction_handlers_, since
  // installers hold raw_ptr to interaction handlers owned by this class.
  ClearInstallers();
}

#pragma mark Public

void InfobarOverlayBrowserAgent::AddInfobarInteractionHandler(
    std::unique_ptr<InfobarInteractionHandler> interaction_handler) {
  // Only one installer should be set for a single request type.
  InfobarType type = interaction_handler->infobar_type();
  DCHECK(!interaction_handlers_[type]);
  // Add the banner installer.  Every InfobarType supports banners, so the added
  // installer is gauranteed to be non-null.
  AddInstaller(interaction_handler->CreateBannerCallbackInstaller(),
               OverlayModality::kInfobarBanner);
  // Add the modal installers.  Not all InfobarTypes support modal UI, so the
  // installer must be checked before being added.
  std::unique_ptr<OverlayRequestCallbackInstaller> modal_installer =
      interaction_handler->CreateModalCallbackInstaller();
  if (modal_installer) {
    AddInstaller(std::move(modal_installer), OverlayModality::kInfobarModal);
  }
  // Add the interaction handler to the list.
  interaction_handlers_[type] = std::move(interaction_handler);
}

void InfobarOverlayBrowserAgent::
    AddDefaultInfobarInteractionHandlerForInfobarType(
        InfobarType infobar_type) {
  AddInfobarInteractionHandler(std::make_unique<InfobarInteractionHandler>(
      infobar_type,
      std::make_unique<DefaultInfobarBannerInteractionHandler>(infobar_type),
      /*modal_handler=*/nullptr));
}

#pragma mark Private

InfobarInteractionHandler* InfobarOverlayBrowserAgent::GetInteractionHandler(
    OverlayRequest* request) {
  if (!request) {
    return nullptr;
  }
  return interaction_handlers_[GetOverlayRequestInfobarType(request)].get();
}

#pragma mark - InfobarOverlayBrowserAgent::OverlayVisibilityObserver

InfobarOverlayBrowserAgent::OverlayVisibilityObserver::
    OverlayVisibilityObserver(Browser* browser,
                              InfobarOverlayBrowserAgent* browser_agent)
    : browser_agent_(browser_agent) {
  DCHECK(browser_agent_);
  scoped_observations_.AddObservation(
      OverlayPresenter::FromBrowser(browser, OverlayModality::kInfobarBanner));
  scoped_observations_.AddObservation(
      OverlayPresenter::FromBrowser(browser, OverlayModality::kInfobarModal));
}

InfobarOverlayBrowserAgent::OverlayVisibilityObserver::
    ~OverlayVisibilityObserver() = default;

void InfobarOverlayBrowserAgent::OverlayVisibilityObserver::
    OverlayVisibilityChanged(OverlayRequest* request, bool visible) {
  InfoBarIOS* infobar = GetOverlayRequestInfobar(request);
  if (!infobar) {
    return;
  }
  browser_agent_->GetInteractionHandler(request)->InfobarVisibilityChanged(
      infobar, GetOverlayRequestInfobarOverlayType(request), visible);
}

const OverlayRequestSupport*
InfobarOverlayBrowserAgent::OverlayVisibilityObserver::GetRequestSupport(
    OverlayPresenter* presenter) const {
  return browser_agent_->GetRequestSupport(presenter->GetModality());
}

void InfobarOverlayBrowserAgent::OverlayVisibilityObserver::DidShowOverlay(
    OverlayPresenter* presenter,
    OverlayRequest* request) {
  OverlayVisibilityChanged(request, /*visible=*/true);
}

void InfobarOverlayBrowserAgent::OverlayVisibilityObserver::DidHideOverlay(
    OverlayPresenter* presenter,
    OverlayRequest* request) {
  OverlayVisibilityChanged(request, /*visible=*/false);
}

void InfobarOverlayBrowserAgent::OverlayVisibilityObserver::
    OverlayPresenterDestroyed(OverlayPresenter* presenter) {
  scoped_observations_.RemoveObservation(presenter);
}
