// Copyright (c) 2012 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.

#ifndef CHROME_BROWSER_CHROMEOS_SETTINGS_CROS_SETTINGS_H_
#define CHROME_BROWSER_CHROMEOS_SETTINGS_CROS_SETTINGS_H_

#include <string>
#include <vector>

#include "base/callback_forward.h"
#include "base/callback_list.h"
#include "base/containers/hash_tables.h"
#include "base/memory/scoped_ptr.h"
#include "base/threading/non_thread_safe.h"
#include "chromeos/settings/cros_settings_names.h"
#include "chromeos/settings/cros_settings_provider.h"

namespace base {
class DictionaryValue;
class ListValue;
class Value;
}

namespace chromeos {

class DeviceSettingsService;

// This class manages per-device/global settings.
class CrosSettings : public base::NonThreadSafe {
 public:
  // Manage singleton instance.
  static void Initialize();
  static bool IsInitialized();
  static void Shutdown();
  static CrosSettings* Get();

  // Creates a device settings service instance. This is meant for unit tests,
  // production code uses the singleton returned by Get() above.
  explicit CrosSettings(DeviceSettingsService* device_settings_service);
  virtual ~CrosSettings();

  // Helper function to test if the given |path| is a valid cros setting.
  static bool IsCrosSettings(const std::string& path);

  // Sets |in_value| to given |path| in cros settings.
  void Set(const std::string& path, const base::Value& in_value);

  // Returns setting value for the given |path|.
  const base::Value* GetPref(const std::string& path) const;

  // Requests all providers to fetch their values from a trusted store, if they
  // haven't done so yet. Returns true if the cros settings returned by |this|
  // are trusted during the current loop cycle; otherwise returns false, and
  // |callback| will be invoked later when trusted values become available.
  // PrepareTrustedValues() should be tried again in that case.
  virtual CrosSettingsProvider::TrustedStatus PrepareTrustedValues(
      const base::Closure& callback) const;

  // Convenience forms of Set().  These methods will replace any existing
  // value at that |path|, even if it has a different type.
  void SetBoolean(const std::string& path, bool in_value);
  void SetInteger(const std::string& path, int in_value);
  void SetDouble(const std::string& path, double in_value);
  void SetString(const std::string& path, const std::string& in_value);

  // Convenience functions for manipulating lists. Note that the following
  // functions employs a read, modify and write pattern. If underlying settings
  // provider updates its value asynchronously such as DeviceSettingsProvider,
  // value cache they read from might not be fresh and multiple calls to those
  // function would lose data. See http://crbug.com/127215
  void AppendToList(const std::string& path, const base::Value* value);
  void RemoveFromList(const std::string& path, const base::Value* value);

  // These are convenience forms of Get().  The value will be retrieved
  // and the return value will be true if the |path| is valid and the value at
  // the end of the path can be returned in the form specified.
  bool GetBoolean(const std::string& path, bool* out_value) const;
  bool GetInteger(const std::string& path, int* out_value) const;
  bool GetDouble(const std::string& path, double* out_value) const;
  bool GetString(const std::string& path, std::string* out_value) const;
  bool GetList(const std::string& path,
               const base::ListValue** out_value) const;
  bool GetDictionary(const std::string& path,
                     const base::DictionaryValue** out_value) const;

  // Helper function for the whitelist op. Implemented here because we will need
  // this in a few places. The functions searches for |email| in the pref |path|
  // It respects whitelists so foo@bar.baz will match *@bar.baz too. If the
  // match was via a wildcard, |wildcard_match| is set to true.
  bool FindEmailInList(const std::string& path,
                       const std::string& email,
                       bool* wildcard_match) const;

  // Adding/removing of providers.
  bool AddSettingsProvider(CrosSettingsProvider* provider);
  bool RemoveSettingsProvider(CrosSettingsProvider* provider);

  // Add an observer Callback for changes for the given |path|.
  typedef base::CallbackList<void(void)>::Subscription ObserverSubscription;
  scoped_ptr<ObserverSubscription> AddSettingsObserver(
      const std::string& path,
      const base::Closure& callback);

  // Returns the provider that handles settings with the |path| or prefix.
  CrosSettingsProvider* GetProvider(const std::string& path) const;

 private:
  friend class CrosSettingsTest;

  // Fires system setting change callback.
  void FireObservers(const std::string& path);

  // List of ChromeOS system settings providers.
  std::vector<CrosSettingsProvider*> providers_;

  // A map from settings names to a list of observers. Observers get fired in
  // the order they are added.
  typedef base::hash_map<std::string, base::CallbackList<void(void)>*>
      SettingsObserverMap;
  SettingsObserverMap settings_observers_;

  DISALLOW_COPY_AND_ASSIGN(CrosSettings);
};

// Helper class for tests. Initializes the CrosSettings singleton on
// construction and tears it down again on destruction.
class ScopedTestCrosSettings {
 public:
  ScopedTestCrosSettings();
  ~ScopedTestCrosSettings();

 private:
  DISALLOW_COPY_AND_ASSIGN(ScopedTestCrosSettings);
};

}  // namespace chromeos

#endif  // CHROME_BROWSER_CHROMEOS_SETTINGS_CROS_SETTINGS_H_