// Copyright 2017 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 "chromeos/components/tether/gms_core_notifications_state_tracker_impl.h"

#include <sstream>

#include "chromeos/components/multidevice/logging/logging.h"

namespace chromeos {

namespace tether {

namespace {

bool ContainsDeviceWithId(
    const std::string& device_id,
    const std::vector<HostScannerOperation::ScannedDeviceInfo>&
        device_info_list) {
  for (const auto& device_info : device_info_list) {
    if (device_info.remote_device.GetDeviceId() == device_id)
      return true;
  }

  return false;
}

bool ContainsDeviceWithId(
    const std::string& device_id,
    const multidevice::RemoteDeviceRefList& remote_device_list) {
  for (const auto& device_info : remote_device_list) {
    if (device_info.GetDeviceId() == device_id)
      return true;
  }

  return false;
}

}  // namespace

GmsCoreNotificationsStateTrackerImpl::GmsCoreNotificationsStateTrackerImpl() =
    default;

GmsCoreNotificationsStateTrackerImpl::~GmsCoreNotificationsStateTrackerImpl() {
  bool was_empty = device_id_to_name_map_.empty();
  device_id_to_name_map_.clear();

  if (!was_empty)
    SendDeviceNamesChangeEvent();
}

std::vector<std::string> GmsCoreNotificationsStateTrackerImpl::
    GetGmsCoreNotificationsDisabledDeviceNames() {
  std::vector<std::string> device_names;
  for (const auto& map_entry : device_id_to_name_map_)
    device_names.push_back(map_entry.second);
  return device_names;
}

void GmsCoreNotificationsStateTrackerImpl::OnTetherAvailabilityResponse(
    const std::vector<HostScannerOperation::ScannedDeviceInfo>&
        scanned_device_list_so_far,
    const multidevice::RemoteDeviceRefList&
        gms_core_notifications_disabled_devices,
    bool is_final_scan_result) {
  size_t old_size = device_id_to_name_map_.size();

  // Insert all names gathered by this scan to |device_id_to_name_map_|.
  for (const auto& remote_device : gms_core_notifications_disabled_devices)
    device_id_to_name_map_[remote_device.GetDeviceId()] = remote_device.name();

  bool names_changed = old_size < device_id_to_name_map_.size();

  // Iterate through |device_id_to_name_map_| and remove entries which are no
  // longer valid given the newest scan results.
  auto it = device_id_to_name_map_.begin();
  while (it != device_id_to_name_map_.end()) {
    // A device has enabled notifications if it is included in the list of
    // scanned devices (only valid tether hosts are present in the list).
    bool device_enabled_notifications =
        ContainsDeviceWithId(it->first, scanned_device_list_so_far);

    // If this is the final scan result for this scan session and
    // |gms_core_notifications_disabled_devices| does not contain a given
    // device, the device was not found in the scan, and there is no way of
    // knowing whether that device has its notifications enabled or not.
    bool device_no_longer_found =
        is_final_scan_result &&
        !ContainsDeviceWithId(it->first,
                              gms_core_notifications_disabled_devices);

    if (device_enabled_notifications || device_no_longer_found) {
      it = device_id_to_name_map_.erase(it);
      names_changed = true;
    } else {
      ++it;
    }
  }

  if (names_changed)
    SendDeviceNamesChangeEvent();
}

void GmsCoreNotificationsStateTrackerImpl::SendDeviceNamesChangeEvent() {
  std::stringstream ss;
  ss << "GmsCore notifications disabled device list changed. Current list: [";

  if (!device_id_to_name_map_.empty()) {
    for (const auto& map_entry : device_id_to_name_map_) {
      ss << "{name: \"" << map_entry.second << "\", id: \""
         << multidevice::RemoteDeviceRef::TruncateDeviceIdForLogs(
                map_entry.first)
         << "\"},";
    }
    // Move backward one character so that the final trailing comma will be
    // replaced by the ']' character below.
    ss.seekp(-1, ss.cur);
  }
  ss << "]";
  PA_LOG(VERBOSE) << ss.str();

  NotifyGmsCoreNotificationStateChanged();
}

}  // namespace tether

}  // namespace chromeos
