// @tag enterprise
/**
 * The SOAP Proxy class is an {@link Ext.data.proxy.Ajax Ajax Proxy} to access v1.1 SOAP
 * (Simple Object Access Protocol) services.  SOAP Proxy constructs a SOAP Envelope and 
 * submits an AJAX request to load a SOAP response from the server.
 * 
 * For help getting started please refer to the
 * [Soap Guide](../guides/backend_connectors/soap.html).
 *
 * @class Ext.data.soap.Proxy
 */
Ext.define('Ext.data.soap.Proxy', {
    extend: 'Ext.data.proxy.Ajax',
    alias: 'proxy.soap',
 
    requires: [
        'Ext.data.soap.Reader'
    ],
 
    config: {
        /**
        * @cfg {Object} api
        * An object containing "create", "read", "update" and "destroy" properties that define
        * SOAP operations for each CRUD action method. These operations will be appended to
        * the {@link #url} as the {@link #operationParam} for each request type.
        * 
        *     api: {
        *         create: undefined,
        *         read: undefined,
        *         update: undefined,
        *         destroy: undefined
        *     }
        *     
        * At least one operation is required, but additional operations do not need to be configured
        * if they will not be used.  For example, if this proxy is only used for read operations
        * the following configuration will be sufficient:
        * 
        *     api: {
        *         read: 'Foo'
        *     }
        */
 
        /**
        * @cfg {Object} soapAction
        * An object containing "create", "read", "update" and "destroy" properties that define
        * the [SOAPAction](http://www.w3.org/TR/2000/NOTE-SOAP-20000508/#_Toc478383528) header
        * for each CRUD action method. A soapAction must be specified for each operation
        * configured in {@link #api}  Defaults to:
        * 
        *     soapAction: {
        *         create: undefined,
        *         read: undefined,
        *         update: undefined,
        *         destroy: undefined
        *     }
        */
       soapAction: {},
 
        /**
        * @cfg {String} operationParam
        * The name of the operation parameter to be appened to the SOAP endpoint url
        */
        operationParam: 'op',
 
        /**
        * @cfg {Object/String/Ext.data.soap.Reader} reader
        * The {@link Ext.data.soap.Reader} to use to decode the server's response. This can
        * either be a SOAP Reader instance, a SOAP Reader config object or 'soap'.
        */
        reader: 'soap',
 
        /**
        * @cfg {String} url
        * The SOAP endpoint url that this proxy will use to request the SOAP data. This can
        * be a proxied url to work around same-origin policy if the SOAP endpoint url is on
        * a different domain from your application.
        */
       url: '',
 
        /**
        * @cfg {String/Ext.XTemplate} envelopeTpl
        * The template used to create the SOAP envelope.  Defaults to:
        * 
        *     [
        *         '<?xml version="1.0" encoding="utf-8" ?>',
        *         '<soap:Envelope xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/">',
        *             '{[values.bodyTpl.apply(values)]}',
        *         '</soap:Envelope>'
        *     ]
        */
        envelopeTpl: [
            '<?xml version="1.0" encoding="utf-8" ?>',
            '<soap:Envelope xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/">',
                '{[values.bodyTpl.apply(values)]}',
            '</soap:Envelope>'
        ],
 
        /**
        * @cfg {Ext.XTemplate/Array} createBodyTpl
        * The template used to create the SOAP body for the "create" action. If not specified
        * {@link #writeBodyTpl} will be used for the "create" action.
        */
       createBodyTpl: null,
 
        /**
        * @cfg {Ext.XTemplate/Array} readBodyTpl
        * The template used to create the SOAP body for the "read" action.  Defaults to: 
        * 
        *     [
        *         '<soap:Body>',
        *             '<{operation} xmlns="{targetNamespace}">',
        *                 '<tpl foreach="params">',
        *                     '<{$}>{.}</{$}>',
        *                 '</tpl>',
        *             '</{operation}>',
        *         '</soap:Body>'
        *     ]
        */
        readBodyTpl: [
            '<soap:Body>',
                '<{operation} xmlns="{targetNamespace}">',
                    '<tpl foreach="params">',
                        '<{$}>{.}</{$}>',
                    '</tpl>',
                '</{operation}>',
            '</soap:Body>'
        ],
 
        /**
        * @cfg {Ext.XTemplate/Array} updateBodyTpl
        * The template used to create the SOAP body for the "update" action. If not specified
        * {@link #writeBodyTpl} will be used for the "update" action.
        */
       updateBodyTpl: null,
 
        /**
        * @cfg {Ext.XTemplate/Array} destroyBodyTpl
        * The template used to create the SOAP body for the "destroy" action. If not specified
        * {@link #writeBodyTpl} will be used for the "destroy" action.
        */
       destroyBodyTpl: null,
 
        /**
        * @cfg {Ext.XTemplate/Array} writeBodyTpl
        * The default template used to create the SOAP body for write actions (create, update,
        * and destroy). The individual body templates for each write action can be configured
        * using {@link #createBodyTpl}, {@link #updateBodyTpl}, and {@link #destroyBodyTpl}.
        * Defaults to:
        * 
        *     [
        *          '<soap:Body>',
        *              '<{operation} xmlns="{targetNamespace}">',
        *                  '<tpl for="records">',
        *                      '{% var recordName=values.modelName.split(".").pop(); %}',
        *                      '<{[recordName]}>',
        *                          '<tpl for="fields">',
        *                              '<{name}>{[parent.get(values.name)]}</{name}>',
        *                          '</tpl>',
        *                      '</{[recordName]}>',
        *                  '</tpl>',
        *              '</{operation}>',
        *          '</soap:Body>'
        *      ]
        */
        writeBodyTpl: [
            '<soap:Body>',
                '<{operation} xmlns="{targetNamespace}">',
                    '<tpl for="records">',
                        '{% var recordName=values.modelName.split(".").pop(); %}',
                        '<{[recordName]}>',
                            '<tpl for="fields">',
                                '<{name}>{[parent.get(values.name)]}</{name}>',
                            '</tpl>',
                        '</{[recordName]}>',
                    '</tpl>',
                '</{operation}>',
            '</soap:Body>'
        ],
 
        /**
        * @cfg {String} targetNamespace
        * namespace URI used by {@link #createBodyTpl}, {@link #readBodyTpl}, {@link #updateBodyTpl},
        * and {@link #destroyBodyTpl} as the "xmlns" attribute for the operation element.
        */
       targetNamespace: ''
    },
    
    applyEnvelopeTpl: function(tpl) {
        return this.createTpl(tpl);
    },
    
    applyCreateBodyTpl: function(tpl) {
        return this.createTpl(tpl);
    },
    
    applyReadBodyTpl: function(tpl) {
        return this.createTpl(tpl);
    },
    
    applyUpdateBodyTpl: function(tpl) {
        return this.createTpl(tpl);
    },
    
    applyDestroyBodyTpl: function(tpl) {
        return this.createTpl(tpl);
    },
    
    applyWriteBodyTpl: function(tpl) {
        return this.createTpl(tpl);
    },
    
    createTpl: function(tpl) {
        if (tpl && !tpl.isTpl) {
            tpl = new Ext.XTemplate(tpl);
        }
        return tpl;
    },
    
    /**
     * @property {Object} actionMethods
     * @readonly
     * Mapping of action name to HTTP request method.  All SOAP actions are mapped to 'POST'
     */
 
    doRequest: function(operation) {
        var me = this,
            action = operation.getAction(),
            soapOperation = me.getApi()[action],
            params = Ext.applyIf(operation.getParams() || {}, me.getExtraParams() || {}),
            xmlData = me.getEnvelopeTpl().apply({
                operation: soapOperation,
                targetNamespace: me.getTargetNamespace(),
                params: params,
                records: operation.getRecords(),
                bodyTpl: me.getBodyTpl(action)
            }),
            request = new Ext.data.Request({
                url: me.getUrl() + '?' + me.getOperationParam() + '=' + soapOperation,
                method: 'POST',
                action: action,
                operation: operation,
                xmlData: xmlData,
                headers: Ext.apply({
                    SOAPAction: me.getSoapAction()[action]
                }, me.getHeaders()),
                timeout: me.getTimeout(),
                scope: me,
                disableCaching: false // explicitly set it to false, ServerProxy handles caching
            });
 
        request.setCallback(me.createRequestCallback(request, operation));
        return me.sendRequest(request);
    },
    
    getBodyTpl: function(action) {
        action = Ext.String.capitalize(action);
        var tpl = this['get' + action + 'BodyTpl']();
        return tpl || this.getWriteBodyTpl();
    }
});