/**
 * This class manages arbitrary data and its relationship to data models. Instances of
 * `ViewModel` are associated with some `Component` and then used by their child items
 * for the purposes of Data Binding.
 * 
 * # Binding
 * 
 * The most commonly used aspect of a `ViewModel` is the `bind` method. This method takes
 * a "bind descriptor" (see below) and a callback to call when the data indicated by the
 * bind descriptor either becomes available or changes.
 *
 * The `bind` method, based on the bind descriptor given, will return different types of
 * "binding" objects. These objects maintain the connection between the requested data and
 * the callback. Bindings ultimately derive from `{@link Ext.app.bind.BaseBinding}`
 * which provides several methods to help manage the binding.
 *
 * Perhaps the most important method is `destroy`. When the binding is no longer needed
 * it is important to remember to `destroy` it. Leaking bindings can cause performance
 * problems or worse when callbacks are called at unexpected times.
 *
 * The types of bindings produced by `bind` are:
 *
 *   * `{@link Ext.app.bind.Binding}`
 *   * `{@link Ext.app.bind.Multi}`
 *   * `{@link Ext.app.bind.TemplateBinding}`
 *
 * ## Bind Descriptors
 * 
 * A "bind descriptor" is a value (a String, an Object or an array of these) that describe
 * the desired data. Any piece of data in the `ViewModel` can be described by a bind
 * descriptor.
 * 
 * ### Textual Bind Descriptors
 * 
 * The simplest and most common form of bind descriptors are strings that look like an
 * `Ext.Template` containing text and tokens surrounded by "{}" with dot notation inside
 * to traverse objects and their properties.
 * 
 * For example:
 * 
 *   * `'Hello {user.name}!'`
 *   * `'You have selected "{selectedItem.text}".'`
 *   * `'{!isDisabled}'`
 *   * `'{a > b ? "Bigger" : "Smaller"}'`
 *   * `'{user.groups}'`
 *
 * All except the last are `{@link Ext.app.bind.TemplateBinding template bindings}`
 * which use the familiar `Ext.Template` syntax with some slight differences. For more on
 * templates see `{@link Ext.app.bind.Template}`.
 *
 * The last descriptor is called a "direct bind descriptor". This special form of
 * bind maps one-to-one to some piece of data in the `ViewModel` and is managed by the
 * `{@link Ext.app.bind.Binding}` class.
 *
 * #### Two-Way Descriptors
 *
 * A direct bind descriptor may be able to write back a value to the `ViewModel` as well
 * as retrieve one. When this is the case, they are said to be "two-way". For example:
 *
 *      var binding = viewModel.bind('{s}', function(s) { console.log('s=' + s); });
 *
 *      binding.setValue('abc');
 *
 * Direct use of `ViewModel` in this way is not commonly needed because `Ext.Component`
 * automates this process. For example, a `textfield` component understands when it is
 * given a "two-way" binding and automatically synchronizes its value bidirectionally using
 * the above technique. For example:
 *
 *      Ext.widget({
 *          items: [{
 *              xtype: 'textfield',
 *              bind: '{s}'  // a two-way / direct bind descriptor
 *          }]
 *      });
 *
 * ### Object and Array Descriptors / Multi-Bind
 *
 * With two exceptions (see below) an Object is interpreted as a "shape" to produce by
 * treating each of its properties as individual bind descriptors. An object of the same
 * shape is passed as the value of the bind except that each property is populated with
 * the appropriate value. Of course, this definition is recursive, so these properties
 * may also be objects.
 *
 * For example:
 *
 *      viewModel.bind({
 *              x: '{x}',
 *              foo: {
 *                  bar: 'Hello {foo.bar}'
 *              }
 *          },
 *          function (obj) {
 *              //  obj = {
 *              //      x: 42,
 *              //      foo: {
 *              //          bar: 'Hello foobar'
 *              //      }
 *              //  }
 *          });
 *
 * Arrays are handled in the same way. Each element of the array is considered a bind
 * descriptor (recursively) and the value produced for the binding is an array with each
 * element set to the bound property.
 *
 * ### Bind Options
 *
 * One exception to the "object is a multi-bind" rule is when that object contains a
 * `bindTo` property. When an object contains a `bindTo` property the object is understood
 * to contain bind options and the value of `bindTo` is considered the actual bind
 * descriptor.
 *
 * For example:
 *
 *      viewModel.bind({
 *              bindTo: '{x}',
 *              single: true
 *          },
 *          function (x) {
 *              console.log('x: ' + x); // only called once
 *          });
 *
 * The available bind options depend on the type of binding, but since all bindings
 * derive from `{@link Ext.app.bind.BaseBinding}` its options are always applicable.
 * For a list of the other types of bindings, see above.
 *
 * #### Deep Binding
 *
 * When a direct bind is made and the bound property is an object, by default the binding
 * callback is only called when that reference changes. This is the most efficient way to
 * understand a bind of this type, but sometimes you may need to be notified if any of the
 * properties of that object change.
 *
 * To do this, we create a "deep bind":
 *
 *      viewModel.bind({
 *              bindTo: '{someObject}',
 *              deep: true
 *          },
 *          function (someObject) {
 *              // called when reference changes or *any* property changes
 *          });
 *
 * #### Binding Timings
 *
 * The `ViewModel` has a {@link #scheduler} attached that is used to coordinate the firing of bindings.
 * It serves 2 main purposes:
 * - To coordinate dependencies between bindings. This means bindings will be fired in an order such that
 * the any dependencies for a binding are fired before the binding itself.
 * - To batch binding firings. The scheduler runs on a short timer, so the following code will only trigger
 * a single binding (the last), the changes in between will never be triggered.
 *
 * Example:
 *
 *     viewModel.bind('{val}', function(v) {
 *         console.log(v);
 *     });
 *     viewModel.set('val', 1);
 *     viewModel.set('val', 2);
 *     viewModel.set('val', 3);
 *     viewModel.set('val', 4);
 *
 * The `ViewModel` can be forced to process by calling `{@link #notify}`, which will force the
 * scheduler to run immediately in the current state.
 * 
 *     viewModel.bind('{val}', function(v) {
 *         console.log(v);
 *     });
 *     viewModel.set('val', 1);
 *     viewModel.notify();
 *     viewModel.set('val', 2);
 *     viewModel.notify();
 *     viewModel.set('val', 3);
 *     viewModel.notify();
 *     viewModel.set('val', 4);
 *     viewModel.notify();
 *  
 *
 * #### Models, Stores and Associations
 *
 * A {@link Ext.data.Session Session} manages model instances and their associations.
 * The `ViewModel` may be used with or without a `Session`. When a `Session` is attached, the
 * `ViewModel` will always consult the `Session` to ask about records and stores. The `Session`
 * ensures that only a single instance of each model Type/Id combination is created. This is 
 * important when tracking changes in models so that we always have the same reference.
 *
 * A `ViewModel` provides functionality to easily consume the built in data package types
 * {@link Ext.data.Model} and {@link Ext.data.Store}, as well as their associations.
 *
 * ### Model Links
 *
 * A model can be described declaratively using {@link #links}. In the example code below,
 * We ask the `ViewModel` to construct a record of type `User` with `id: 17`. The model will be loaded
 * from the server and the bindings will trigger once the load has completed. Similarly, we could also
 * attach a model instance to the `ViewModel` data directly.
 *
 *     Ext.define('MyApp.model.User', {
 *         extend: 'Ext.data.Model',
 *         fields: ['name']
 *     });
 *     
 *     var rec = new MyApp.model.User({
 *         id: 12,
 *         name: 'Foo'
 *     });
 *     
 *     var viewModel = new Ext.app.ViewModel({
 *         links: {
 *             theUser: {
 *                 type: 'User',
 *                 id: 17
 *             }
 *         },
 *         data: {
 *             otherUser: rec
 *         }
 *     });
 *     viewModel.bind('{theUser.name}', function(v) {
 *         console.log(v);
 *     });
 *     viewModel.bind('{otherUser.name}', function(v) {
 *         console.log(v);
 *     });
 *
 * ### Model Fields
 *
 * Bindings have the functionality to inspect the parent values and resolve the underlying
 * value dynamically. This behavior allows model fields to be interrogated as part of a binding.
 *
 *     Ext.define('MyApp.model.User', {
 *         extend: 'Ext.data.Model',
 *         fields: ['name', 'age']
 *     });
 *
 *     var viewModel = new Ext.app.ViewModel({
 *         links: {
 *             theUser: {
 *                 type: 'User',
 *                 id: 22
 *             }
 *         }
 *     });
 *
 *     // Server responds with:
 *     {
 *         "id": 22,
 *         "name": "Foo",
 *         "age": 100
 *     }
 *
 *     viewModel.bind('Hello {name}, you are {age} years old', function(v) {
 *         console.log(v);
 *     });
 *
 * ### Record Properties
 *
 * It is possible to bind to the certain state properties of a record. The available options are:
 * - `{@link Ext.data.Model#property-dirty dirty}`
 * - `{@link Ext.data.Model#property-phantom phantom}`
 * - `{@link Ext.data.Model#method-isValid valid}`
 *
 * Example usage:
 *
 *     Ext.define('MyApp.model.User', {
 *         extend: 'Ext.data.Model',
 *         fields: [{
 *             name: 'name',
 *             validators: 'presence'
 *         }, {
 *             name: 'age',
 *             validators: {
 *                type: 'range',
 *                 min: 0
 *              }
 *         }]
 *     });
 *
 *     var rec = new MyApp.model.User();
 *
 *     var viewModel = new Ext.app.ViewModel({
 *         data: {
 *             theUser: rec
 *         }
 *     });
 *
 *     viewModel.bind({
 *         dirty: '{theUser.dirty}',
 *         phantom: '{theUser.phantom}',
 *         valid: '{theUser.valid}'
 *     }, function(v) {
 *         console.log(v.dirty, v.valid);
 *     });
 *
 *     rec.set('name', 'Foo');
 *     viewModel.notify(); // dirty, not valid
 *     rec.set('age', 20);
 *     viewModel.notify(); // dirty, valid
 *     rec.reject();
 *     viewModel.notify(); // not dirty, not valid
 *
 * ### Advanced Record Binding
 *
 * For accessing other record information that is not exposed by the binding API, formulas
 * can be used to achieve more advanced operations:
 *
 *     Ext.define('MyApp.model.User', {
 *         extend: 'Ext.data.Model',
 *         fields: ['name', 'age']
 *     });
 *
 *     var rec = new MyApp.model.User();
 *
 *     var viewModel = new Ext.app.ViewModel({
 *         formulas: {
 *             isNameModified: {
 *                 bind: {
 *                     bindTo: '{theUser}',
 *                     deep: true
 *                 },
 *                 get: function(rec) {
 *                     return rec.isModified('name');
 *                 }
 *             }
 *         },
 *         data: {
 *             theUser: rec
 *         }
 *     });
 *
 *     viewModel.bind('{isNameModified}', function(modified) {
 *         console.log(modified);
 *     });
 *     rec.set('name', 'Foo');
 *
 * ### Associations
 *
 * In the same way as fields, the bindings can also traverse associations in a bind statement.
 * The `ViewModel` will handle the asynchronous loading of data and only present the value once
 * the full path has been loaded. For more information on associations see {@link Ext.data.schema.OneToOne OneToOne} and
 * {@link Ext.data.schema.ManyToOne ManyToOne} associations.
 *
 *     Ext.define('User', {
 *         extend: 'Ext.data.Model',
 *         fields: ['name']
 *     });
 *
 *     Ext.define('Order', {
 *         extend: 'Ext.data.Model',
 *         fields: ['date', {
 *             name: 'userId',
 *             reference: 'User'
 *         }]
 *     });
 *
 *     Ext.define('OrderItem', {
 *         extend: 'Ext.data.Model',
 *         fields: ['price', 'qty', {
 *             name: 'orderId',
 *             reference: 'Order'
 *         }]
 *     });
 *
 *     var viewModel = new Ext.app.ViewModel({
 *         links: {
 *             orderItem: {
 *                 type: 'OrderItem',
 *                 id: 13
 *             }
 *         }
 *     });
 *     // The viewmodel will handle both ways of loading the data:
 *     // a) If the data is loaded inline in a nested fashion it will
 *     //    not make requests for extra data
 *     // b) Only loading a single model at a time. So the Order will be loaded once
 *     //    the OrderItem returns. The User will be loaded once the Order loads.
 *     viewModel.bind('{orderItem.order.user.name}', function(name) {
 *         console.log(name);
 *     });
 *
 * ### Stores
 *
 * Stores can be created as part of the `ViewModel` definition. The definitions are processed
 * like bindings which allows for very powerful dynamic functionality.
 *
 * It is important to ensure that you name viewModel's data keys uniquely. If data is not named  
 * uniquely, binds and formulas may receive information from an unintended data source.  
 * This applies to keys in the viewModel's data block, stores, and links configs.
 *
 *     var viewModel = new Ext.app.ViewModel({
 *         stores: {
 *             users: {
 *                 model: 'User',
 *                 autoLoad: true,
 *                 filters: [{
 *                     property: 'createdDate',
 *                     value: '{createdFilter}',
 *                     operator: '>'
 *                 }]
 *             }
 *         }
 *     });
 *     // Later on in our code, we set the date so that the store is created.
 *     viewModel.set('createdFilter', Ext.Date.subtract(new Date(), Ext.Date.DAY, 7));
 *
 * See {@link #stores} for more detail.
 *
 * ### Store Properties
 *
 * It is possible to bind to the certain state properties of the store. The available options are:
 * - `{@link Ext.data.Store#method-getCount count}`
 * - `{@link Ext.data.Store#method-first}`
 * - `{@link Ext.data.Store#method-last}`
 * - `{@link Ext.data.Store#method-hasPendingLoad loading}`
 * - `{@link Ext.data.Store#method-getTotalCount totalCount}`
 *
 * Example:
 *
 *     Ext.define('MyApp.model.User', {
 *         extend: 'Ext.data.Model',
 *         fields: ['name']
 *     });
 *
 *     var viewModel = new Ext.app.ViewModel({
 *         stores: {
 *             users: {
 *                 model: 'MyApp.model.User',
 *                 data: [{
 *                     name: 'Foo'
 *                 }, {
 *                     name: 'Bar'
 *                 }]
 *             }
 *         }
 *     });
 *
 *     viewModel.bind('{users.first}', function(first) {
 *         console.log(first ? first.get('name') : 'Nobody');
 *     });
 *
 *     var timer = Ext.interval(function() {
 *         var store = viewModel.getStore('users');
 *         if (store.getCount()) {
 *             store.removeAt(0);
 *         } else {
 *             Ext.uninterval(timer);
 *         }
 *     }, 100);
 *
 * ### Advanced Store Binding
 *
 * For accessing other store information that is not exposed by the binding API, formulas
 * can be used to achieve more advanced operations:
 *
 *     Ext.define('MyApp.model.User', {
 *         extend: 'Ext.data.Model',
 *         fields: ['name', 'score']
 *     });
 *
 *     var viewModel = new Ext.app.ViewModel({
 *         stores: {
 *             users: {
 *                 model: 'MyApp.model.User',
 *                 data: [{
 *                     name: 'Foo',
 *                     score: 100
 *                 }, {
 *                     name: 'Bar',
 *                     score: 350
 *                 }]
 *             }
 *         },
 *         formulas: {
 *             totalScore: {
 *                 bind: {
 *                     bindTo: '{users}',
 *                     deep: true
 *                 },
 *                 get: function(store) {
 *                     return store.sum('score');
 *                 }
 *             }
 *         }
 *     });
 *
 *     viewModel.bind('{totalScore}', function(score) {
 *         console.log(score);
 *     });
 *
 *     viewModel.notify();
 *     viewModel.getStore('users').removeAll();
 *
 * #### Formulas
 *
 * Formulas allow for calculated `ViewModel` data values. The dependencies for these formulas
 * are automatically determined so that the formula will not be processed until the required
 * data is present.
 *
 *     var viewModel = new Ext.app.ViewModel({
 *         formulas: {
 *             fullName: function(get) {
 *                 return get('firstName') + ' ' + get('lastName');
 *             }
 *         },
 *         data: {firstName: 'John', lastName: 'Smith'}
 *     });
 *
 *     viewModel.bind('{fullName}', function(v) {
 *         console.log(v);
 *     });
 *
 * See {@link #formulas} for more detail.
 *
 * #### Inheriting Data With Nesting
 *
 * ViewModels can have a {@link #parent} which allows values to be consumed from
 * a shared base. These values that are available from the {@link #parent} are not copied,
 * rather they are "inherited" in a similar fashion to a javascript closure scope chain. 
 * This is demonstrated in the example below:
 *
 *     var parent = new Ext.app.ViewModel({
 *         data: {
 *             foo: 3
 *         }
 *     });
 *     var child = new Ext.app.ViewModel({
 *         parent: parent
 *     });
 *
 * This is analogous to the following javascript closure:
 *
 *     var foo = 3;
 *     Ext.Ajax.request({
 *         success: function() {
 *             // foo is available here
 *         }
 *     });
 *
 * ### Climbing/Inheriting
 *
 * In line with the above, the default behaviour when setting the value of a child ViewModel (either)
 * through {@link #set} or {@link Ext.app.bind.Binding#method-setValue} is to climb to where the value 
 * is "owned" and set the value there:
 *
 *     var parent = new Ext.app.ViewModel({
 *         data: {
 *             foo: 3
 *         }
 *     });
 *     var child = new Ext.app.ViewModel({
 *         parent: parent
 *     });
 *     
 *     child.set('foo', 100); // Climbs to set the value on parent
 *     console.log(parent.get('foo')); // 100
 *     parent.set('foo', 200);
 *     console.log(child.get('foo')); // 200, inherited from the parent
 *
 * Any subsequent sets are also inherited in the same fashion. The inheriting/climbing behavior 
 * occurs for any arbitrary depth, climbing/inherting can owned by a parent at any level above.
 *
 *     function log() {
 *         console.log([a, b, c, d, e].map(function(vm) {
 *             return vm.get('foo');
 *         }));
 *     }
 *
 *     var a = new Ext.app.ViewModel({data: {foo: 3}}),
 *         b = new Ext.app.ViewModel({parent: a}),
 *         c = new Ext.app.ViewModel({parent: b}),
 *         d = new Ext.app.ViewModel({parent: c}),
 *         e = new Ext.app.ViewModel({parent: d});
 *
 *     log(); // [3, 3, 3, 3, 3]
 *
 *     e.set('foo', 100);
 *     log(); // [100, 100, 100, 100, 100]
 *
 * This same climbing behavior applies when setting a value on a binding. The climbing begins from
 * the ViewModel where the binding was attached:
 *
 *     function log() {
 *         console.log([a, b, c].map(function(vm) {
 *             return vm.get('foo');
 *         }));
 *     }
 *
 *     var a = new Ext.app.ViewModel({data: {foo: 3}}),
 *         b = new Ext.app.ViewModel({parent: a}),
 *         c = new Ext.app.ViewModel({parent: b});
 *
 *     var bind = c.bind('{foo}', function() {});
 *
 *     bind.setValue(100);
 *     log(); // [100, 100, 100]
 *
 * The exception to this rule is when there is nothing above to climb to. If a value is set and there
 * is no parent above to hold it, then the value is set where it was called:
 *
 *     function log() {
 *         console.log([a, b, c].map(function(vm) {
 *             return vm.get('foo');
 *         }));
 *     }
 *
 *     var a = new Ext.app.ViewModel(),
 *         b = new Ext.app.ViewModel({parent: a}),
 *         c = new Ext.app.ViewModel({parent: b});
 *
 *     c.set('foo', 3);
 *     log(); // [null, null, 3]
 *
 *     b.set('foo', 2);
 *     log(); // [null, 2, 3]
 *
 *     a.set('foo', 1);
 *     log(); // [1, 2, 3]
 *
 * These values are called local values, which are discussed below.
 *
 * ### Local Values
 *
 * If the child ViewModel is declared with top level data that also exists in the parent, then that child is
 * considered to own that local value, so no value is inherited from the parent, nor does the climbing
 * behaviour occur.
 *
 *     var parent = new Ext.app.ViewModel({
 *         data: {
 *             foo: 3
 *         }
 *     });
 *     var child = new Ext.app.ViewModel({
 *         parent: parent,
 *         data: {
 *             foo: 5
 *         }
 *     });
 *
 *     console.log(parent.get('foo'), child.get('foo')); // 3, 5
 *     child.set('foo', 100);
 *     console.log(parent.get('foo'), child.get('foo')); // 3, 100
 *     parent.set('foo', 200);
 *     console.log(parent.get('foo'), child.get('foo')); // 200, 100
 *
 * The inheriting/climbing behavior is limited to local values:
 *
 *     function log() {
 *         console.log([a, b, c, d, e].map(function(vm) {
 *             return vm.get('foo');
 *         }));
 *     }
 *
 *     var a = new Ext.app.ViewModel({data: {foo: 1}}),
 *         b = new Ext.app.ViewModel({parent: a}),
 *         c = new Ext.app.ViewModel({parent: b, data: {foo: 2}}),
 *         d = new Ext.app.ViewModel({parent: c}),
 *         e = new Ext.app.ViewModel({parent: d, data: {foo: 3}});
 *
 *     log(); // [1, 1, 2, 2, 3]
 *
 *     e.set('foo', 100);
 *     log(); // [1, 1, 2, 2, 100]
 *
 *     d.set('foo', 200);
 *     log(); // [1, 1, 200, 200, 100]
 *
 *     c.set('foo', 201);
 *     log(); // [1, 1, 201, 201, 100]
 *
 *     b.set('foo', 300);
 *     log(); // [300, 300, 201, 201, 100]
 *
 *     a.set('foo', 301);
 *     log(); // [301, 301, 201, 201, 100]
 *
 * ### Attaching/Clearing Local Values Dynamically
 *
 * To bypass the climbing behaviour and push a value into a particular point
 * in the hierarchy, the {@link #setData} method should be used. Once a local value
 * is set, it will be used as such in the future.
 *
 *     function log() {
 *         console.log([a, b, c, d, e].map(function(vm) {
 *             return vm.get('foo');
 *         }));
 *     }
 *
 *     var a = new Ext.app.ViewModel({data: {foo: 3}}),
 *         b = new Ext.app.ViewModel({parent: a}),
 *         c = new Ext.app.ViewModel({parent: b}),
 *         d = new Ext.app.ViewModel({parent: c}),
 *         e = new Ext.app.ViewModel({parent: d});
 *
 *     log(); // [3, 3, 3, 3, 3]
 *
 *     c.setData({
 *         foo: 100
 *     });
 *
 *     log(); // [3, 3, 100, 100, 100]
 *
 *     d.set('foo', 200); // Climbs to new local value
 *     log(); // [3, 3, 200, 200, 200]
 *
 * Similarly, data can be cleared from being a local value by setting the value to undefined:
 *
 *     function log() {
 *         console.log([a, b, c, d].map(function(vm) {
 *             return vm.get('foo');
 *         }));
 *     }
 *
 *     var a = new Ext.app.ViewModel({data: {foo: 3}}),
 *         b = new Ext.app.ViewModel({parent: a}),
 *         c = new Ext.app.ViewModel({parent: b, data: {foo: 100}}),
 *         d = new Ext.app.ViewModel({parent: c});
 *
 *     log(); // [3, 3, 100, 100]
 *
 *     c.setData({
 *         foo: undefined
 *     });
 *     log([3, 3, 3, 3]);
 *
 */
Ext.define('Ext.app.ViewModel', {
    mixins: [
        'Ext.mixin.Factoryable',
        'Ext.mixin.Identifiable'
    ],
 
    requires: [
        'Ext.util.Scheduler',
        'Ext.data.Session',
        'Ext.app.bind.RootStub',
        'Ext.app.bind.LinkStub',
        'Ext.app.bind.Multi',
        'Ext.app.bind.Formula',
        'Ext.app.bind.TemplateBinding',
        // TODO: this is an injected dependency in onStoreBind, need to define so 
        // cmd can detect it
        'Ext.data.ChainedStore'
    ],
 
    alias: 'viewmodel.default', // also configures Factoryable
 
    isViewModel: true,
 
    factoryConfig: {
        name: 'viewModel'
    },
 
    collectTimeout: 100,
 
    expressionRe: /^(?:\{(?:(\d+)|([a-z_][\w\.]*))\})$/i,
 
    statics: {
        /**
         * Escape bind strings so they are treated as literals.
         * 
         * @param {Object/String} value The value to escape. If the value is
         * an object, any strings will be recursively escaped.
         * @return {Object/String} The escaped value. Matches the type of the
         * passed value.
         *
         * @since 6.5.2
         * @private
         */
        escape: function(value) {
            var ret = value,
                key;
 
            if (typeof value === 'string') {
                ret = '~~' + value;
            } else if (value && value.constructor === Object) {
                ret = {};
                for (key in value) {
                    ret[key] = this.escape(value[key]);
                }
            }
            return ret;
        }
    },
 
    $configStrict: false, // allow "formulas" to be specified on derived class body
    config: {
        /**
         * @cfg {Object} data
         * This object holds the arbitrary data that populates the `ViewModel` and is
         * then available for binding.
         * @since 5.0.0
         */
        data: true,
 
        /**
         * @cfg {Object} formulas
         * An object that defines named values whose value is managed by function calls.
         * The names of the properties of this object are assigned as values in the
         * ViewModel.
         *
         * For example:
         *
         *      formulas: {
         *          xy: function (get) { return get('x') * get('y'); }
         *      }
         *
         * For more details about defining a formula, see `{@link Ext.app.bind.Formula}`.
         * @since 5.0.0
         */
        formulas: {
            $value: null,
            merge: function (newValue, currentValue, target, mixinClass) {
                return this.mergeNew(newValue, currentValue, target, mixinClass);
            }
        },
 
        /**
         * @cfg {Object} links
         * Links provide a way to assign a simple name to a more complex bind. The primary
         * use for this is to assign names to records in the data model.
         *
         *      links: {
         *          theUser: {
         *              type: 'User',
         *              id: 12
         *          }
         *      }
         *
         * It is also possible to force a new phantom record to be created by not specifying an
         * id but passing `create: true` as part of the descriptor. This is often useful when
         * creating a new record for a child session.
         *
         *     links: {
         *         newUser: {
         *             type: 'User',
         *             create: true
         *         }
         *     } 
         *
         * `create` can also be an object containing initial data for the record.
         *
         *     links: {
         *         newUser: {
         *             type: 'User',
         *             create: {
         *                 firstName: 'John',
         *                 lastName: 'Smith'
         *             }
         *         }
         *     } 
         *
         * While that is the typical use, the value of each property in `links` may also be
         * a bind descriptor (see `{@link #method-bind}` for the various forms of bind
         * descriptors).
         * @since 5.0.0
         */
        links: null,
 
        /**
         * @cfg {Ext.app.ViewModel} parent
         * The parent `ViewModel` of this `ViewModel`. Once set, this cannot be changed.
         * @readonly
         * @since 5.0.0
         */
        parent: null,
 
        /**
         * @cfg {Ext.app.bind.RootStub} root
         * A reference to the root "stub" (an object that manages bindings).
         * @private
         * @since 5.0.0
         */
        root: true,
 
        /**
         * @cfg {Ext.util.Scheduler} scheduler
         * The scheduler used to schedule and manage the delivery of notifications for
         * all connections to this `ViewModel` and any other attached to it. The normal
         * process to initialize the `scheduler` is to get the scheduler used by the
         * `parent` or `session` and failing either of those, create one.
         * @readonly
         * @private
         * @since 5.0.0
         */
        scheduler: null,
 
        /**
         * @cfg {String/Ext.data.schema.Schema} schema
         * The schema to use for getting information about entities.
         */
        schema: 'default',
 
        /**
         * @cfg {Ext.data.Session} session
         * The session used to manage the data model (records and stores).
         * @since 5.0.0
         */
        session: null,
 
        // @cmd-auto-dependency {isKeyedObject: true, aliasPrefix: "store.", defaultType: "store"}
        /**
         * @cfg {Object} stores
         * A declaration of `Ext.data.Store` configurations that are first processed as
         * binds to produce an effective store configuration.
         *
         * A simple store definition. We can reference this in our bind statements using the
         * `{users}` as we would with other data values.
         *
         *     new Ext.app.ViewModel({
         *         stores: {
         *             users: {
         *                 model: 'User',
         *                 autoLoad: true
         *             }
         *         }
         *     });
         *
         * This store definition contains a dynamic binding. The store will not be created until
         * the initial value for groupId is set. Once that occurs, the store is created with the appropriate
         * filter configuration. Subsequently, once we change the group value, the old filter will be
         * overwritten with the new value.
         *
         *     var viewModel = new Ext.app.ViewModel({
         *         stores: {
         *             users: {
         *                 model: 'User',
         *                 filters: [{
         *                     property: 'groupId',
         *                     value: '{groupId}'
         *                 }]
         *             }
         *         }
         *     });
         *     viewModel.set('groupId', 1); // This will trigger the store creation with the filter.
         *     viewModel.set('groupId', 2); // The filter value will be changed.
         *
         * This store uses {@link Ext.data.ChainedStore store chaining} to create a store backed by the
         * data in another store. By specifying a string as the store, it will bind our creation and backing
         * to the other store. This functionality is especially useful when wanting to display a different "view"
         * of a store, for example a different sort order or different filters.
         *
         *     var viewModel = new Ext.app.ViewModel({
         *         stores: {
         *             allUsers: {
         *                 model: 'User',
         *                 autoLoad: true
         *             },
         *             children: {
         *                 source: '{allUsers}',
         *                 filters: [{
         *                     property: 'age',
         *                     value: 18,
         *                     operator: '<'
         *                 }]
         *             }
         *         }
         *     });
         *
         * @since 5.0.0
         */
        stores: null,
 
        /**
         * @cfg {Ext.container.Container} view
         * The Container that owns this `ViewModel` instance.
         * @since 5.0.0
         */
        view: null
    },
 
    constructor: function (config) {
        // Used to track non-stub bindings
        this.bindings = {};
        /*
         *  me.data = {
         *      foo: {
         *      },
         *          
         *      selectedUser: {
         *          name: null
         *      },
         *  }
         *
         *  me.root = new Ext.app.bind.RootStub({
         *      children: {
         *          foo: new Ext.app.bind.Stub(),
         *          selectedUser: new Ext.app.bind.LinkStub({
         *              binding: session.bind(...),
         *              children: {
         *                  name: : new Ext.app.bind.Stub()
         *              }
         *          }),
         *      }
         *  })
         */
 
        this.initConfig(config);
    },
 
    destroy: function () {
        var me = this,
            scheduler = me._scheduler,
            stores = me.storeInfo,
            parent = me.getParent(),
            task = me.collectTask,
            children = me.children,
            bindings = me.bindings,
            key, store, autoDestroy, storeBinding;
 
        me.destroying = true;
        if (task) {
            task.cancel();
            me.collectTask = null;
        }
 
        // When used with components, they are destroyed bottom up
        // so this scenario is only likely to happen in the case where
        // we're using the VM without any component attachment, in which case
        // we need to clean up here.
        if (children) {
            for (key in children) {
                children[key].destroy();
            }
        }
 
        if (stores) {
            for (key in stores) {
                store = stores[key];
 
                // Cache this property in case store is destroyed;
                // Properties are cleared on destroy
                storeBinding = store.$binding;
                autoDestroy = store.autoDestroy;
                if (autoDestroy || (!store.$wasInstance && autoDestroy !== false)) {
                    store.destroy();
                }
                Ext.destroy(storeBinding);
            }
        }
 
        if (parent) {
            parent.unregisterChild(me);
        }
 
        
        me.getRoot().destroy();
 
        for (key in bindings) {
            bindings[key].destroy();
        }
 
        if (scheduler && scheduler.$owner === me) {
            scheduler.$owner = null;
            scheduler.destroy();
        }
 
        me.children = me.storeInfo = me._session = me._view = me._scheduler =
                      me.bindings = me._root = me._parent = me.formulaFn = me.$formulaData = null;
 
        me.destroying = false;
        me.callParent();
    },
 
    /**
     * This method requests that data in this `ViewModel` be delivered to the specified
     * `callback`. The data desired is given in a "bind descriptor" which is the first
     * argument.
     *
     * A simple call might look like this:
     *
     *     var binding = vm.bind('{foo}', this.onFoo, this);
     * 
     *     binding.destroy();  // when done with the binding
     *
     * Options for the binding can be provided in the last argument:
     *
     *     var binding = vm.bind('{foo}', this.onFoo, this, {
     *         deep: true
     *     });
     * 
     * Alternatively, bind options can be combined with the bind descriptor using only
     * the first argument:
     *
     *     var binding = vm.bind({
     *         bindTo: '{foo}',  // the presence of bindTo identifies this form
     *         deep: true
     *     }, this.onFoo, this);
     * 
     * See the class documentation for more details on Bind Descriptors and options.
     *
     * @param {String/Object/Array} descriptor The bind descriptor. See class description
     * for details.
     * @param {Function} callback The function to call with the value of the bound property.
     * @param {Object} [scope] The scope (`this` pointer) for the `callback`.
     * @param {Object} [options] Additional options to configure the {@link Ext.app.bind.Binding binding}.
     * If this parameter is provided, the `bindTo` form of combining options and bind descriptor is not
     * recognized.
     * @return {Ext.app.bind.BaseBinding/Ext.app.bind.Binding} The binding.
     */
    bind: function (descriptor, callback, scope, options) {
        var me = this,
            track = true,
            binding;
 
        scope = scope || me;
 
        if (!options && descriptor.bindTo !== undefined && !Ext.isString(descriptor)) {
            options = descriptor;
            descriptor = options.bindTo;
        }
 
        if (!Ext.isString(descriptor)) {
            binding = new Ext.app.bind.Multi(descriptor, me, callback, scope, options);
        } else if (me.expressionRe.test(descriptor)) {
            // If we have '{foo}' alone it is a literal
            descriptor = descriptor.substring(1, descriptor.length - 1);
            binding = me.bindExpression(descriptor, callback, scope, options);
            track = false;
        } else {
            binding = new Ext.app.bind.TemplateBinding(descriptor, me, callback, scope, options);
        }
 
        if (track) {
            me.bindings[binding.id] = binding;
        }
 
        return binding;
    },
 
    /**
     * Gets the session attached to this (or a parent) ViewModel. See the {@link #session} configuration.
     * @return {Ext.data.Session} The session. `null` if no session exists.
     */
    getSession: function () {
        var me = this,
            session = me._session,
            parent;
 
        if (!session && (parent = me.getParent())) {
            me.setSession(session = parent.getSession());
        }
 
        return session || null;
    },
    
    /**
     * Gets a store configured via the {@link #stores} configuration.
     * @param {String} key The name of the store.
     * @return {Ext.data.Store} The store. `null` if no store exists.
     */
    getStore: function(key) {
        var storeInfo = this.storeInfo,
            store;
        
        if (storeInfo) {
            store = storeInfo[key];
        }
        return store || null;
    },
    
    /**
     * @method getStores
     * @hide
     */
 
    /**
     * Create a link to a reference. See the {@link #links} configuration.
     * @param {String} key The name for the link.
     * @param {Object} reference The reference descriptor.
     */
    linkTo: function (key, reference) {
        var me = this,
            stub, create, id, modelType, linkStub, rec;
 
        //<debug>
        if (key.indexOf('.') > -1) {
            Ext.raise('Links can only be at the top-level: "' + key + '"');
        }
        //</debug>
 
        if (reference.isModel) {
            reference = {
                type: reference.entityName,
                id: reference.id
            };
        }
        // reference is backwards compat, type is preferred.
        modelType = reference.type || reference.reference;
        create = reference.create;
        if (modelType) {
            // It's a record
            id = reference.id;
            //<debug>
            if (!reference.create && Ext.isEmpty(id)) {
                Ext.raise('No id specified. To create a phantom model, specify "create: true" as part of the reference.');
            }
            //</debug>
            if (create) {
                id = undefined;
            }
            rec = me.getRecord(modelType, id);
            if (Ext.isObject(create)) {
                rec.set(create);
                rec.commit();
                rec.phantom = true;
            }
            // Force creation at the root level. If an existing stub is there
            // it will be grafted in place here.
            stub = me.getRoot().createStubChild(key);
            stub.set(rec);
        } else {
            stub = me.getStub(key);
            if (!stub.isLinkStub) {
                // Pass parent=null since we will graft in this new stub to replace us:
                linkStub = new Ext.app.bind.LinkStub(me, stub.name);
                stub.graft(linkStub);
                stub = linkStub;
            }
            stub.link(reference);
        }
    },
 
    /**
     * Forces all bindings in this ViewModel hierarchy to evaluate immediately. Use this to do a synchronous flush
     * of all bindings.
     */
    notify: function () {
        var scheduler = this.getScheduler();
        if (!scheduler.firing) {
            scheduler.notify();
        }
    },
 
    /**
     * Get a value from the data for this viewmodel.
     * @param {String} path The path of the data to retrieve.
     *
     *     var value = vm.get('theUser.address.city');
     *
     * @return {Object} The data stored at the passed path.
     */
    get: function(path) {
        return this.getStub(path).getValue();
    },
 
    /**
     * Set a value in the data for this viewmodel. This method will climb to set data on
     * a parent view model if appropriate. See "Inheriting Data" in the class introduction for
     * more information.
     * 
     * @param {Object/String} path The path of the value to set, or an object literal to set
     * at the root of the viewmodel.
     * @param {Object} value The data to set at the value. If the value is an object literal,
     * any required paths will be created.
     *
     *     // Set a single property at the root level
     *     viewModel.set('expiry', Ext.Date.add(new Date(), Ext.Date.DAY, 7));
     *     console.log(viewModel.get('expiry'));
     *     // Sets a single property in user.address, does not overwrite any hierarchy.
     *     viewModel.set('user.address.city', 'London');
     *     console.log(viewModel.get('user.address.city'));
     *     // Sets 2 properties of "user". Overwrites any existing hierarchy.
     *     viewModel.set('user', {firstName: 'Foo', lastName: 'Bar'});
     *     console.log(viewModel.get('user.firstName'));
     *     // Sets a single property at the root level. Overwrites any existing hierarchy.
     *     viewModel.set({rootKey: 1});
     *     console.log(viewModel.get('rootKey'));
     */
    set: function (path, value) {
        var me = this,
            obj, stub;
 
        // Force data creation
        me.getData();
 
        if (value === undefined && path && path.constructor === Object) {
            stub = me.getRoot();
            value = path;
        } else if (path && path.indexOf('.') < 0) {
            obj = {};
            obj[path] = value;
            value = obj;
            stub = me.getRoot();
        } else {
            stub = me.getStub(path);
        }
 
        stub.set(value);
    },
 
    /**
     * Sets data directly at the level of this viewmodel. This method does not climb
     * to set data on parent view models. Passing `undefined` will clear the value
     * in this viewmodel, which means that this viewmodel is free to inherit data
     * from a parent. See "Inheriting Data" in the class introduction for more information.
     * @param {Object} data The new data to set.
     * @method setData
     */
 
    //=========================================================================
    privates: {
        registerChild: function(child) {
            var children = this.children;
            if (!children) {
                this.children = children = {};
            }
            children[child.getId()] = child;
        },
        
        unregisterChild: function(child) {
            var children = this.children;
            // If we're destroying we'll be wiping this collection shortly, so
            // just ignore it here
            if (!this.destroying && children) {
                delete children[child.getId()];
            }
        },
 
        /**
         * Get a record instance given a reference descriptor. Will ask
         * the session if one exists.
         * @param {String/Ext.Class} type The model type.
         * @param {Object} id The model id.
         * @return {Ext.data.Model} The model instance.
         * @private
         */
         getRecord: function(type, id) {
            var session = this.getSession(),
                Model = type,
                hasId = id !== undefined,
                record;
 
            if (session) {
                if (hasId) {
                    record = session.getRecord(type, id);
                } else {
                    record = session.createRecord(type);
                }
            } else {
                if (!Model.$isClass) {
                    Model = this.getSchema().getEntity(Model);
                    //<debug>
                    if (!Model) {
                        Ext.raise('Invalid model name: ' + type);
                    }
                    //</debug>
                }
                if (hasId) {
                    record = Model.createWithId(id);
                    record.load();
                } else {
                    record = new Model();
                }
            }
            return record;
        },
 
        bindExpression: function (descriptor, callback, scope, options) {
            var stub = this.getStub(descriptor);
            return stub.bind(callback, scope, options);
        },
 
        applyScheduler: function (scheduler) {
            if (scheduler && !scheduler.isInstance) {
                if (scheduler === true) {
                    scheduler = {};
                }
                if (!('preSort' in scheduler)) {
                    scheduler = Ext.apply({
                        preSort: 'kind,-depth'
                    }, scheduler);
                }
                scheduler = new Ext.util.Scheduler(scheduler);
                scheduler.$owner = this;
            }
            return scheduler;
        },
 
        getScheduler: function () {
            var me = this,
                scheduler = me._scheduler,
                parent;
 
            if (!scheduler) {
                if (!(parent = me.getParent())) {
                    scheduler = new Ext.util.Scheduler({
                        // See Session#scheduler
                        preSort: 'kind,-depth'
                    });
                    scheduler.$owner = me;
                } else {
                    scheduler = parent.getScheduler();
                }
 
                me.setScheduler(scheduler);
            }
 
            return scheduler;
        },
 
        /**
         * This method looks up the `Stub` for a single bind descriptor.
         * @param {String/Object} bindDescr The bind descriptor.
         * @return {Ext.app.bind.AbstractStub} The `Stub` associated to the bind descriptor.
         * @private
         */
        getStub: function (bindDescr) {
            var root = this.getRoot();
            return bindDescr ? root.getChild(bindDescr) : root;
        },
 
        collect: function() {
            var me = this,
                parent = me.getParent(),
                task = me.collectTask;
 
            if (parent) {
                parent.collect();
                return;
            }
 
            if (!task) {
                task = me.collectTask = new Ext.util.DelayedTask(me.doCollect, me);
            }
 
            // Useful for testing
            if (me.collectTimeout === 0) {
                me.doCollect();
            } else {
                task.delay(me.collectTimeout);
            }
        },
 
        doCollect: function() {
            var children = this.children,
                key;
            
            // We need to loop over the children first, since they may have link stubs
            // that create bindings inside our VM. Attempt to clean them up first.
            if (children) {
                for (key in children) {
                    children[key].doCollect();
                }
            }
            this.getRoot().collect();
        },
 
        invalidateChildLinks: function(name, clear) {
            var children = this.children,
                key;
 
            if (children) {
                for (key in children) {
                    children[key].getRoot().invalidateChildLink(name, clear);
                }
            }
        },
 
        onBindDestroy: function(binding, fromChild) {
            var me = this,
                parent;
 
            if (me.destroying) {
                return;
            }
 
            if (!fromChild) {
                delete me.bindings[binding.id];
            }
 
            parent = me.getParent();
            if (parent) {
                parent.onBindDestroy(binding, true);
            } else {
                me.collect();
            }
        },
 
        //-------------------------------------------------------------------------
        // Config
        // <editor-fold>
 
        applyData: function (newData, data) {
            var me = this,
                linkData, parent;
 
            // Force any session to be invoked so we can access it
            me.getSession();
            if (!data) {
                parent = me.getParent();
 
                /**
                 * @property {Object} linkData
                 * This object is used to hold the result of a linked value. This is done
                 * so that the data object hasOwnProperty equates to whether or not this
                 * property is owned by this instance or inherited.
                 * @private
                 * @readonly
                 * @since 5.0.0
                 */
                me.linkData = linkData = parent ? Ext.Object.chain(parent.getData()) : {};
 
                /**
                 * @property {Object} data
                 * This object holds all of the properties of this `ViewModel`. It is
                 * prototype chained to the `linkData` which is, in turn, prototype chained
                 * to (if present) the `data` object of the parent `ViewModel`.
                 * @private
                 * @readonly
                 * @since 5.0.0
                 */
                me.data = me._data = Ext.Object.chain(linkData);
            }
 
            if (newData && newData.constructor === Object) {
                me.getRoot().set(newData, true);
            }
        },
 
        applyParent: function(parent) {
            if (parent) {
                parent.registerChild(this);
            }
            return parent;
        },
        
        applyStores: function(stores) {
            var me = this,
                root = me.getRoot(),
                key, cfg, storeBind, stub, listeners;
            
            me.storeInfo = {};
            me.listenerScopeFn = function() {
                return me.getView().getInheritedConfig('defaultListenerScope');
            };
            for (key in stores) {
                cfg = stores[key];
                if (cfg.isStore) {
                    cfg.$wasInstance = true;
                    me.setupStore(cfg, key);
                    continue;
                } else if (Ext.isString(cfg)) {
                    cfg = {
                        source: cfg
                    };
                } else {
                    cfg = Ext.apply({}, cfg);
                }
                // Get rid of listeners so they don't get considered as a bind
                listeners = cfg.listeners;
                delete cfg.listeners;
                storeBind = me.bind(cfg, me.onStoreBind, me, {trackStatics: true});
                if (storeBind.isStatic()) {
                    // Everything is static, we don't need to wait, so remove the
                    // binding because it will only fire the first time.
                    storeBind.destroy();
                    me.createStore(key, cfg, listeners);
                } else {
                    storeBind.$storeKey = key;
                    storeBind.$listeners = listeners;
                    stub = root.createStubChild(key);
                    stub.setStore(storeBind);
                }
            }
        },
        
        onStoreBind: function(cfg, oldValue, binding) {
            var info = this.storeInfo,
                key = binding.$storeKey,
                store = info[key],
                proxy;
 
            if (!store) {
                this.createStore(key, cfg, binding.$listeners, binding);
            } else {
                cfg = Ext.merge({}, binding.pruneStaticKeys());
                proxy = cfg.proxy;
                delete cfg.type;
                delete cfg.model;
                delete cfg.fields;
                delete cfg.proxy;
                delete cfg.listeners;
                
                // TODO: possibly optimize this so we can figure out what has changed
                // instead of smashing the whole lot
                if (proxy) {
                    delete proxy.reader;
                    delete proxy.writer;
                    store.getProxy().setConfig(proxy);
                }
                store.setConfig(cfg);
            }
        },
 
        createStore: function(key, cfg, listeners, binding) {
            var session = this.getSession(),
                store;
 
            cfg = Ext.apply({}, cfg);
 
            if (cfg.session) {
                cfg.session = session;
            }
            if (cfg.source) {
                cfg.type = cfg.type || 'chained';
            }
 
            // Restore the listeners from applyStores here
            cfg.listeners = listeners;
            // Ensure events fired by ctor can find their target:
            cfg.resolveListenerScope = this.listenerScopeFn;
 
            store = Ext.Factory.store(cfg);
            store.$binding = binding;
 
            this.setupStore(store, key);
        },
 
        setupStore: function (store, key) {
            var me = this,
                obj = {};
            
            // Force data object creation
            me.getData();
            
            // May have been given a store instance
            store.resolveListenerScope = me.listenerScopeFn;
            me.storeInfo[key] = store;
            
            obj[key] = store;
            me.setData(obj);
        },
 
        applyFormulas: function (formulas) {
            var me = this,
                root = me.getRoot(),
                name, stub;
 
            me.getData(); // make sure our data is setup first
 
            for (name in formulas) {
                //<debug>
                if (name.indexOf('.') >= 0) {
                    Ext.raise('Formula names cannot contain dots: ' + name);
                }
                //</debug>
                
                // Force a stub to be created
                root.createStubChild(name);
 
                stub = me.getStub(name);
                stub.setFormula(formulas[name]);
            }
            return formulas;
        },
 
        applyLinks: function (links) {
            for (var link in links) {
                this.linkTo(link, links[link]);
            }
        },
 
        applySchema: function (schema) {
            return Ext.data.schema.Schema.get(schema);
        },
 
        applyRoot: function () {
            var root = new Ext.app.bind.RootStub(this),
                parent = this.getParent();
 
            if (parent) {
                // We are assigning the root of a child VM such that its bindings will be
                // pre-sorted after the bindings of the parent VM.
                root.depth = parent.getRoot().depth - 1000;
            }
 
            return root;
        },
 
        getFormulaFn: function(data) {
            var me = this,
                fn = me.formulaFn;
 
            if (!fn) {
                fn = me.formulaFn = function(name) {
                    // Note that the `this` pointer here is the view model because
                    // the VM calls it in the VM scope.
                    return me.$formulaData[name];
                };
            }
            me.$formulaData = data;
            return fn;
        }
 
        // </editor-fold>
    }
});