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

#include "ui/accessibility/platform/ax_unique_id.h"

#include <memory>
#include <unordered_set>

#include "base/containers/contains.h"
#include "base/lazy_instance.h"
#include "base/logging.h"

namespace ui {

namespace {

base::LazyInstance<std::unordered_set<int32_t>>::Leaky g_assigned_ids =
    LAZY_INSTANCE_INITIALIZER;

}  // namespace

AXUniqueId::AXUniqueId() : AXUniqueId(INT32_MAX) {}

AXUniqueId::AXUniqueId(const int32_t max_id) : id_(GetNextAXUniqueId(max_id)) {}

AXUniqueId::~AXUniqueId() {
  g_assigned_ids.Get().erase(id_);
}

bool AXUniqueId::operator==(const AXUniqueId& other) const {
  return Get() == other.Get();
}

bool AXUniqueId::operator!=(const AXUniqueId& other) const {
  return !(*this == other);
}

bool AXUniqueId::IsAssigned(const int32_t id) const {
  return base::Contains(g_assigned_ids.Get(), id);
}

int32_t AXUniqueId::GetNextAXUniqueId(const int32_t max_id) {
  static int32_t current_id = 0;
  static bool has_wrapped = false;

  const int32_t prev_id = current_id;
  do {
    if (current_id >= max_id) {
      current_id = 1;
      has_wrapped = true;
    } else {
      ++current_id;
    }
    if (current_id == prev_id) {
      LOG(FATAL) << "There are over 2 billion available IDs, so the newly "
                    "created ID cannot be equal to the most recently created "
                    "ID.";
    }
    // If it |has_wrapped| then we need to continue until we find the first
    // unassigned ID.
  } while (has_wrapped && IsAssigned(current_id));

  g_assigned_ids.Get().insert(current_id);
  return current_id;
}

}  // namespace ui