/* Copyright 2015 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. */

/**
 * @fileoverview
 * 'cr-settings-prefs' is an element which serves as a model for
 * interaction with settings which are stored in Chrome's
 * Preferences.
 *
 * Example:
 *
 *    <cr-settings-prefs id="prefs"></cr-settings-prefs>
 *    <cr-settings-a11y-page prefs="{{this.$.prefs}}"></cr-settings-a11y-page>
 *
 * @group Chrome Settings Elements
 * @element cr-settings-a11y-page
 */
(function() {
  'use strict';

  Polymer({
    is: 'cr-settings-prefs',

    properties: {
      /**
       * Object containing all preferences.
       */
      prefStore: {
        type: Object,
        value: function() { return {}; },
        notify: true,
      },
    },

    /** @override */
    created: function() {
      CrSettingsPrefs.isInitialized = false;

      chrome.settingsPrivate.onPrefsChanged.addListener(
          this.onPrefsChanged_.bind(this));
      chrome.settingsPrivate.getAllPrefs(this.onPrefsFetched_.bind(this));
    },

    /**
     * Called when prefs in the underlying Chrome pref store are changed.
     * @param {!Array<!chrome.settingsPrivate.PrefObject>} prefs The prefs that
     *     changed.
     * @private
     */
    onPrefsChanged_: function(prefs) {
      this.updatePrefs_(prefs, false);
    },

    /**
     * Called when prefs are fetched from settingsPrivate.
     * @param {!Array<!chrome.settingsPrivate.PrefObject>} prefs
     * @private
     */
    onPrefsFetched_: function(prefs) {
      this.updatePrefs_(prefs, true);

      CrSettingsPrefs.isInitialized = true;
      document.dispatchEvent(new Event(CrSettingsPrefs.INITIALIZED));
    },


    /**
     * Updates the settings model with the given prefs.
     * @param {!Array<!chrome.settingsPrivate.PrefObject>} prefs
     * @param {boolean} shouldObserve Whether to add an ObjectObserver for each
     *     of the prefs.
     * @private
     */
    updatePrefs_: function(prefs, shouldObserve) {
      prefs.forEach(function(prefObj) {
        let root = this.prefStore;
        let tokens = prefObj.key.split('.');

        assert(tokens.length > 0);

        for (let i = 0; i < tokens.length; i++) {
          let token = tokens[i];

          if (!root.hasOwnProperty(token)) {
            let path = 'prefStore.' + tokens.slice(0, i + 1).join('.');
            this.setPathValue(path, {});
          }
          root = root[token];
        }

        // NOTE: Do this copy rather than just a re-assignment, so that the
        // ObjectObserver fires.
        for (let objKey in prefObj) {
          let path = 'prefStore.' + prefObj.key + '.' + objKey;
          this.setPathValue(path, prefObj[objKey]);
        }

        if (shouldObserve) {
          let keyObserver = new ObjectObserver(root);
          keyObserver.open(
              this.propertyChangeCallback_.bind(this, prefObj.key));
        }
      }, this);
    },

    /**
     * Called when a property of a pref changes.
     * @param {string} propertyPath The path before the property names.
     * @param {!Array<string>} added An array of keys which were added.
     * @param {!Array<string>} removed An array of keys which were removed.
     * @param {!Array<string>} changed An array of keys of properties whose
     *     values changed.
     * @param {function(string) : *} getOldValueFn A function which takes a
     *     property name and returns the old value for that property.
     * @private
     */
    propertyChangeCallback_: function(
        propertyPath, added, removed, changed, getOldValueFn) {
      for (let property in changed) {
        // UI should only be able to change the value of a setting for now, not
        // disabled, etc.
        assert(property == 'value');

        let newValue = changed[property];
        assert(newValue !== undefined);

        chrome.settingsPrivate.setPref(
            propertyPath,
            newValue,
            /* pageId */ '',
            /* callback */ function() {});
      }
    },
  });
})();

@@ -1934,41 +1934,35 @@
 her 
-to add an ObjectObserver for each
+each of the prefs should be
 %0A   
@@ -1970,27 +1970,23 @@
  *     o
-f the prefs
+bserved
 .%0A     *
@@ -2593,23 +2593,17 @@
      // 
-ObjectO
+o
 bserver 
@@ -2817,93 +2817,28 @@
     
-let keyObserver = new 
 Object
-O
+.o
 bserve
-r
 (root
-);%0A          keyObserver.open(%0A             
+,
  thi
@@ -2866,32 +2866,20 @@
 ack_
-.bind(this, prefObj.key)
+, %5B'update'%5D
 );%0A 
@@ -2987,623 +2987,251 @@
 am %7B
-string%7D propertyPath The path before the property names.%0A     * @param %7B!Array%3Cstring%3E%7D added An array of keys which were added.%0A     * @param %7B!Array%3Cstring%3E%7D removed An array of keys which were removed.%0A     * @param %7B!Array%3Cstring%3E%7D changed An array of keys of properties whose%0A     *     values changed.%0A     * @param %7Bfunction(string) : *%7D getOldValueFn A function which takes a%0A     *     property name and returns the old value for that property.%0A     * @private%0A     */%0A    propertyChangeCallback_: function(%0A        propertyPath, added, removed, changed, getOldValueFn) %7B%0A      for (let property in 
+!Array%3C!Object%3E%7D changes An array of objects describing changes.%0A     *     @see http://www.html5rocks.com/en/tutorials/es7/observe/%0A     * @private%0A     */%0A    propertyChangeCallback_: function(changes) %7B%0A      changes.forEach(function(
 change
-d
 ) %7B%0A
@@ -3351,24 +3351,27 @@
  assert(
-property
+change.name
  == 'val
@@ -3410,18 +3410,27 @@
 ange
-d%5Bproperty
+.object%5Bchange.name
 %5D;%0A 
@@ -3525,20 +3525,28 @@
     
-propertyPath
+change.object%5B'key'%5D
 ,%0A  
@@ -3640,24 +3640,26 @@
 %7B%7D);%0A      %7D
+);
 %0A    %7D,%0A  %7D)

Switch prefs.js to Object.observe now that observe-js is no longer a dep.