/**
 * @author Ed Spencer
 * @class Ext.data.proxy.Proxy
 *
 * <p>Proxies are used by {@link Ext.data.Store Stores} to handle the loading and saving of {@link Ext.data.Model Model} data.
 * Usually developers will not need to create or interact with proxies directly.</p>
 * <p><u>Types of Proxy</u></p>
 *
 * <p>There are two main types of Proxy - {@link Ext.data.proxy.Client Client} and {@link Ext.data.proxy.Server Server}. The Client proxies
 * save their data locally and include the following subclasses:</p>
 *
 * <ul style="list-style-type: disc; padding-left: 25px">
 * <li>{@link Ext.data.proxy.LocalStorage LocalStorageProxy} - saves its data to localStorage if the browser supports it</li>
 * <li>{@link Ext.data.proxy.SessionStorage SessionStorageProxy} - saves its data to sessionStorage if the browsers supports it</li>
 * <li>{@link Ext.data.proxy.Memory MemoryProxy} - holds data in memory only, any data is lost when the page is refreshed</li>
 * </ul>
 *
 * <p>The Server proxies save their data by sending requests to some remote server. These proxies include:</p>
 *
 * <ul style="list-style-type: disc; padding-left: 25px">
 * <li>{@link Ext.data.proxy.Ajax Ajax} - sends requests to a server on the same domain</li>
 * <li>{@link Ext.data.proxy.JsonP JsonP} - uses JSON-P to send requests to a server on a different domain</li>
 * <li>{@link Ext.data.proxy.Direct Direct} - uses {@link Ext.direct} to send requests</li>
 * </ul>
 *
 * <p>Proxies operate on the principle that all operations performed are either Create, Read, Update or Delete. These four operations
 * are mapped to the methods {@link #create}, {@link #read}, {@link #update} and {@link #destroy} respectively. Each Proxy subclass
 * implements these functions.</p>
 *
 * <p>The CRUD methods each expect an {@link Ext.data.Operation Operation} object as the sole argument. The Operation encapsulates
 * information about the action the Store wishes to perform, the {@link Ext.data.Model model} instances that are to be modified, etc.
 * See the {@link Ext.data.Operation Operation} documentation for more details. Each CRUD method also accepts a callback function to be
 * called asynchronously on completion.</p>
 *
 * <p>Proxies also support batching of Operations via a {@link Ext.data.Batch batch} object, invoked by the {@link #batch} method.</p>
 *
 * @constructor
 * Creates the Proxy
 * @param {Object} config Optional config object
 */

Ext.define('Ext.data.proxy.Proxy', {
   
alias: 'proxy.proxy',
    alternateClassName
: ['Ext.data.DataProxy', 'Ext.data.Proxy'],
    requires
: [
       
'Ext.data.reader.Json',
       
'Ext.data.writer.Json'
   
],
    uses
: [
       
'Ext.data.Batch',
       
'Ext.data.Operation',
       
'Ext.data.Model'
   
],
    mixins
: {
        observable
: 'Ext.util.Observable'
   
},
   
    /**
     * @cfg {String} batchOrder
     * Comma-separated ordering 'create', 'update' and 'destroy' actions when batching. Override this
     * to set a different order for the batched CRUD actions to be executed in. Defaults to 'create,update,destroy'
     */

    batchOrder
: 'create,update,destroy',
   
    /**
     * @cfg {Boolean} batchActions True to batch actions of a particular type when synchronizing the store.
     * Defaults to <tt>true</tt>.
     */

    batchActions
: true,
   
    /**
     * @cfg {String} defaultReaderType The default registered reader type. Defaults to 'json'
     * @private
     */

    defaultReaderType
: 'json',
   
    /**
     * @cfg {String} defaultWriterType The default registered writer type. Defaults to 'json'
     * @private
     */

    defaultWriterType
: 'json',
   
    /**
     * @cfg {String/Ext.data.Model} model The name of the Model to tie to this Proxy. Can be either the string name of
     * the Model, or a reference to the Model constructor. Required.
     */

   
    isProxy
: true,
   
    constructor
: function(config) {
        config
= config || {};
       
       
if (config.model === undefined) {
           
delete config.model;
       
}

       
this.mixins.observable.constructor.call(this, config);
       
       
if (this.model !== undefined && !(this.model instanceof Ext.data.Model)) {
           
this.setModel(this.model);
       
}
   
},
   
    /**
     * Sets the model associated with this proxy. This will only usually be called by a Store
     * @param {String|Ext.data.Model} model The new model. Can be either the model name string,
     * or a reference to the model's constructor
     * @param {Boolean} setOnStore Sets the new model on the associated Store, if one is present
     */

    setModel
: function(model, setOnStore) {
       
this.model = Ext.ModelManager.getModel(model);
       
       
var reader = this.reader,
            writer
= this.writer;
       
       
this.setReader(reader);
       
this.setWriter(writer);
       
       
if (setOnStore && this.store) {
           
this.store.setModel(this.model);
       
}
   
},
   
    /**
     * Returns the model attached to this Proxy
     * @return {Ext.data.Model} The model
     */

    getModel
: function() {
       
return this.model;
   
},
   
    /**
     * Sets the Proxy's Reader by string, config object or Reader instance
     * @param {String|Object|Ext.data.reader.Reader} reader The new Reader, which can be either a type string, a configuration object
     * or an Ext.data.reader.Reader instance
     * @return {Ext.data.reader.Reader} The attached Reader object
     */

    setReader
: function(reader) {
       
var me = this;
       
       
if (reader === undefined || typeof reader == 'string') {
            reader
= {
                type
: reader
           
};
       
}

       
if (reader.isReader) {
            reader
.setModel(me.model);
       
} else {
           
Ext.applyIf(reader, {
                proxy
: me,
                model
: me.model,
                type
: me.defaultReaderType
           
});

            reader
= Ext.createByAlias('reader.' + reader.type, reader);
       
}
       
        me
.reader = reader;
       
return me.reader;
   
},
   
    /**
     * Returns the reader currently attached to this proxy instance
     * @return {Ext.data.reader.Reader} The Reader instance
     */

    getReader
: function() {
       
return this.reader;
   
},
   
    /**
     * Sets the Proxy's Writer by string, config object or Writer instance
     * @param {String|Object|Ext.data.writer.Writer} writer The new Writer, which can be either a type string, a configuration object
     * or an Ext.data.writer.Writer instance
     * @return {Ext.data.writer.Writer} The attached Writer object
     */

    setWriter
: function(writer) {
       
if (writer === undefined || typeof writer == 'string') {
            writer
= {
                type
: writer
           
};
       
}

       
if (!(writer instanceof Ext.data.writer.Writer)) {
           
Ext.applyIf(writer, {
                model
: this.model,
                type
: this.defaultWriterType
           
});

            writer
= Ext.createByAlias('writer.' + writer.type, writer);
       
}
       
       
this.writer = writer;
       
       
return this.writer;
   
},
   
    /**
     * Returns the writer currently attached to this proxy instance
     * @return {Ext.data.writer.Writer} The Writer instance
     */

    getWriter
: function() {
       
return this.writer;
   
},
   
    /**
     * Performs the given create operation.
     * @param {Ext.data.Operation} operation The Operation to perform
     * @param {Function} callback Callback function to be called when the Operation has completed (whether successful or not)
     * @param {Object} scope Scope to execute the callback function in
     */

    create
: Ext.emptyFn,
   
    /**
     * Performs the given read operation.
     * @param {Ext.data.Operation} operation The Operation to perform
     * @param {Function} callback Callback function to be called when the Operation has completed (whether successful or not)
     * @param {Object} scope Scope to execute the callback function in
     */

    read
: Ext.emptyFn,
   
    /**
     * Performs the given update operation.
     * @param {Ext.data.Operation} operation The Operation to perform
     * @param {Function} callback Callback function to be called when the Operation has completed (whether successful or not)
     * @param {Object} scope Scope to execute the callback function in
     */

    update
: Ext.emptyFn,
   
    /**
     * Performs the given destroy operation.
     * @param {Ext.data.Operation} operation The Operation to perform
     * @param {Function} callback Callback function to be called when the Operation has completed (whether successful or not)
     * @param {Object} scope Scope to execute the callback function in
     */

    destroy
: Ext.emptyFn,
   
    /**
     * Performs a batch of {@link Ext.data.Operation Operations}, in the order specified by {@link #batchOrder}. Used internally by
     * {@link Ext.data.Store}'s {@link Ext.data.Store#sync sync} method. Example usage:
     * <pre><code>
     * myProxy.batch({
     *     create : [myModel1, myModel2],
     *     update : [myModel3],
     *     destroy: [myModel4, myModel5]
     * });
     * </code></pre>
     * Where the myModel* above are {@link Ext.data.Model Model} instances - in this case 1 and 2 are new instances and have not been
     * saved before, 3 has been saved previously but needs to be updated, and 4 and 5 have already been saved but should now be destroyed.
     * @param {Object} operations Object containing the Model instances to act upon, keyed by action name
     * @param {Object} listeners Optional listeners object passed straight through to the Batch - see {@link Ext.data.Batch}
     * @return {Ext.data.Batch} The newly created Ext.data.Batch object
     */

    batch
: function(operations, listeners) {
       
var me = this,
            batch
= Ext.create('Ext.data.Batch', {
                proxy
: me,
                listeners
: listeners || {}
           
}),
            useBatch
= me.batchActions,
            records
;
       
       
Ext.each(me.batchOrder.split(','), function(action) {
            records
= operations[action];
           
if (records) {
               
if (useBatch) {
                    batch
.add(Ext.create('Ext.data.Operation', {
                        action
: action,
                        records
: records
                   
}));
               
} else {
                   
Ext.each(records, function(record){
                        batch
.add(Ext.create('Ext.data.Operation', {
                            action
: action,
                            records
: [record]
                       
}));
                   
});
               
}
           
}
       
}, me);
       
        batch
.start();
       
return batch;
   
}
}, function() {
   
// Ext.data.proxy.ProxyMgr.registerType('proxy', this);
   
   
//backwards compatibility
   
Ext.data.DataProxy = this;
   
// Ext.deprecate('platform', '2.0', function() {
   
//     Ext.data.DataProxy = this;
   
// }, this);
});