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

#include "components/prefs/segregated_pref_store.h"

#include <string_view>
#include <utility>

#include "base/barrier_closure.h"
#include "base/check_op.h"
#include "base/notreached.h"
#include "base/observer_list.h"
#include "base/values.h"
#include "components/prefs/pref_name_set.h"

SegregatedPrefStore::UnderlyingPrefStoreObserver::UnderlyingPrefStoreObserver(
    SegregatedPrefStore* outer)
    : outer_(outer) {
  DCHECK(outer_);
}

void SegregatedPrefStore::UnderlyingPrefStoreObserver::OnPrefValueChanged(
    std::string_view key) {
  // Notify Observers only after all underlying PrefStores of the outer
  // SegregatedPrefStore are initialized.
  if (!outer_->IsInitializationComplete()) {
    return;
  }
  outer_->observers_.NotifyAllowReentrancy(
      &PrefStore::Observer::OnPrefValueChanged, key);
}

void SegregatedPrefStore::UnderlyingPrefStoreObserver::
    OnInitializationCompleted(bool succeeded) {
  initialization_succeeded_ = succeeded;

  // Notify Observers only after all underlying PrefStores of the outer
  // SegregatedPrefStore are initialized.
  if (!outer_->IsInitializationComplete()) {
    return;
  }

  if (outer_->read_error_delegate_.has_value() &&
      outer_->read_error_delegate_.value()) {
    PersistentPrefStore::PrefReadError read_error = outer_->GetReadError();
    if (read_error != PersistentPrefStore::PREF_READ_ERROR_NONE) {
      outer_->read_error_delegate_.value()->OnError(read_error);
    }
  }

  for (auto& observer : outer_->observers_) {
    observer.OnInitializationCompleted(outer_->IsInitializationSuccessful());
  }
}

SegregatedPrefStore::SegregatedPrefStore(
    scoped_refptr<PersistentPrefStore> default_pref_store,
    scoped_refptr<PersistentPrefStore> selected_pref_store,
    PrefNameSet selected_pref_names)
    : default_pref_store_(std::move(default_pref_store)),
      selected_pref_store_(std::move(selected_pref_store)),
      selected_preference_names_(std::move(selected_pref_names)),
      default_observer_(this),
      selected_observer_(this) {
  default_pref_store_->AddObserver(&default_observer_);
  selected_pref_store_->AddObserver(&selected_observer_);
}

void SegregatedPrefStore::AddObserver(Observer* observer) {
  observers_.AddObserver(observer);
}

void SegregatedPrefStore::RemoveObserver(Observer* observer) {
  observers_.RemoveObserver(observer);
}

bool SegregatedPrefStore::HasObservers() const {
  return !observers_.empty();
}

bool SegregatedPrefStore::IsInitializationComplete() const {
  return default_pref_store_->IsInitializationComplete() &&
         selected_pref_store_->IsInitializationComplete();
}

bool SegregatedPrefStore::IsInitializationSuccessful() const {
  return default_observer_.initialization_succeeded() &&
         selected_observer_.initialization_succeeded();
}

bool SegregatedPrefStore::GetValue(std::string_view key,
                                   const base::Value** result) const {
  return StoreForKey(key)->GetValue(key, result);
}

base::DictValue SegregatedPrefStore::GetValues() const {
  base::DictValue values = default_pref_store_->GetValues();
  base::DictValue selected_pref_store_values =
      selected_pref_store_->GetValues();
  for (const auto& key : selected_preference_names_) {
    if (base::Value* value = selected_pref_store_values.FindByDottedPath(key)) {
      values.SetByDottedPath(key, std::move(*value));
    } else {
      values.Remove(key);
    }
  }
  return values;
}

void SegregatedPrefStore::SetValue(std::string_view key,
                                   base::Value value,
                                   uint32_t flags) {
  StoreForKey(key)->SetValue(key, std::move(value), flags);
}

void SegregatedPrefStore::RemoveValue(std::string_view key, uint32_t flags) {
  StoreForKey(key)->RemoveValue(key, flags);
}

void SegregatedPrefStore::RemoveValuesByPrefixSilently(
    std::string_view prefix) {
  // Since we can't guarantee to have all the prefs in one the pref stores, we
  // have to push the removal command down to both of them.
  default_pref_store_->RemoveValuesByPrefixSilently(prefix);
  selected_pref_store_->RemoveValuesByPrefixSilently(prefix);
}

bool SegregatedPrefStore::GetMutableValue(std::string_view key,
                                          base::Value** result) {
  return StoreForKey(key)->GetMutableValue(key, result);
}

void SegregatedPrefStore::ReportValueChanged(std::string_view key,
                                             uint32_t flags) {
  StoreForKey(key)->ReportValueChanged(key, flags);
}

void SegregatedPrefStore::SetValueSilently(std::string_view key,
                                           base::Value value,
                                           uint32_t flags) {
  StoreForKey(key)->SetValueSilently(key, std::move(value), flags);
}

bool SegregatedPrefStore::ReadOnly() const {
  return selected_pref_store_->ReadOnly() || default_pref_store_->ReadOnly();
}

PersistentPrefStore::PrefReadError SegregatedPrefStore::GetReadError() const {
  PersistentPrefStore::PrefReadError read_error =
      default_pref_store_->GetReadError();
  if (read_error == PersistentPrefStore::PREF_READ_ERROR_NONE) {
    read_error = selected_pref_store_->GetReadError();
    // Ignore NO_FILE from selected_pref_store_.
    if (read_error == PersistentPrefStore::PREF_READ_ERROR_NO_FILE) {
      read_error = PersistentPrefStore::PREF_READ_ERROR_NONE;
    }
  }
  return read_error;
}

PersistentPrefStore::PrefReadError SegregatedPrefStore::ReadPrefs() {
  // Note: Both of these stores own PrefFilters which makes ReadPrefs
  // asynchronous. This is okay in this case as only the first call will be
  // truly asynchronous, the second call will then unblock the migration in
  // TrackedPreferencesMigrator and complete synchronously.
  default_pref_store_->ReadPrefs();
  PersistentPrefStore::PrefReadError selected_store_read_error =
      selected_pref_store_->ReadPrefs();
  DCHECK_NE(PersistentPrefStore::PREF_READ_ERROR_ASYNCHRONOUS_TASK_INCOMPLETE,
            selected_store_read_error);

  return GetReadError();
}

void SegregatedPrefStore::ReadPrefsAsync(ReadErrorDelegate* error_delegate) {
  read_error_delegate_.emplace(error_delegate);
  default_pref_store_->ReadPrefsAsync(nullptr);
  selected_pref_store_->ReadPrefsAsync(nullptr);
}

void SegregatedPrefStore::CommitPendingWrite(
    base::OnceClosure reply_callback,
    base::OnceClosure synchronous_done_callback) {
  // A BarrierClosure will run its callback wherever the last instance of the
  // returned wrapper is invoked. As such it is guaranteed to respect the reply
  // vs synchronous semantics assuming |default_pref_store_| and
  // |selected_pref_store_| honor it.

  base::RepeatingClosure reply_callback_wrapper =
      reply_callback ? base::BarrierClosure(2, std::move(reply_callback))
                     : base::RepeatingClosure();

  base::RepeatingClosure synchronous_callback_wrapper =
      synchronous_done_callback
          ? base::BarrierClosure(2, std::move(synchronous_done_callback))
          : base::RepeatingClosure();

  default_pref_store_->CommitPendingWrite(reply_callback_wrapper,
                                          synchronous_callback_wrapper);
  selected_pref_store_->CommitPendingWrite(reply_callback_wrapper,
                                           synchronous_callback_wrapper);
}

void SegregatedPrefStore::SchedulePendingLossyWrites() {
  default_pref_store_->SchedulePendingLossyWrites();
  selected_pref_store_->SchedulePendingLossyWrites();
}

void SegregatedPrefStore::OnStoreDeletionFromDisk() {
  default_pref_store_->OnStoreDeletionFromDisk();
  selected_pref_store_->OnStoreDeletionFromDisk();
}

SegregatedPrefStore::~SegregatedPrefStore() {
  default_pref_store_->RemoveObserver(&default_observer_);
  selected_pref_store_->RemoveObserver(&selected_observer_);
}

PersistentPrefStore* SegregatedPrefStore::StoreForKey(std::string_view key) {
  return (selected_preference_names_.contains(key) ? selected_pref_store_
                                                   : default_pref_store_)
      .get();
}

const PersistentPrefStore* SegregatedPrefStore::StoreForKey(
    std::string_view key) const {
  return (selected_preference_names_.contains(key) ? selected_pref_store_
                                                   : default_pref_store_)
      .get();
}

bool SegregatedPrefStore::HasReadErrorDelegate() const {
  return read_error_delegate_.has_value();
}

PrefFilter* SegregatedPrefStore::GetDefaultStoreFilter() {
  return default_pref_store_->GetFilter();
}

PrefFilter* SegregatedPrefStore::GetSelectedStoreFilter() {
  return selected_pref_store_ ? selected_pref_store_->GetFilter() : nullptr;
}
