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

#include "chrome/browser/metrics/desktop_session_duration/audible_contents_tracker.h"

#include "chrome/browser/metrics/desktop_session_duration/desktop_session_duration_tracker.h"
#include "chrome/browser/ui/browser_window/public/browser_window_interface.h"
#include "chrome/browser/ui/browser_window/public/browser_window_interface_iterator.h"
#include "chrome/browser/ui/browser_window/public/global_browser_collection.h"
#include "chrome/browser/ui/recently_audible_helper.h"
#include "chrome/browser/ui/tabs/tab_strip_model.h"

namespace metrics {

AudibleContentsTracker::AudibleContentsTracker(Observer* observer)
    : observer_(observer) {
  ForEachCurrentBrowserWindowInterfaceOrderedByActivation(
      [this](BrowserWindowInterface* browser) {
        browser->GetTabStripModel()->AddObserver(this);
        return true;
      });

  browser_collection_observation_.Observe(
      GlobalBrowserCollection::GetInstance());
}

AudibleContentsTracker::~AudibleContentsTracker() = default;

void AudibleContentsTracker::OnBrowserCreated(BrowserWindowInterface* browser) {
  // TODO(crbug.com/452120900): TabStripModel auto-unregistered by dtor
  browser->GetTabStripModel()->AddObserver(this);
}

void AudibleContentsTracker::OnTabStripModelChanged(
    TabStripModel* tab_strip_model,
    const TabStripModelChange& change,
    const TabStripSelectionChange& selection) {
  if (change.type() == TabStripModelChange::kRemoved) {
    for (const auto& contents : change.GetRemove()->contents) {
      if (TabRemoveReasonUtils::WillDeleteTab(contents.remove_reason)) {
        RemoveAudibleWebContents(contents.contents);
      }
    }
  } else if (change.type() == TabStripModelChange::kReplaced) {
    auto* replace = change.GetReplace();
    if (replace->old_contents)
      RemoveAudibleWebContents(replace->old_contents);
    if (replace->new_contents) {
      auto* audible_helper =
          RecentlyAudibleHelper::FromWebContents(replace->new_contents);
      if (audible_helper->WasRecentlyAudible())
        AddAudibleWebContents(replace->new_contents);
    }
  }
}

void AudibleContentsTracker::OnTabChangedAt(tabs::TabInterface* tab,
                                            TabChangeType change_type) {
  // Ignore 'loading' and 'title' changes.
  if (change_type != TabChangeType::kAll)
    return;

  auto* web_contents = tab->GetContents();
  auto* audible_helper = RecentlyAudibleHelper::FromWebContents(web_contents);
  if (audible_helper->WasRecentlyAudible())
    AddAudibleWebContents(web_contents);
  else
    RemoveAudibleWebContents(web_contents);
}

void AudibleContentsTracker::AddAudibleWebContents(
    content::WebContents* web_contents) {
  // The first web contents to become audible indicates that audio has started.
  bool added = audible_contents_.insert(web_contents).second;
  if (added && audible_contents_.size() == 1)
    observer_->OnAudioStart();
}

void AudibleContentsTracker::RemoveAudibleWebContents(
    content::WebContents* web_contents) {
  // If the web content was previously audible and there are no other audible
  // web contents then notify that audio ended.
  bool removed = (audible_contents_.erase(web_contents) == 1);
  if (removed && audible_contents_.empty())
    observer_->OnAudioEnd();
}

}  // namespace metrics
